diff options
Diffstat (limited to 'src/data/composite/wiki-properties')
35 files changed, 1079 insertions, 0 deletions
diff --git a/src/data/composite/wiki-properties/additionalFiles.js b/src/data/composite/wiki-properties/additionalFiles.js new file mode 100644 index 00000000..6760527a --- /dev/null +++ b/src/data/composite/wiki-properties/additionalFiles.js @@ -0,0 +1,30 @@ +// This is a somewhat more involved data structure - it's for additional +// or "bonus" files associated with albums or tracks (or anything else). +// It's got this form: +// +// [ +// {title: 'Booklet', files: ['Booklet.pdf']}, +// { +// title: 'Wallpaper', +// description: 'Cool Wallpaper!', +// files: ['1440x900.png', '1920x1080.png'] +// }, +// {title: 'Alternate Covers', description: null, files: [...]}, +// ... +// ] +// + +import {isAdditionalFileList} from '#validators'; + +// TODO: Not templateCompositeFrom. + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isAdditionalFileList}, + expose: { + transform: (additionalFiles) => + additionalFiles ?? [], + }, + }; +} diff --git a/src/data/composite/wiki-properties/additionalNameList.js b/src/data/composite/wiki-properties/additionalNameList.js new file mode 100644 index 00000000..c5971d4a --- /dev/null +++ b/src/data/composite/wiki-properties/additionalNameList.js @@ -0,0 +1,14 @@ +// A list of additional names! These can be used for a variety of purposes, +// e.g. providing extra searchable titles, localizations, romanizations or +// original titles, and so on. Each item has a name and, optionally, a +// descriptive annotation. + +import {isAdditionalNameList} from '#validators'; + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isAdditionalNameList}, + expose: {transform: value => value ?? []}, + }; +} diff --git a/src/data/composite/wiki-properties/annotatedReferenceList.js b/src/data/composite/wiki-properties/annotatedReferenceList.js new file mode 100644 index 00000000..8e6c96a1 --- /dev/null +++ b/src/data/composite/wiki-properties/annotatedReferenceList.js @@ -0,0 +1,64 @@ +import {input, templateCompositeFrom} from '#composite'; + +import { + isContentString, + optional, + validateArrayItems, + validateProperties, + validateReference, +} from '#validators'; + +import {exposeDependency} from '#composite/control-flow'; +import {inputSoupyFind, inputWikiData, withResolvedAnnotatedReferenceList} + from '#composite/wiki-data'; + +import {referenceListInputDescriptions, referenceListUpdateDescription} + from './helpers/reference-list-helpers.js'; + +export default templateCompositeFrom({ + annotation: `annotatedReferenceList`, + + compose: false, + + inputs: { + ...referenceListInputDescriptions(), + + data: inputWikiData({allowMixedTypes: true}), + find: inputSoupyFind(), + + reference: input.staticValue({type: 'string', defaultValue: 'reference'}), + annotation: input.staticValue({type: 'string', defaultValue: 'annotation'}), + thing: input.staticValue({type: 'string', defaultValue: 'thing'}), + }, + + update(staticInputs) { + const { + [input.staticValue('reference')]: referenceProperty, + [input.staticValue('annotation')]: annotationProperty, + } = staticInputs; + + return referenceListUpdateDescription({ + validateReferenceList: type => + validateArrayItems( + validateProperties({ + [referenceProperty]: validateReference(type), + [annotationProperty]: optional(isContentString), + })), + })(staticInputs); + }, + + steps: () => [ + withResolvedAnnotatedReferenceList({ + list: input.updateValue(), + + reference: input('reference'), + annotation: input('annotation'), + thing: input('thing'), + + data: input('data'), + find: input('find'), + }), + + exposeDependency({dependency: '#resolvedAnnotatedReferenceList'}), + ], +}); diff --git a/src/data/composite/wiki-properties/color.js b/src/data/composite/wiki-properties/color.js new file mode 100644 index 00000000..1bc9888b --- /dev/null +++ b/src/data/composite/wiki-properties/color.js @@ -0,0 +1,12 @@ +// A color! This'll be some CSS-ready value. + +import {isColor} from '#validators'; + +// TODO: Not templateCompositeFrom. + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isColor}, + }; +} diff --git a/src/data/composite/wiki-properties/commentary.js b/src/data/composite/wiki-properties/commentary.js new file mode 100644 index 00000000..928bbd1b --- /dev/null +++ b/src/data/composite/wiki-properties/commentary.js @@ -0,0 +1,34 @@ +// Artist commentary! Generally present on tracks and albums. + +import {input, templateCompositeFrom} from '#composite'; +import {isCommentary} from '#validators'; + +import {exitWithoutDependency, exposeDependency} + from '#composite/control-flow'; +import {withParsedCommentaryEntries} from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `commentary`, + + compose: false, + + update: { + validate: isCommentary, + }, + + steps: () => [ + exitWithoutDependency({ + dependency: input.updateValue(), + mode: input.value('falsy'), + value: input.value([]), + }), + + withParsedCommentaryEntries({ + from: input.updateValue(), + }), + + exposeDependency({ + dependency: '#parsedCommentaryEntries', + }), + ], +}); diff --git a/src/data/composite/wiki-properties/commentatorArtists.js b/src/data/composite/wiki-properties/commentatorArtists.js new file mode 100644 index 00000000..c5c14769 --- /dev/null +++ b/src/data/composite/wiki-properties/commentatorArtists.js @@ -0,0 +1,49 @@ +// List of artists referenced in commentary entries. +// This is mostly useful for credits and listings on artist pages. + +import {input, templateCompositeFrom} from '#composite'; + +import {exitWithoutDependency, exposeDependency} + from '#composite/control-flow'; +import {withFlattenedList, withPropertyFromList, withUniqueItemsOnly} + from '#composite/data'; +import {withParsedCommentaryEntries} from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `commentatorArtists`, + + compose: false, + + steps: () => [ + exitWithoutDependency({ + dependency: 'commentary', + mode: input.value('falsy'), + value: input.value([]), + }), + + withParsedCommentaryEntries({ + from: 'commentary', + }), + + withPropertyFromList({ + list: '#parsedCommentaryEntries', + property: input.value('artists'), + }).outputs({ + '#parsedCommentaryEntries.artists': '#artistLists', + }), + + withFlattenedList({ + list: '#artistLists', + }).outputs({ + '#flattenedList': '#artists', + }), + + withUniqueItemsOnly({ + list: '#artists', + }), + + exposeDependency({ + dependency: '#artists', + }), + ], +}); diff --git a/src/data/composite/wiki-properties/constitutibleArtwork.js b/src/data/composite/wiki-properties/constitutibleArtwork.js new file mode 100644 index 00000000..0ee3bfcd --- /dev/null +++ b/src/data/composite/wiki-properties/constitutibleArtwork.js @@ -0,0 +1,68 @@ +// This composition does not actually inspect the values of any properties +// specified, so it's not responsible for determining whether a constituted +// artwork should exist at all. + +import {input, templateCompositeFrom} from '#composite'; +import {withEntries} from '#sugar'; +import Thing from '#thing'; +import {validateThing} from '#validators'; + +import {exposeDependency, exposeUpdateValueOrContinue} + from '#composite/control-flow'; +import {withConstitutedArtwork} from '#composite/wiki-data'; + +const template = templateCompositeFrom({ + annotation: `constitutibleArtwork`, + + compose: false, + + inputs: { + dimensionsFromThingProperty: input({type: 'string', acceptsNull: true}), + fileExtensionFromThingProperty: input({type: 'string', acceptsNull: true}), + dateFromThingProperty: input({type: 'string', acceptsNull: true}), + artistContribsFromThingProperty: input({type: 'string', acceptsNull: true}), + artistContribsArtistProperty: input({type: 'string', acceptsNull: true}), + artTagsFromThingProperty: input({type: 'string', acceptsNull: true}), + referencedArtworksFromThingProperty: input({type: 'string', acceptsNull: true}), + }, + + steps: () => [ + exposeUpdateValueOrContinue({ + validate: input.value( + validateThing({ + referenceType: 'artwork', + })), + }), + + withConstitutedArtwork({ + dimensionsFromThingProperty: input('dimensionsFromThingProperty'), + fileExtensionFromThingProperty: input('fileExtensionFromThingProperty'), + dateFromThingProperty: input('dateFromThingProperty'), + artistContribsFromThingProperty: input('artistContribsFromThingProperty'), + artistContribsArtistProperty: input('artistContribsArtistProperty'), + artTagsFromThingProperty: input('artTagsFromThingProperty'), + referencedArtworksFromThingProperty: input('referencedArtworksFromThingProperty'), + }), + + exposeDependency({ + dependency: '#constitutedArtwork', + }), + ], +}); + +template.fromYAMLFieldSpec = function(field) { + const {[Thing.yamlDocumentSpec]: documentSpec} = this; + + const {provide} = documentSpec.fields[field].transform; + + const inputs = + withEntries(provide, entries => + entries.map(([property, value]) => [ + property, + input.value(value), + ])); + + return template(inputs); +}; + +export default template; diff --git a/src/data/composite/wiki-properties/constitutibleArtworkList.js b/src/data/composite/wiki-properties/constitutibleArtworkList.js new file mode 100644 index 00000000..246c08b5 --- /dev/null +++ b/src/data/composite/wiki-properties/constitutibleArtworkList.js @@ -0,0 +1,70 @@ +// This composition does not actually inspect the values of any properties +// specified, so it's not responsible for determining whether a constituted +// artwork should exist at all. + +import {input, templateCompositeFrom} from '#composite'; +import {withEntries} from '#sugar'; +import Thing from '#thing'; +import {validateWikiData} from '#validators'; + +import {exposeUpdateValueOrContinue} from '#composite/control-flow'; +import {withConstitutedArtwork} from '#composite/wiki-data'; + +const template = templateCompositeFrom({ + annotation: `constitutibleArtworkList`, + + compose: false, + + inputs: { + dimensionsFromThingProperty: input({type: 'string', acceptsNull: true}), + fileExtensionFromThingProperty: input({type: 'string', acceptsNull: true}), + dateFromThingProperty: input({type: 'string', acceptsNull: true}), + artistContribsFromThingProperty: input({type: 'string', acceptsNull: true}), + artistContribsArtistProperty: input({type: 'string', acceptsNull: true}), + artTagsFromThingProperty: input({type: 'string', acceptsNull: true}), + referencedArtworksFromThingProperty: input({type: 'string', acceptsNull: true}), + }, + + steps: () => [ + exposeUpdateValueOrContinue({ + validate: input.value( + validateWikiData({ + referenceType: 'artwork', + })), + }), + + withConstitutedArtwork({ + dimensionsFromThingProperty: input('dimensionsFromThingProperty'), + fileExtensionFromThingProperty: input('fileExtensionFromThingProperty'), + dateFromThingProperty: input('dateFromThingProperty'), + artistContribsFromThingProperty: input('artistContribsFromThingProperty'), + artistContribsArtistProperty: input('artistContribsArtistProperty'), + artTagsFromThingProperty: input('artTagsFromThingProperty'), + referencedArtworksFromThingProperty: input('referencedArtworksFromThingProperty'), + }), + + { + dependencies: ['#constitutedArtwork'], + compute: ({ + ['#constitutedArtwork']: constitutedArtwork, + }) => [constitutedArtwork], + }, + ], +}); + +template.fromYAMLFieldSpec = function(field) { + const {[Thing.yamlDocumentSpec]: documentSpec} = this; + + const {provide} = documentSpec.fields[field].transform; + + const inputs = + withEntries(provide, entries => + entries.map(([property, value]) => [ + property, + input.value(value), + ])); + + return template(inputs); +}; + +export default template; diff --git a/src/data/composite/wiki-properties/contentString.js b/src/data/composite/wiki-properties/contentString.js new file mode 100644 index 00000000..b0e82444 --- /dev/null +++ b/src/data/composite/wiki-properties/contentString.js @@ -0,0 +1,15 @@ +// String type that's slightly more specific than simpleString. If the +// property is a generic piece of human-reading content, this adds some +// useful valiation on top of simpleString - but still check if more +// particular properties like `name` are more appropriate. +// +// This type adapts validation for single- and multiline content. + +import {isContentString} from '#validators'; + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isContentString}, + }; +} diff --git a/src/data/composite/wiki-properties/contribsPresent.js b/src/data/composite/wiki-properties/contribsPresent.js new file mode 100644 index 00000000..24f302a5 --- /dev/null +++ b/src/data/composite/wiki-properties/contribsPresent.js @@ -0,0 +1,30 @@ +// Nice 'n simple shorthand for an exposed-only flag which is true when any +// contributions are present in the specified property. + +import {input, templateCompositeFrom} from '#composite'; +import {isContributionList} from '#validators'; + +import {exposeDependency, withResultOfAvailabilityCheck} + from '#composite/control-flow'; + +export default templateCompositeFrom({ + annotation: `contribsPresent`, + + compose: false, + + inputs: { + contribs: input.staticDependency({ + validate: isContributionList, + acceptsNull: true, + }), + }, + + steps: () => [ + withResultOfAvailabilityCheck({ + from: input('contribs'), + mode: input.value('empty'), + }), + + exposeDependency({dependency: '#availability'}), + ], +}); diff --git a/src/data/composite/wiki-properties/contributionList.js b/src/data/composite/wiki-properties/contributionList.js new file mode 100644 index 00000000..d9a6b417 --- /dev/null +++ b/src/data/composite/wiki-properties/contributionList.js @@ -0,0 +1,58 @@ +// Strong 'n sturdy contribution list, rolling a list of references (provided +// as this property's update value) and the resolved results (as get exposed) +// into one property. Update value will look something like this: +// +// [ +// {artist: 'Artist Name', annotation: 'Viola'}, +// {artist: 'artist:john-cena', annotation: null}, +// ... +// ] +// +// ...typically as processed from YAML, spreadsheet, or elsewhere. +// Exposes as the same, but with the artist property replaced with matches +// found in artistData - which means this always depends on an `artistData` +// property also existing on this object! +// + +import {input, templateCompositeFrom} from '#composite'; +import {isContributionList, isDate, isStringNonEmpty} from '#validators'; + +import {exposeConstant, exposeDependencyOrContinue} from '#composite/control-flow'; +import {withResolvedContribs} from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `contributionList`, + + compose: false, + + inputs: { + date: input({ + validate: isDate, + acceptsNull: true, + }), + + artistProperty: input({ + validate: isStringNonEmpty, + defaultValue: null, + }), + }, + + update: {validate: isContributionList}, + + steps: () => [ + withResolvedContribs({ + from: input.updateValue(), + thingProperty: input.thisProperty(), + artistProperty: input('artistProperty'), + date: input('date'), + }), + + exposeDependencyOrContinue({ + dependency: '#resolvedContribs', + }), + + exposeConstant({ + value: input.value([]), + }), + ], +}); diff --git a/src/data/composite/wiki-properties/dimensions.js b/src/data/composite/wiki-properties/dimensions.js new file mode 100644 index 00000000..57a01279 --- /dev/null +++ b/src/data/composite/wiki-properties/dimensions.js @@ -0,0 +1,13 @@ +// Plain ol' image dimensions. This is a two-item array of positive integers, +// corresponding to width and height respectively. + +import {isDimensions} from '#validators'; + +// TODO: Not templateCompositeFrom. + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isDimensions}, + }; +} diff --git a/src/data/composite/wiki-properties/directory.js b/src/data/composite/wiki-properties/directory.js new file mode 100644 index 00000000..1756a8e5 --- /dev/null +++ b/src/data/composite/wiki-properties/directory.js @@ -0,0 +1,41 @@ +// The all-encompassing "directory" property, used as the unique identifier for +// almost any data object. Also corresponds to a part of the URL which pages of +// such objects are visited at. + +import {input, templateCompositeFrom} from '#composite'; + +import {isDirectory, isName} from '#validators'; + +import {exposeDependency} from '#composite/control-flow'; +import {withDirectory} from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `directory`, + + compose: false, + + inputs: { + name: input({ + validate: isName, + defaultDependency: 'name', + acceptsNull: true, + }), + + suffix: input({ + validate: isDirectory, + defaultValue: null, + }), + }, + + steps: () => [ + withDirectory({ + directory: input.updateValue({validate: isDirectory}), + name: input('name'), + suffix: input('suffix'), + }), + + exposeDependency({ + dependency: '#directory', + }), + ], +}); diff --git a/src/data/composite/wiki-properties/duration.js b/src/data/composite/wiki-properties/duration.js new file mode 100644 index 00000000..827f282d --- /dev/null +++ b/src/data/composite/wiki-properties/duration.js @@ -0,0 +1,13 @@ +// Duration! This is a number of seconds, possibly floating point, always +// at minimum zero. + +import {isDuration} from '#validators'; + +// TODO: Not templateCompositeFrom. + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isDuration}, + }; +} diff --git a/src/data/composite/wiki-properties/externalFunction.js b/src/data/composite/wiki-properties/externalFunction.js new file mode 100644 index 00000000..c388da6c --- /dev/null +++ b/src/data/composite/wiki-properties/externalFunction.js @@ -0,0 +1,11 @@ +// External function. These should only be used as dependencies for other +// properties, so they're left unexposed. + +// TODO: Not templateCompositeFrom. + +export default function() { + return { + flags: {update: true}, + update: {validate: (t) => typeof t === 'function'}, + }; +} diff --git a/src/data/composite/wiki-properties/fileExtension.js b/src/data/composite/wiki-properties/fileExtension.js new file mode 100644 index 00000000..c926fa8b --- /dev/null +++ b/src/data/composite/wiki-properties/fileExtension.js @@ -0,0 +1,13 @@ +// A file extension! Or the default, if provided when calling this. + +import {isFileExtension} from '#validators'; + +// TODO: Not templateCompositeFrom. + +export default function(defaultFileExtension = null) { + return { + flags: {update: true, expose: true}, + update: {validate: isFileExtension}, + expose: {transform: (value) => value ?? defaultFileExtension}, + }; +} diff --git a/src/data/composite/wiki-properties/flag.js b/src/data/composite/wiki-properties/flag.js new file mode 100644 index 00000000..076e663f --- /dev/null +++ b/src/data/composite/wiki-properties/flag.js @@ -0,0 +1,19 @@ +// Straightforward flag descriptor for a variety of property purposes. +// Provide a default value, true or false! + +import {isBoolean} from '#validators'; + +// TODO: Not templateCompositeFrom. + +// TODO: The description is a lie. This defaults to false. Bad. + +export default function(defaultValue = false) { + if (typeof defaultValue !== 'boolean') { + throw new TypeError(`Always set explicit defaults for flags!`); + } + + return { + flags: {update: true, expose: true}, + update: {validate: isBoolean, default: defaultValue}, + }; +} diff --git a/src/data/composite/wiki-properties/helpers/reference-list-helpers.js b/src/data/composite/wiki-properties/helpers/reference-list-helpers.js new file mode 100644 index 00000000..dfdc6b41 --- /dev/null +++ b/src/data/composite/wiki-properties/helpers/reference-list-helpers.js @@ -0,0 +1,44 @@ +import {input} from '#composite'; +import {anyOf, isString, isThingClass, validateArrayItems} from '#validators'; + +export function referenceListInputDescriptions() { + return { + class: input.staticValue({ + validate: + anyOf( + isThingClass, + validateArrayItems(isThingClass)), + + acceptsNull: true, + defaultValue: null, + }), + + referenceType: input.staticValue({ + validate: + anyOf( + isString, + validateArrayItems(isString)), + + acceptsNull: true, + defaultValue: null, + }), + }; +} + +export function referenceListUpdateDescription({ + validateReferenceList, +}) { + return ({ + [input.staticValue('class')]: thingClass, + [input.staticValue('referenceType')]: referenceType, + }) => ({ + validate: + validateReferenceList( + (Array.isArray(thingClass) + ? thingClass.map(thingClass => + thingClass[Symbol.for('Thing.referenceType')]) + : thingClass + ? thingClass[Symbol.for('Thing.referenceType')] + : referenceType)), + }); +} diff --git a/src/data/composite/wiki-properties/index.js b/src/data/composite/wiki-properties/index.js new file mode 100644 index 00000000..892fc44a --- /dev/null +++ b/src/data/composite/wiki-properties/index.js @@ -0,0 +1,38 @@ +// #composite/wiki-properties +// +// Entries here may depend on entries in #composite/control-flow, +// #composite/data, and #composite/wiki-data. + +export {default as additionalFiles} from './additionalFiles.js'; +export {default as additionalNameList} from './additionalNameList.js'; +export {default as annotatedReferenceList} from './annotatedReferenceList.js'; +export {default as color} from './color.js'; +export {default as commentary} from './commentary.js'; +export {default as commentatorArtists} from './commentatorArtists.js'; +export {default as constitutibleArtwork} from './constitutibleArtwork.js'; +export {default as constitutibleArtworkList} from './constitutibleArtworkList.js'; +export {default as contentString} from './contentString.js'; +export {default as contribsPresent} from './contribsPresent.js'; +export {default as contributionList} from './contributionList.js'; +export {default as dimensions} from './dimensions.js'; +export {default as directory} from './directory.js'; +export {default as duration} from './duration.js'; +export {default as externalFunction} from './externalFunction.js'; +export {default as fileExtension} from './fileExtension.js'; +export {default as flag} from './flag.js'; +export {default as lyrics} from './lyrics.js'; +export {default as name} from './name.js'; +export {default as referenceList} from './referenceList.js'; +export {default as referencedArtworkList} from './referencedArtworkList.js'; +export {default as reverseReferenceList} from './reverseReferenceList.js'; +export {default as seriesList} from './seriesList.js'; +export {default as simpleDate} from './simpleDate.js'; +export {default as simpleString} from './simpleString.js'; +export {default as singleReference} from './singleReference.js'; +export {default as soupyFind} from './soupyFind.js'; +export {default as soupyReverse} from './soupyReverse.js'; +export {default as thing} from './thing.js'; +export {default as thingList} from './thingList.js'; +export {default as urls} from './urls.js'; +export {default as wallpaperParts} from './wallpaperParts.js'; +export {default as wikiData} from './wikiData.js'; diff --git a/src/data/composite/wiki-properties/lyrics.js b/src/data/composite/wiki-properties/lyrics.js new file mode 100644 index 00000000..eb5e524a --- /dev/null +++ b/src/data/composite/wiki-properties/lyrics.js @@ -0,0 +1,36 @@ +// Lyrics! This comes in two styles - "old", where there's just one set of +// lyrics, or the newer/standard one, with multiple sets that are each +// annotated, credited, etc. + +import {input, templateCompositeFrom} from '#composite'; +import {isLyrics} from '#validators'; + +import {exitWithoutDependency, exposeDependency} + from '#composite/control-flow'; +import {withParsedLyricsEntries} from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `lyrics`, + + compose: false, + + update: { + validate: isLyrics, + }, + + steps: () => [ + exitWithoutDependency({ + dependency: input.updateValue(), + mode: input.value('falsy'), + value: input.value([]), + }), + + withParsedLyricsEntries({ + from: input.updateValue(), + }), + + exposeDependency({ + dependency: '#parsedLyricsEntries', + }), + ], +}); diff --git a/src/data/composite/wiki-properties/name.js b/src/data/composite/wiki-properties/name.js new file mode 100644 index 00000000..5146488b --- /dev/null +++ b/src/data/composite/wiki-properties/name.js @@ -0,0 +1,11 @@ +// A wiki data object's name! Its directory (i.e. unique identifier) will be +// computed based on this value if not otherwise specified. + +import {isName} from '#validators'; + +export default function(defaultName) { + return { + flags: {update: true, expose: true}, + update: {validate: isName, default: defaultName}, + }; +} diff --git a/src/data/composite/wiki-properties/referenceList.js b/src/data/composite/wiki-properties/referenceList.js new file mode 100644 index 00000000..4f8207b5 --- /dev/null +++ b/src/data/composite/wiki-properties/referenceList.js @@ -0,0 +1,46 @@ +// Stores and exposes a list of references to other data objects; all items +// must be references to the same type, which is either implied from the class +// input, or explicitly set on the referenceType input. +// +// See also: +// - singleReference +// - withResolvedReferenceList +// + +import {input, templateCompositeFrom} from '#composite'; +import {validateReferenceList} from '#validators'; + +import {exposeDependency} from '#composite/control-flow'; +import {inputSoupyFind, inputWikiData, withResolvedReferenceList} + from '#composite/wiki-data'; + +import {referenceListInputDescriptions, referenceListUpdateDescription} + from './helpers/reference-list-helpers.js'; + +export default templateCompositeFrom({ + annotation: `referenceList`, + + compose: false, + + inputs: { + ...referenceListInputDescriptions(), + + data: inputWikiData({allowMixedTypes: true}), + find: inputSoupyFind(), + }, + + update: + referenceListUpdateDescription({ + validateReferenceList: validateReferenceList, + }), + + steps: () => [ + withResolvedReferenceList({ + list: input.updateValue(), + data: input('data'), + find: input('find'), + }), + + exposeDependency({dependency: '#resolvedReferenceList'}), + ], +}); diff --git a/src/data/composite/wiki-properties/referencedArtworkList.js b/src/data/composite/wiki-properties/referencedArtworkList.js new file mode 100644 index 00000000..9ba2e393 --- /dev/null +++ b/src/data/composite/wiki-properties/referencedArtworkList.js @@ -0,0 +1,32 @@ +import {input, templateCompositeFrom} from '#composite'; +import find from '#find'; +import {isDate} from '#validators'; + +import annotatedReferenceList from './annotatedReferenceList.js'; + +export default templateCompositeFrom({ + annotation: `referencedArtworkList`, + + compose: false, + + steps: () => [ + { + compute: (continuation) => continuation({ + ['#find']: + find.mixed({ + track: find.trackPrimaryArtwork, + album: find.albumPrimaryArtwork, + }), + }), + }, + + annotatedReferenceList({ + referenceType: input.value(['album', 'track']), + + data: 'artworkData', + find: '#find', + + thing: input.value('artwork'), + }), + ], +}); diff --git a/src/data/composite/wiki-properties/reverseReferenceList.js b/src/data/composite/wiki-properties/reverseReferenceList.js new file mode 100644 index 00000000..6d590a67 --- /dev/null +++ b/src/data/composite/wiki-properties/reverseReferenceList.js @@ -0,0 +1,30 @@ +// Neat little shortcut for "reversing" the reference lists stored on other +// things - for example, tracks specify a "referenced tracks" property, and +// you would use this to compute a corresponding "referenced *by* tracks" +// property. + +import {input, templateCompositeFrom} from '#composite'; + +import {exposeDependency} from '#composite/control-flow'; +import {inputSoupyReverse, inputWikiData, withReverseReferenceList} + from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `reverseReferenceList`, + + compose: false, + + inputs: { + data: inputWikiData({allowMixedTypes: true}), + reverse: inputSoupyReverse(), + }, + + steps: () => [ + withReverseReferenceList({ + data: input('data'), + reverse: input('reverse'), + }), + + exposeDependency({dependency: '#reverseReferenceList'}), + ], +}); diff --git a/src/data/composite/wiki-properties/seriesList.js b/src/data/composite/wiki-properties/seriesList.js new file mode 100644 index 00000000..2a101b45 --- /dev/null +++ b/src/data/composite/wiki-properties/seriesList.js @@ -0,0 +1,31 @@ +import {input, templateCompositeFrom} from '#composite'; +import {isSeriesList, validateThing} from '#validators'; + +import {exposeDependency} from '#composite/control-flow'; +import {withResolvedSeriesList} from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `seriesList`, + + compose: false, + + inputs: { + group: input({ + validate: validateThing({referenceType: 'group'}), + }), + }, + + steps: () => [ + withResolvedSeriesList({ + group: input('group'), + + list: input.updateValue({ + validate: isSeriesList, + }), + }), + + exposeDependency({ + dependency: '#resolvedSeriesList', + }), + ], +}); diff --git a/src/data/composite/wiki-properties/simpleDate.js b/src/data/composite/wiki-properties/simpleDate.js new file mode 100644 index 00000000..f08d8323 --- /dev/null +++ b/src/data/composite/wiki-properties/simpleDate.js @@ -0,0 +1,14 @@ +// General date type, used as the descriptor for a bunch of properties. +// This isn't dynamic though - it won't inherit from a date stored on +// another object, for example. + +import {isDate} from '#validators'; + +// TODO: Not templateCompositeFrom. + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isDate}, + }; +} diff --git a/src/data/composite/wiki-properties/simpleString.js b/src/data/composite/wiki-properties/simpleString.js new file mode 100644 index 00000000..7bf317ac --- /dev/null +++ b/src/data/composite/wiki-properties/simpleString.js @@ -0,0 +1,12 @@ +// General string type. This should probably generally be avoided in favor +// of more specific validation, but using it makes it easy to find where we +// might want to improve later, and it's a useful shorthand meanwhile. + +import {isString} from '#validators'; + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isString}, + }; +} diff --git a/src/data/composite/wiki-properties/singleReference.js b/src/data/composite/wiki-properties/singleReference.js new file mode 100644 index 00000000..f532ebbe --- /dev/null +++ b/src/data/composite/wiki-properties/singleReference.js @@ -0,0 +1,46 @@ +// Stores and exposes one connection, or reference, to another data object. +// The reference must be to a specific type, which is specified on the class +// input. +// +// See also: +// - referenceList +// - withResolvedReference +// + +import {input, templateCompositeFrom} from '#composite'; +import {isThingClass, validateReference} from '#validators'; + +import {exposeDependency} from '#composite/control-flow'; +import {inputSoupyFind, inputWikiData, withResolvedReference} + from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `singleReference`, + + compose: false, + + inputs: { + class: input.staticValue({validate: isThingClass}), + + find: inputSoupyFind(), + data: inputWikiData({allowMixedTypes: false}), + }, + + update: ({ + [input.staticValue('class')]: thingClass, + }) => ({ + validate: + validateReference( + thingClass[Symbol.for('Thing.referenceType')]), + }), + + steps: () => [ + withResolvedReference({ + ref: input.updateValue(), + data: input('data'), + find: input('find'), + }), + + exposeDependency({dependency: '#resolvedReference'}), + ], +}); diff --git a/src/data/composite/wiki-properties/soupyFind.js b/src/data/composite/wiki-properties/soupyFind.js new file mode 100644 index 00000000..0f9a17e3 --- /dev/null +++ b/src/data/composite/wiki-properties/soupyFind.js @@ -0,0 +1,14 @@ +import {isObject} from '#validators'; + +import {inputSoupyFind} from '#composite/wiki-data'; + +function soupyFind() { + return { + flags: {update: true}, + update: {validate: isObject}, + }; +} + +soupyFind.input = inputSoupyFind.input; + +export default soupyFind; diff --git a/src/data/composite/wiki-properties/soupyReverse.js b/src/data/composite/wiki-properties/soupyReverse.js new file mode 100644 index 00000000..784a66b4 --- /dev/null +++ b/src/data/composite/wiki-properties/soupyReverse.js @@ -0,0 +1,37 @@ +import {isObject} from '#validators'; + +import {inputSoupyReverse} from '#composite/wiki-data'; + +function soupyReverse() { + return { + flags: {update: true}, + update: {validate: isObject}, + }; +} + +soupyReverse.input = inputSoupyReverse.input; + +soupyReverse.contributionsBy = + (bindTo, contributionsProperty) => ({ + bindTo, + + referencing: thing => thing[contributionsProperty], + referenced: contrib => [contrib.artist], + }); + +soupyReverse.artworkContributionsBy = + (bindTo, artworkProperty, {single = false} = {}) => ({ + bindTo, + + referencing: thing => + (single + ? (thing[artworkProperty] + ? thing[artworkProperty].artistContribs + : []) + : thing[artworkProperty] + .flatMap(artwork => artwork.artistContribs)), + + referenced: contrib => [contrib.artist], + }); + +export default soupyReverse; diff --git a/src/data/composite/wiki-properties/thing.js b/src/data/composite/wiki-properties/thing.js new file mode 100644 index 00000000..1f97a362 --- /dev/null +++ b/src/data/composite/wiki-properties/thing.js @@ -0,0 +1,40 @@ +// An individual Thing, provided directly rather than by reference. + +import {input, templateCompositeFrom} from '#composite'; +import {isThingClass, validateThing} from '#validators'; + +import {exposeConstant, exposeUpdateValueOrContinue} + from '#composite/control-flow'; + +export default templateCompositeFrom({ + annotation: `wikiData`, + + compose: false, + + inputs: { + class: input.staticValue({ + validate: isThingClass, + defaultValue: null, + }), + }, + + update: ({ + [input.staticValue('class')]: thingClass, + }) => ({ + validate: + validateThing({ + referenceType: + (thingClass + ? thingClass[Symbol.for('Thing.referenceType')] + : ''), + }), + }), + + steps: () => [ + exposeUpdateValueOrContinue(), + + exposeConstant({ + value: input.value(null), + }), + ], +}); diff --git a/src/data/composite/wiki-properties/thingList.js b/src/data/composite/wiki-properties/thingList.js new file mode 100644 index 00000000..f4c00e06 --- /dev/null +++ b/src/data/composite/wiki-properties/thingList.js @@ -0,0 +1,44 @@ +// A list of Things, provided directly rather than by reference. +// +// Essentially the same as wikiData, but exposes the list of things, +// instead of keeping it private. + +import {input, templateCompositeFrom} from '#composite'; +import {isThingClass, validateWikiData} from '#validators'; + +import {exposeConstant, exposeUpdateValueOrContinue} + from '#composite/control-flow'; + +export default templateCompositeFrom({ + annotation: `wikiData`, + + compose: false, + + inputs: { + class: input.staticValue({ + validate: isThingClass, + defaultValue: null, + }), + }, + + update: ({ + [input.staticValue('class')]: thingClass, + }) => ({ + validate: + validateWikiData({ + referenceType: + (thingClass + ? thingClass[Symbol.for('Thing.referenceType')] + : ''), + }), + }), + + steps: () => [ + exposeUpdateValueOrContinue(), + + exposeConstant({ + value: input.value([]), + }), + ], +}); + diff --git a/src/data/composite/wiki-properties/urls.js b/src/data/composite/wiki-properties/urls.js new file mode 100644 index 00000000..3160a0bf --- /dev/null +++ b/src/data/composite/wiki-properties/urls.js @@ -0,0 +1,14 @@ +// A list of URLs! This will always be present on the data object, even if set +// to an empty array or null. + +import {isURL, validateArrayItems} from '#validators'; + +// TODO: Not templateCompositeFrom. + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: validateArrayItems(isURL)}, + expose: {transform: value => value ?? []}, + }; +} diff --git a/src/data/composite/wiki-properties/wallpaperParts.js b/src/data/composite/wiki-properties/wallpaperParts.js new file mode 100644 index 00000000..23049397 --- /dev/null +++ b/src/data/composite/wiki-properties/wallpaperParts.js @@ -0,0 +1,9 @@ +import {isWallpaperPartList} from '#validators'; + +export default function() { + return { + flags: {update: true, expose: true}, + update: {validate: isWallpaperPartList}, + expose: {transform: value => value ?? []}, + }; +} diff --git a/src/data/composite/wiki-properties/wikiData.js b/src/data/composite/wiki-properties/wikiData.js new file mode 100644 index 00000000..3bebed33 --- /dev/null +++ b/src/data/composite/wiki-properties/wikiData.js @@ -0,0 +1,27 @@ +// General purpose wiki data constructor, for properties like artistData, +// trackData, etc. + +import {input, templateCompositeFrom} from '#composite'; +import {isThingClass, validateWikiData} from '#validators'; + +export default templateCompositeFrom({ + annotation: `wikiData`, + + compose: false, + + inputs: { + class: input.staticValue({validate: isThingClass}), + }, + + update: ({ + [input.staticValue('class')]: thingClass, + }) => ({ + validate: + validateWikiData({ + referenceType: + thingClass[Symbol.for('Thing.referenceType')], + }), + }), + + steps: () => [], +}); |