« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/composite/things
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/composite/things')
-rw-r--r--src/data/composite/things/album/index.js2
-rw-r--r--src/data/composite/things/album/withTrackSections.js127
-rw-r--r--src/data/composite/things/album/withTracks.js51
-rw-r--r--src/data/composite/things/artwork/index.js1
-rw-r--r--src/data/composite/things/artwork/withContainingArtworkList.js46
-rw-r--r--src/data/composite/things/content/hasAnnotationPart.js25
-rw-r--r--src/data/composite/things/content/index.js4
-rw-r--r--src/data/composite/things/content/withAnnotationPartNodeLists.js28
-rw-r--r--src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js61
-rw-r--r--src/data/composite/things/content/withWebArchiveDate.js41
-rw-r--r--src/data/composite/things/contribution/index.js3
-rw-r--r--src/data/composite/things/contribution/inheritFromContributionPresets.js50
-rw-r--r--src/data/composite/things/contribution/withContainingReverseContributionList.js76
-rw-r--r--src/data/composite/things/contribution/withContributionContext.js45
-rw-r--r--src/data/composite/things/flash/index.js1
-rw-r--r--src/data/composite/things/flash/withFlashAct.js108
-rw-r--r--src/data/composite/things/track/exitWithoutUniqueCoverArt.js26
-rw-r--r--src/data/composite/things/track/index.js13
-rw-r--r--src/data/composite/things/track/inferredAdditionalNameList.js67
-rw-r--r--src/data/composite/things/track/inheritContributionListFromMainRelease.js41
-rw-r--r--src/data/composite/things/track/inheritFromMainRelease.js29
-rw-r--r--src/data/composite/things/track/inheritFromOriginalRelease.js50
-rw-r--r--src/data/composite/things/track/sharedAdditionalNameList.js38
-rw-r--r--src/data/composite/things/track/trackAdditionalNameList.js38
-rw-r--r--src/data/composite/things/track/trackReverseReferenceList.js38
-rw-r--r--src/data/composite/things/track/withAlbum.js108
-rw-r--r--src/data/composite/things/track/withAlwaysReferenceByDirectory.js78
-rw-r--r--src/data/composite/things/track/withContainingTrackSection.js63
-rw-r--r--src/data/composite/things/track/withHasUniqueCoverArt.js61
-rw-r--r--src/data/composite/things/track/withOriginalRelease.js78
-rw-r--r--src/data/composite/things/track/withOtherReleases.js41
-rw-r--r--src/data/composite/things/track/withPropertyFromAlbum.js49
32 files changed, 452 insertions, 1035 deletions
diff --git a/src/data/composite/things/album/index.js b/src/data/composite/things/album/index.js
deleted file mode 100644
index 8139f10e..00000000
--- a/src/data/composite/things/album/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export {default as withTracks} from './withTracks.js';
-export {default as withTrackSections} from './withTrackSections.js';
diff --git a/src/data/composite/things/album/withTrackSections.js b/src/data/composite/things/album/withTrackSections.js
deleted file mode 100644
index 0a1ebebc..00000000
--- a/src/data/composite/things/album/withTrackSections.js
+++ /dev/null
@@ -1,127 +0,0 @@
-import {input, templateCompositeFrom} from '#composite';
-import find from '#find';
-import {empty, filterMultipleArrays, stitchArrays} from '#sugar';
-import {isTrackSectionList} from '#validators';
-
-import {exitWithoutDependency, exitWithoutUpdateValue}
-  from '#composite/control-flow';
-import {withResolvedReferenceList} from '#composite/wiki-data';
-
-import {
-  fillMissingListItems,
-  withFlattenedList,
-  withPropertiesFromList,
-  withUnflattenedList,
-} from '#composite/data';
-
-export default templateCompositeFrom({
-  annotation: `withTrackSections`,
-
-  outputs: ['#trackSections'],
-
-  steps: () => [
-    exitWithoutDependency({
-      dependency: 'ownTrackData',
-      value: input.value([]),
-    }),
-
-    exitWithoutUpdateValue({
-      mode: input.value('empty'),
-      value: input.value([]),
-    }),
-
-    // TODO: input.updateValue description down here is a kludge.
-    withPropertiesFromList({
-      list: input.updateValue({
-        validate: isTrackSectionList,
-      }),
-      prefix: input.value('#sections'),
-      properties: input.value([
-        'tracks',
-        'dateOriginallyReleased',
-        'isDefaultTrackSection',
-        'name',
-        'color',
-      ]),
-    }),
-
-    fillMissingListItems({
-      list: '#sections.tracks',
-      fill: input.value([]),
-    }),
-
-    fillMissingListItems({
-      list: '#sections.isDefaultTrackSection',
-      fill: input.value(false),
-    }),
-
-    fillMissingListItems({
-      list: '#sections.name',
-      fill: input.value('Unnamed Track Section'),
-    }),
-
-    fillMissingListItems({
-      list: '#sections.color',
-      fill: input.dependency('color'),
-    }),
-
-    withFlattenedList({
-      list: '#sections.tracks',
-    }).outputs({
-      ['#flattenedList']: '#trackRefs',
-      ['#flattenedIndices']: '#sections.startIndex',
-    }),
-
-    withResolvedReferenceList({
-      list: '#trackRefs',
-      data: 'ownTrackData',
-      notFoundMode: input.value('null'),
-      find: input.value(find.track),
-    }).outputs({
-      ['#resolvedReferenceList']: '#tracks',
-    }),
-
-    withUnflattenedList({
-      list: '#tracks',
-      indices: '#sections.startIndex',
-    }).outputs({
-      ['#unflattenedList']: '#sections.tracks',
-    }),
-
-    {
-      dependencies: [
-        '#sections.tracks',
-        '#sections.name',
-        '#sections.color',
-        '#sections.dateOriginallyReleased',
-        '#sections.isDefaultTrackSection',
-        '#sections.startIndex',
-      ],
-
-      compute: (continuation, {
-        '#sections.tracks': tracks,
-        '#sections.name': name,
-        '#sections.color': color,
-        '#sections.dateOriginallyReleased': dateOriginallyReleased,
-        '#sections.isDefaultTrackSection': isDefaultTrackSection,
-        '#sections.startIndex': startIndex,
-      }) => {
-        filterMultipleArrays(
-          tracks, name, color, dateOriginallyReleased, isDefaultTrackSection, startIndex,
-          tracks => !empty(tracks));
-
-        return continuation({
-          ['#trackSections']:
-            stitchArrays({
-              tracks,
-              name,
-              color,
-              dateOriginallyReleased,
-              isDefaultTrackSection,
-              startIndex,
-            }),
-        });
-      },
-    },
-  ],
-});
diff --git a/src/data/composite/things/album/withTracks.js b/src/data/composite/things/album/withTracks.js
deleted file mode 100644
index fff3d5ae..00000000
--- a/src/data/composite/things/album/withTracks.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import {input, templateCompositeFrom} from '#composite';
-import find from '#find';
-
-import {exitWithoutDependency, raiseOutputWithoutDependency}
-  from '#composite/control-flow';
-import {withResolvedReferenceList} from '#composite/wiki-data';
-
-export default templateCompositeFrom({
-  annotation: `withTracks`,
-
-  outputs: ['#tracks'],
-
-  steps: () => [
-    exitWithoutDependency({
-      dependency: 'ownTrackData',
-      value: input.value([]),
-    }),
-
-    raiseOutputWithoutDependency({
-      dependency: 'trackSections',
-      mode: input.value('empty'),
-      output: input.value({
-        ['#tracks']: [],
-      }),
-    }),
-
-    {
-      dependencies: ['trackSections'],
-      compute: (continuation, {trackSections}) =>
-        continuation({
-          '#trackRefs': trackSections
-            .flatMap(section => section.tracks ?? []),
-        }),
-    },
-
-    withResolvedReferenceList({
-      list: '#trackRefs',
-      data: 'ownTrackData',
-      find: input.value(find.track),
-    }),
-
-    {
-      dependencies: ['#resolvedReferenceList'],
-      compute: (continuation, {
-        ['#resolvedReferenceList']: resolvedReferenceList,
-      }) => continuation({
-        ['#tracks']: resolvedReferenceList,
-      })
-    },
-  ],
-});
diff --git a/src/data/composite/things/artwork/index.js b/src/data/composite/things/artwork/index.js
new file mode 100644
index 00000000..2cd3c388
--- /dev/null
+++ b/src/data/composite/things/artwork/index.js
@@ -0,0 +1 @@
+export {default as withContainingArtworkList} from './withContainingArtworkList.js';
diff --git a/src/data/composite/things/artwork/withContainingArtworkList.js b/src/data/composite/things/artwork/withContainingArtworkList.js
new file mode 100644
index 00000000..9c928ffd
--- /dev/null
+++ b/src/data/composite/things/artwork/withContainingArtworkList.js
@@ -0,0 +1,46 @@
+// Gets the list of artworks which contains this one, which is functionally
+// equivalent to `this.thing[this.thingProperty]`. If the exposed value is not
+// a list at all (i.e. the property holds a single artwork), this composition
+// outputs null.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+
+export default templateCompositeFrom({
+  annotation: `withContainingArtworkList`,
+
+  outputs: ['#containingArtworkList'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'thing',
+      output: input.value({'#containingArtworkList': null}),
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: 'thingProperty',
+      output: input.value({'#containingArtworkList': null}),
+    }),
+
+    withPropertyFromObject({
+      object: 'thing',
+      property: 'thingProperty',
+    }).outputs({
+      '#value': '#containingValue',
+    }),
+
+    {
+      dependencies: ['#containingValue'],
+      compute: (continuation, {
+        ['#containingValue']: containingValue,
+      }) => continuation({
+        ['#containingArtworkList']:
+          (Array.isArray(containingValue)
+            ? containingValue
+            : null),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/content/hasAnnotationPart.js b/src/data/composite/things/content/hasAnnotationPart.js
new file mode 100644
index 00000000..93aaf5e5
--- /dev/null
+++ b/src/data/composite/things/content/hasAnnotationPart.js
@@ -0,0 +1,25 @@
+import {input, templateCompositeFrom} from '#composite';
+
+export default templateCompositeFrom({
+  annotation: `hasAnnotationPart`,
+
+  compose: false,
+
+  inputs: {
+    part: input({type: 'string'}),
+  },
+
+  steps: () => [
+    {
+      dependencies: [input('part'), 'annotationParts'],
+
+      compute: ({
+        [input('part')]: search,
+        ['annotationParts']: parts,
+      }) =>
+          parts.some(part =>
+            part.toLowerCase() ===
+            search.toLowerCase()),
+    },
+  ],
+});
diff --git a/src/data/composite/things/content/index.js b/src/data/composite/things/content/index.js
new file mode 100644
index 00000000..27bf7c53
--- /dev/null
+++ b/src/data/composite/things/content/index.js
@@ -0,0 +1,4 @@
+export {default as hasAnnotationPart} from './hasAnnotationPart.js';
+export {default as withAnnotationPartNodeLists} from './withAnnotationPartNodeLists.js';
+export {default as withExpressedOrImplicitArtistReferences} from './withExpressedOrImplicitArtistReferences.js';
+export {default as withWebArchiveDate} from './withWebArchiveDate.js';
diff --git a/src/data/composite/things/content/withAnnotationPartNodeLists.js b/src/data/composite/things/content/withAnnotationPartNodeLists.js
new file mode 100644
index 00000000..fc304594
--- /dev/null
+++ b/src/data/composite/things/content/withAnnotationPartNodeLists.js
@@ -0,0 +1,28 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {splitContentNodesAround, withContentNodes} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `withAnnotationPartNodeLists`,
+
+  outputs: ['#annotationPartNodeLists'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'annotation',
+      output: input.value({'#annotationPartNodeLists': []}),
+    }),
+
+    withContentNodes({
+      from: 'annotation',
+    }),
+
+    splitContentNodesAround({
+      nodes: '#contentNodes',
+      around: input.value(/, */g),
+    }).outputs({
+      '#contentNodeLists': '#annotationPartNodeLists',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js b/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
new file mode 100644
index 00000000..69da8c75
--- /dev/null
+++ b/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
@@ -0,0 +1,61 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withFilteredList, withMappedList} from '#composite/data';
+import {withContentNodes} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `withExpressedOrImplicitArtistReferences`,
+
+  inputs: {
+    from: input({type: 'array', acceptsNull: true}),
+  },
+
+  outputs: ['#artistReferences'],
+
+  steps: () => [
+    {
+      dependencies: [input('from')],
+      compute: (continuation, {
+        [input('from')]: expressedArtistReferences,
+      }) =>
+        (expressedArtistReferences
+          ? continuation.raiseOutput({'#artistReferences': expressedArtistReferences})
+          : continuation()),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: 'artistText',
+      output: input.value({'#artistReferences': null}),
+    }),
+
+    withContentNodes({
+      from: 'artistText',
+    }),
+
+    withMappedList({
+      list: '#contentNodes',
+      map: input.value(node =>
+        node.type === 'tag' &&
+        node.data.replacerKey?.data === 'artist'),
+    }).outputs({
+      '#mappedList': '#artistTagFilter',
+    }),
+
+    withFilteredList({
+      list: '#contentNodes',
+      filter: '#artistTagFilter',
+    }).outputs({
+      '#filteredList': '#artistTags',
+    }),
+
+    withMappedList({
+      list: '#artistTags',
+      map: input.value(node =>
+        'artist:' +
+        node.data.replacerValue[0].data),
+    }).outputs({
+      '#mappedList': '#artistReferences',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/content/withWebArchiveDate.js b/src/data/composite/things/content/withWebArchiveDate.js
new file mode 100644
index 00000000..3aaa4f64
--- /dev/null
+++ b/src/data/composite/things/content/withWebArchiveDate.js
@@ -0,0 +1,41 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+
+export default templateCompositeFrom({
+  annotation: `withWebArchiveDate`,
+
+  outputs: ['#webArchiveDate'],
+
+  steps: () => [
+    {
+      dependencies: ['annotation'],
+
+      compute: (continuation, {annotation}) =>
+        continuation({
+          ['#dateText']:
+            annotation
+              ?.match(/https?:\/\/web.archive.org\/web\/([0-9]{8,8})[0-9]*\//)
+              ?.[1] ??
+            null,
+        }),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: '#dateText',
+      output: input.value({['#webArchiveDate']: null}),
+    }),
+
+    {
+      dependencies: ['#dateText'],
+      compute: (continuation, {['#dateText']: dateText}) =>
+        continuation({
+          ['#webArchiveDate']:
+            new Date(
+              dateText.slice(0, 4) + '/' +
+              dateText.slice(4, 6) + '/' +
+              dateText.slice(6, 8)),
+        }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/contribution/index.js b/src/data/composite/things/contribution/index.js
new file mode 100644
index 00000000..2bbf994d
--- /dev/null
+++ b/src/data/composite/things/contribution/index.js
@@ -0,0 +1,3 @@
+export {default as inheritFromContributionPresets} from './inheritFromContributionPresets.js';
+export {default as withContainingReverseContributionList} from './withContainingReverseContributionList.js';
+export {default as withContributionContext} from './withContributionContext.js';
diff --git a/src/data/composite/things/contribution/inheritFromContributionPresets.js b/src/data/composite/things/contribution/inheritFromContributionPresets.js
new file mode 100644
index 00000000..17387404
--- /dev/null
+++ b/src/data/composite/things/contribution/inheritFromContributionPresets.js
@@ -0,0 +1,50 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withPropertyFromList} from '#composite/data';
+
+export default templateCompositeFrom({
+  annotation: `inheritFromContributionPresets`,
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'matchingPresets',
+      mode: input.value('empty'),
+    }),
+
+    withPropertyFromList({
+      list: 'matchingPresets',
+      property: input.thisProperty(),
+    }),
+
+    {
+      dependencies: ['#values'],
+
+      compute: (continuation, {
+        ['#values']: values,
+      }) => continuation({
+        ['#index']:
+          values.findIndex(value =>
+            value !== undefined &&
+            value !== null),
+      }),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: '#index',
+      mode: input.value('index'),
+    }),
+
+    {
+      dependencies: ['#values', '#index'],
+
+      compute: (continuation, {
+        ['#values']: values,
+        ['#index']: index,
+      }) => continuation({
+        ['#value']:
+          values[index],
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/contribution/withContainingReverseContributionList.js b/src/data/composite/things/contribution/withContainingReverseContributionList.js
new file mode 100644
index 00000000..a9ba31c9
--- /dev/null
+++ b/src/data/composite/things/contribution/withContainingReverseContributionList.js
@@ -0,0 +1,76 @@
+// Get the artist's contribution list containing this property. Although that
+// list literally includes both dated and dateless contributions, here, if the
+// current contribution is dateless, the list is filtered to only include
+// dateless contributions from the same immediately nearby context.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency, withResultOfAvailabilityCheck}
+  from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+
+export default templateCompositeFrom({
+  annotation: `withContainingReverseContributionList`,
+
+  inputs: {
+    artistProperty: input({
+      defaultDependency: '_artistProperty',
+      acceptsNull: true,
+    }),
+  },
+
+  outputs: ['#containingReverseContributionList'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: input('artistProperty'),
+      output: input.value({
+        ['#containingReverseContributionList']:
+          null,
+      }),
+    }),
+
+    withPropertyFromObject({
+      object: 'artist',
+      property: input('artistProperty'),
+    }).outputs({
+      ['#value']: '#list',
+    }),
+
+    withResultOfAvailabilityCheck({
+      from: 'date',
+    }).outputs({
+      ['#availability']: '#hasDate',
+    }),
+
+    {
+      dependencies: ['#hasDate', '#list'],
+      compute: (continuation, {
+        ['#hasDate']: hasDate,
+        ['#list']: list,
+      }) =>
+        (hasDate
+          ? continuation.raiseOutput({
+              ['#containingReverseContributionList']:
+                list.filter(contrib => contrib.date),
+            })
+          : continuation({
+              ['#list']:
+                list.filter(contrib => !contrib.date),
+            })),
+    },
+
+    {
+      dependencies: ['#list', 'thing'],
+      compute: (continuation, {
+        ['#list']: list,
+        ['thing']: thing,
+      }) => continuation({
+        ['#containingReverseContributionList']:
+          (thing.album
+            ? list.filter(contrib => contrib.thing.album === thing.album)
+            : list),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/contribution/withContributionContext.js b/src/data/composite/things/contribution/withContributionContext.js
new file mode 100644
index 00000000..3c1c31c0
--- /dev/null
+++ b/src/data/composite/things/contribution/withContributionContext.js
@@ -0,0 +1,45 @@
+import {input, templateCompositeFrom} from '#composite';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+
+export default templateCompositeFrom({
+  annotation: `withContributionContext`,
+
+  outputs: [
+    '#contributionTarget',
+    '#contributionProperty',
+  ],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'thing',
+      output: input.value({
+        '#contributionTarget': null,
+        '#contributionProperty': null,
+      }),
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: 'thingProperty',
+      output: input.value({
+        '#contributionTarget': null,
+        '#contributionProperty': null,
+      }),
+    }),
+
+    {
+      dependencies: ['thing', 'thingProperty'],
+
+      compute: (continuation, {
+        ['thing']: thing,
+        ['thingProperty']: thingProperty,
+      }) => continuation({
+        ['#contributionTarget']:
+          thing.constructor[Symbol.for('Thing.referenceType')],
+
+        ['#contributionProperty']:
+          thingProperty,
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/flash/index.js b/src/data/composite/things/flash/index.js
deleted file mode 100644
index 63ac13da..00000000
--- a/src/data/composite/things/flash/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export {default as withFlashAct} from './withFlashAct.js';
diff --git a/src/data/composite/things/flash/withFlashAct.js b/src/data/composite/things/flash/withFlashAct.js
deleted file mode 100644
index ada2dcfe..00000000
--- a/src/data/composite/things/flash/withFlashAct.js
+++ /dev/null
@@ -1,108 +0,0 @@
-// Gets the flash's act. This will early exit if flashActData is missing.
-// By default, if there's no flash whose list of flashes includes this flash,
-// the output dependency will be null; set {notFoundMode: 'exit'} to early
-// exit instead.
-//
-// This step models with Flash.withAlbum.
-
-import {input, templateCompositeFrom} from '#composite';
-import {is} from '#validators';
-
-import {exitWithoutDependency, withResultOfAvailabilityCheck}
-  from '#composite/control-flow';
-import {withPropertyFromList} from '#composite/data';
-
-export default templateCompositeFrom({
-  annotation: `withFlashAct`,
-
-  inputs: {
-    notFoundMode: input({
-      validate: is('exit', 'null'),
-      defaultValue: 'null',
-    }),
-  },
-
-  outputs: ['#flashAct'],
-
-  steps: () => [
-    // null flashActData is always an early exit.
-
-    exitWithoutDependency({
-      dependency: 'flashActData',
-      mode: input.value('null'),
-    }),
-
-    // empty flashActData conditionally exits early or outputs null.
-
-    withResultOfAvailabilityCheck({
-      from: 'flashActData',
-      mode: input.value('empty'),
-    }).outputs({
-      '#availability': '#flashActDataAvailability',
-    }),
-
-    {
-      dependencies: [input('notFoundMode'), '#flashActDataAvailability'],
-      compute(continuation, {
-        [input('notFoundMode')]: notFoundMode,
-        ['#flashActDataAvailability']: flashActDataIsAvailable,
-      }) {
-        if (flashActDataIsAvailable) return continuation();
-        switch (notFoundMode) {
-          case 'exit': return continuation.exit(null);
-          case 'null': return continuation.raiseOutput({'#flashAct': null});
-        }
-      },
-    },
-
-    withPropertyFromList({
-      list: 'flashActData',
-      property: input.value('flashes'),
-    }),
-
-    {
-      dependencies: [input.myself(), '#flashActData.flashes'],
-      compute: (continuation, {
-        [input.myself()]: track,
-        ['#flashActData.flashes']: flashLists,
-      }) => continuation({
-        ['#flashActIndex']:
-          flashLists.findIndex(flashes => flashes.includes(track)),
-      }),
-    },
-
-    // album not found conditionally exits or outputs null.
-
-    withResultOfAvailabilityCheck({
-      from: '#flashActIndex',
-      mode: input.value('index'),
-    }).outputs({
-      '#availability': '#flashActAvailability',
-    }),
-
-    {
-      dependencies: [input('notFoundMode'), '#flashActAvailability'],
-      compute(continuation, {
-        [input('notFoundMode')]: notFoundMode,
-        ['#flashActAvailability']: flashActIsAvailable,
-      }) {
-        if (flashActIsAvailable) return continuation();
-        switch (notFoundMode) {
-          case 'exit': return continuation.exit(null);
-          case 'null': return continuation.raiseOutput({'#flashAct': null});
-        }
-      },
-    },
-
-    {
-      dependencies: ['flashActData', '#flashActIndex'],
-      compute: (continuation, {
-        ['flashActData']: flashActData,
-        ['#flashActIndex']: flashActIndex,
-      }) => continuation.raiseOutput({
-        ['#flashAct']:
-          flashActData[flashActIndex],
-      }),
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/exitWithoutUniqueCoverArt.js b/src/data/composite/things/track/exitWithoutUniqueCoverArt.js
deleted file mode 100644
index f47086d9..00000000
--- a/src/data/composite/things/track/exitWithoutUniqueCoverArt.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// Shorthand for checking if the track has unique cover art and exposing a
-// fallback value if it isn't.
-
-import {input, templateCompositeFrom} from '#composite';
-
-import {exitWithoutDependency} from '#composite/control-flow';
-
-import withHasUniqueCoverArt from './withHasUniqueCoverArt.js';
-
-export default templateCompositeFrom({
-  annotation: `exitWithoutUniqueCoverArt`,
-
-  inputs: {
-    value: input({defaultValue: null}),
-  },
-
-  steps: () => [
-    withHasUniqueCoverArt(),
-
-    exitWithoutDependency({
-      dependency: '#hasUniqueCoverArt',
-      mode: input.value('falsy'),
-      value: input('value'),
-    }),
-  ],
-});
diff --git a/src/data/composite/things/track/index.js b/src/data/composite/things/track/index.js
index cc723a24..c200df19 100644
--- a/src/data/composite/things/track/index.js
+++ b/src/data/composite/things/track/index.js
@@ -1,11 +1,2 @@
-export {default as exitWithoutUniqueCoverArt} from './exitWithoutUniqueCoverArt.js';
-export {default as inferredAdditionalNameList} from './inferredAdditionalNameList.js';
-export {default as inheritFromOriginalRelease} from './inheritFromOriginalRelease.js';
-export {default as sharedAdditionalNameList} from './sharedAdditionalNameList.js';
-export {default as trackReverseReferenceList} from './trackReverseReferenceList.js';
-export {default as withAlbum} from './withAlbum.js';
-export {default as withAlwaysReferenceByDirectory} from './withAlwaysReferenceByDirectory.js';
-export {default as withContainingTrackSection} from './withContainingTrackSection.js';
-export {default as withHasUniqueCoverArt} from './withHasUniqueCoverArt.js';
-export {default as withOtherReleases} from './withOtherReleases.js';
-export {default as withPropertyFromAlbum} from './withPropertyFromAlbum.js';
+export {default as inheritContributionListFromMainRelease} from './inheritContributionListFromMainRelease.js';
+export {default as inheritFromMainRelease} from './inheritFromMainRelease.js';
diff --git a/src/data/composite/things/track/inferredAdditionalNameList.js b/src/data/composite/things/track/inferredAdditionalNameList.js
deleted file mode 100644
index 58e8d2a1..00000000
--- a/src/data/composite/things/track/inferredAdditionalNameList.js
+++ /dev/null
@@ -1,67 +0,0 @@
-// Infers additional name entries from other releases that were titled
-// differently; the corresponding releases are stored in eacn entry's "from"
-// array, which will include multiple items, if more than one other release
-// shares the same name differing from this one's.
-
-import {input, templateCompositeFrom} from '#composite';
-import {chunkByProperties} from '#sugar';
-
-import {exitWithoutDependency} from '#composite/control-flow';
-import {withFilteredList, withPropertyFromList} from '#composite/data';
-import {withThingsSortedAlphabetically} from '#composite/wiki-data';
-
-import withOtherReleases from './withOtherReleases.js';
-
-export default templateCompositeFrom({
-  annotation: `inferredAdditionalNameList`,
-
-  compose: false,
-
-  steps: () => [
-    withOtherReleases(),
-
-    exitWithoutDependency({
-      dependency: '#otherReleases',
-      mode: input.value('empty'),
-      value: input.value([]),
-    }),
-
-    withPropertyFromList({
-      list: '#otherReleases',
-      property: input.value('name'),
-    }),
-
-    {
-      dependencies: ['#otherReleases.name', 'name'],
-      compute: (continuation, {
-        ['#otherReleases.name']: releaseNames,
-        ['name']: ownName,
-      }) => continuation({
-        ['#nameFilter']:
-          releaseNames.map(name => name !== ownName),
-      }),
-    },
-
-    withFilteredList({
-      list: '#otherReleases',
-      filter: '#nameFilter',
-    }).outputs({
-      '#filteredList': '#differentlyNamedReleases',
-    }),
-
-    withThingsSortedAlphabetically({
-      things: '#differentlyNamedReleases',
-    }).outputs({
-      '#sortedThings': '#differentlyNamedReleases',
-    }),
-
-    {
-      dependencies: ['#differentlyNamedReleases'],
-      compute: ({
-        ['#differentlyNamedReleases']: releases,
-      }) =>
-        chunkByProperties(releases, ['name'])
-          .map(({name, chunk}) => ({name, from: chunk})),
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/inheritContributionListFromMainRelease.js b/src/data/composite/things/track/inheritContributionListFromMainRelease.js
new file mode 100644
index 00000000..8db50060
--- /dev/null
+++ b/src/data/composite/things/track/inheritContributionListFromMainRelease.js
@@ -0,0 +1,41 @@
+// Like inheritFromMainRelease, but tuned for contributions.
+// Recontextualizes contributions for this track.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import {exposeDependency, raiseOutputWithoutDependency}
+  from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+import {withRecontextualizedContributionList, withRedatedContributionList}
+  from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `inheritContributionListFromMainRelease`,
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'isSecondaryRelease',
+      mode: input.value('falsy'),
+    }),
+
+    withPropertyFromObject({
+      object: 'mainReleaseTrack',
+      property: input.thisProperty(),
+    }).outputs({
+      '#value': '#contributions',
+    }),
+
+    withRecontextualizedContributionList({
+      list: '#contributions',
+    }),
+
+    withRedatedContributionList({
+      list: '#contributions',
+      date: 'date',
+    }),
+
+    exposeDependency({
+      dependency: '#contributions',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/track/inheritFromMainRelease.js b/src/data/composite/things/track/inheritFromMainRelease.js
new file mode 100644
index 00000000..ca532bc7
--- /dev/null
+++ b/src/data/composite/things/track/inheritFromMainRelease.js
@@ -0,0 +1,29 @@
+// Early exits with the value for the same property as specified on the
+// main release, if this track is a secondary release, and otherwise continues
+// without providing any further dependencies.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import {exposeDependency, raiseOutputWithoutDependency}
+  from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+
+export default templateCompositeFrom({
+  annotation: `inheritFromMainRelease`,
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'isSecondaryRelease',
+      mode: input.value('falsy'),
+    }),
+
+    withPropertyFromObject({
+      object: 'mainReleaseTrack',
+      property: input.thisProperty(),
+    }),
+
+    exposeDependency({
+      dependency: '#value',
+    }),
+  ],
+});
diff --git a/src/data/composite/things/track/inheritFromOriginalRelease.js b/src/data/composite/things/track/inheritFromOriginalRelease.js
deleted file mode 100644
index 27ed1387..00000000
--- a/src/data/composite/things/track/inheritFromOriginalRelease.js
+++ /dev/null
@@ -1,50 +0,0 @@
-// Early exits with a value inherited from the original release, if
-// this track is a rerelease, and otherwise continues with no further
-// dependencies provided. If allowOverride is true, then the continuation
-// will also be called if the original release exposed the requested
-// property as null.
-//
-// Like withOriginalRelease, this will early exit (with notFoundValue) if the
-// original release is specified by reference and that reference doesn't
-// resolve to anything.
-
-import {input, templateCompositeFrom} from '#composite';
-
-import withOriginalRelease from './withOriginalRelease.js';
-
-export default templateCompositeFrom({
-  annotation: `inheritFromOriginalRelease`,
-
-  inputs: {
-    property: input({type: 'string'}),
-    allowOverride: input({type: 'boolean', defaultValue: false}),
-    notFoundValue: input({defaultValue: null}),
-  },
-
-  steps: () => [
-    withOriginalRelease({
-      notFoundValue: input('notFoundValue'),
-    }),
-
-    {
-      dependencies: [
-        '#originalRelease',
-        input('property'),
-        input('allowOverride'),
-      ],
-
-      compute: (continuation, {
-        ['#originalRelease']: originalRelease,
-        [input('property')]: originalProperty,
-        [input('allowOverride')]: allowOverride,
-      }) => {
-        if (!originalRelease) return continuation();
-
-        const value = originalRelease[originalProperty];
-        if (allowOverride && value === null) return continuation();
-
-        return continuation.exit(value);
-      },
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/sharedAdditionalNameList.js b/src/data/composite/things/track/sharedAdditionalNameList.js
deleted file mode 100644
index 1806ec80..00000000
--- a/src/data/composite/things/track/sharedAdditionalNameList.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Compiles additional names directly provided by other releases.
-
-import {input, templateCompositeFrom} from '#composite';
-
-import {exitWithoutDependency, exposeDependency}
-  from '#composite/control-flow';
-import {withFlattenedList, withPropertyFromList} from '#composite/data';
-
-import withOtherReleases from './withOtherReleases.js';
-
-export default templateCompositeFrom({
-  annotation: `sharedAdditionalNameList`,
-
-  compose: false,
-
-  steps: () => [
-    withOtherReleases(),
-
-    exitWithoutDependency({
-      dependency: '#otherReleases',
-      mode: input.value('empty'),
-      value: input.value([]),
-    }),
-
-    withPropertyFromList({
-      list: '#otherReleases',
-      property: input.value('additionalNames'),
-    }),
-
-    withFlattenedList({
-      list: '#otherReleases.additionalNames',
-    }),
-
-    exposeDependency({
-      dependency: '#flattenedList',
-    }),
-  ],
-});
diff --git a/src/data/composite/things/track/trackAdditionalNameList.js b/src/data/composite/things/track/trackAdditionalNameList.js
deleted file mode 100644
index 65a2263d..00000000
--- a/src/data/composite/things/track/trackAdditionalNameList.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Compiles additional names from various sources.
-
-import {input, templateCompositeFrom} from '#composite';
-import {isAdditionalNameList} from '#validators';
-
-import withInferredAdditionalNames from './withInferredAdditionalNames.js';
-import withSharedAdditionalNames from './withSharedAdditionalNames.js';
-
-export default templateCompositeFrom({
-  annotation: `trackAdditionalNameList`,
-
-  compose: false,
-
-  update: {validate: isAdditionalNameList},
-
-  steps: () => [
-    withInferredAdditionalNames(),
-    withSharedAdditionalNames(),
-
-    {
-      dependencies: [
-        '#inferredAdditionalNames',
-        '#sharedAdditionalNames',
-        input.updateValue(),
-      ],
-
-      compute: ({
-        ['#inferredAdditionalNames']: inferredAdditionalNames,
-        ['#sharedAdditionalNames']: sharedAdditionalNames,
-        [input.updateValue()]: providedAdditionalNames,
-      }) => [
-        ...providedAdditionalNames ?? [],
-        ...sharedAdditionalNames,
-        ...inferredAdditionalNames,
-      ],
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/trackReverseReferenceList.js b/src/data/composite/things/track/trackReverseReferenceList.js
deleted file mode 100644
index 44940ae7..00000000
--- a/src/data/composite/things/track/trackReverseReferenceList.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Like a normal reverse reference list ("objects which reference this object
-// under a specified property"), only excluding rereleases from the possible
-// outputs. While it's useful to travel from a rerelease to the tracks it
-// references, rereleases aren't generally relevant from the perspective of
-// the tracks *being* referenced. Apart from hiding rereleases from lists on
-// the site, it also excludes keeps them from relational data processing, such
-// as on the "Tracks - by Times Referenced" listing page.
-
-import {input, templateCompositeFrom} from '#composite';
-import {withReverseReferenceList} from '#composite/wiki-data';
-
-export default templateCompositeFrom({
-  annotation: `trackReverseReferenceList`,
-
-  compose: false,
-
-  inputs: {
-    list: input({type: 'string'}),
-  },
-
-  steps: () => [
-    withReverseReferenceList({
-      data: 'trackData',
-      list: input('list'),
-    }),
-
-    {
-      flags: {expose: true},
-      expose: {
-        dependencies: ['#reverseReferenceList'],
-        compute: ({
-          ['#reverseReferenceList']: reverseReferenceList,
-        }) =>
-          reverseReferenceList.filter(track => !track.originalReleaseTrack),
-      },
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withAlbum.js b/src/data/composite/things/track/withAlbum.js
deleted file mode 100644
index cbd16dcd..00000000
--- a/src/data/composite/things/track/withAlbum.js
+++ /dev/null
@@ -1,108 +0,0 @@
-// Gets the track's album. This will early exit if albumData is missing.
-// By default, if there's no album whose list of tracks includes this track,
-// the output dependency will be null; set {notFoundMode: 'exit'} to early
-// exit instead.
-//
-// This step models with Flash.withFlashAct.
-
-import {input, templateCompositeFrom} from '#composite';
-import {is} from '#validators';
-
-import {exitWithoutDependency, withResultOfAvailabilityCheck}
-  from '#composite/control-flow';
-import {withPropertyFromList} from '#composite/data';
-
-export default templateCompositeFrom({
-  annotation: `withAlbum`,
-
-  inputs: {
-    notFoundMode: input({
-      validate: is('exit', 'null'),
-      defaultValue: 'null',
-    }),
-  },
-
-  outputs: ['#album'],
-
-  steps: () => [
-    // null albumData is always an early exit.
-
-    exitWithoutDependency({
-      dependency: 'albumData',
-      mode: input.value('null'),
-    }),
-
-    // empty albumData conditionally exits early or outputs null.
-
-    withResultOfAvailabilityCheck({
-      from: 'albumData',
-      mode: input.value('empty'),
-    }).outputs({
-      '#availability': '#albumDataAvailability',
-    }),
-
-    {
-      dependencies: [input('notFoundMode'), '#albumDataAvailability'],
-      compute(continuation, {
-        [input('notFoundMode')]: notFoundMode,
-        ['#albumDataAvailability']: albumDataIsAvailable,
-      }) {
-        if (albumDataIsAvailable) return continuation();
-        switch (notFoundMode) {
-          case 'exit': return continuation.exit(null);
-          case 'null': return continuation.raiseOutput({'#album': null});
-        }
-      },
-    },
-
-    withPropertyFromList({
-      list: 'albumData',
-      property: input.value('tracks'),
-    }),
-
-    {
-      dependencies: [input.myself(), '#albumData.tracks'],
-      compute: (continuation, {
-        [input.myself()]: track,
-        ['#albumData.tracks']: trackLists,
-      }) => continuation({
-        ['#albumIndex']:
-          trackLists.findIndex(tracks => tracks.includes(track)),
-      }),
-    },
-
-    // album not found conditionally exits or outputs null.
-
-    withResultOfAvailabilityCheck({
-      from: '#albumIndex',
-      mode: input.value('index'),
-    }).outputs({
-      '#availability': '#albumAvailability',
-    }),
-
-    {
-      dependencies: [input('notFoundMode'), '#albumAvailability'],
-      compute(continuation, {
-        [input('notFoundMode')]: notFoundMode,
-        ['#albumAvailability']: albumIsAvailable,
-      }) {
-        if (albumIsAvailable) return continuation();
-        switch (notFoundMode) {
-          case 'exit': return continuation.exit(null);
-          case 'null': return continuation.raiseOutput({'#album': null});
-        }
-      },
-    },
-
-    {
-      dependencies: ['albumData', '#albumIndex'],
-      compute: (continuation, {
-        ['albumData']: albumData,
-        ['#albumIndex']: albumIndex,
-      }) => continuation.raiseOutput({
-        ['#album']:
-          albumData[albumIndex],
-      }),
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withAlwaysReferenceByDirectory.js b/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
deleted file mode 100644
index fac8e213..00000000
--- a/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
+++ /dev/null
@@ -1,78 +0,0 @@
-// Controls how find.track works - it'll never be matched by a reference
-// just to the track's name, which means you don't have to always reference
-// some *other* (much more commonly referenced) track by directory instead
-// of more naturally by name.
-
-import {input, templateCompositeFrom} from '#composite';
-import find from '#find';
-import {isBoolean} from '#validators';
-
-import {exitWithoutDependency, exposeUpdateValueOrContinue}
-  from '#composite/control-flow';
-import {withPropertyFromObject} from '#composite/data';
-import {withResolvedReference} from '#composite/wiki-data';
-
-export default templateCompositeFrom({
-  annotation: `withAlwaysReferenceByDirectory`,
-
-  outputs: ['#alwaysReferenceByDirectory'],
-
-  steps: () => [
-    exposeUpdateValueOrContinue({
-      validate: input.value(isBoolean),
-    }),
-
-    // Remaining code is for defaulting to true if this track is a rerelease of
-    // another with the same name, so everything further depends on access to
-    // trackData as well as originalReleaseTrack.
-
-    exitWithoutDependency({
-      dependency: 'trackData',
-      mode: input.value('empty'),
-      value: input.value(false),
-    }),
-
-    exitWithoutDependency({
-      dependency: 'originalReleaseTrack',
-      value: input.value(false),
-    }),
-
-    // It's necessary to use the custom trackOriginalReleasesOnly find function
-    // here, so as to avoid recursion issues - the find.track() function depends
-    // on accessing each track's alwaysReferenceByDirectory, which means it'll
-    // hit *this track* - and thus this step - and end up recursing infinitely.
-    // By definition, find.trackOriginalReleasesOnly excludes tracks which have
-    // an originalReleaseTrack update value set, which means even though it does
-    // still access each of tracks' `alwaysReferenceByDirectory` property, it
-    // won't access that of *this* track - it will never proceed past the
-    // `exitWithoutDependency` step directly above, so there's no opportunity
-    // for recursion.
-    withResolvedReference({
-      ref: 'originalReleaseTrack',
-      data: 'trackData',
-      find: input.value(find.trackOriginalReleasesOnly),
-    }).outputs({
-      '#resolvedReference': '#originalRelease',
-    }),
-
-    exitWithoutDependency({
-      dependency: '#originalRelease',
-      value: input.value(false),
-    }),
-
-    withPropertyFromObject({
-      object: '#originalRelease',
-      property: input.value('name'),
-    }),
-
-    {
-      dependencies: ['name', '#originalRelease.name'],
-      compute: (continuation, {
-        name,
-        ['#originalRelease.name']: originalName,
-      }) => continuation({
-        ['#alwaysReferenceByDirectory']: name === originalName,
-      }),
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withContainingTrackSection.js b/src/data/composite/things/track/withContainingTrackSection.js
deleted file mode 100644
index b2e5f2b3..00000000
--- a/src/data/composite/things/track/withContainingTrackSection.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Gets the track section containing this track from its album's track list.
-// If notFoundMode is set to 'exit', this will early exit if the album can't be
-// found or if none of its trackSections includes the track for some reason.
-
-import {input, templateCompositeFrom} from '#composite';
-import {is} from '#validators';
-
-import withPropertyFromAlbum from './withPropertyFromAlbum.js';
-
-export default templateCompositeFrom({
-  annotation: `withContainingTrackSection`,
-
-  inputs: {
-    notFoundMode: input({
-      validate: is('exit', 'null'),
-      defaultValue: 'null',
-    }),
-  },
-
-  outputs: ['#trackSection'],
-
-  steps: () => [
-    withPropertyFromAlbum({
-      property: input.value('trackSections'),
-      notFoundMode: input('notFoundMode'),
-    }),
-
-    {
-      dependencies: [
-        input.myself(),
-        input('notFoundMode'),
-        '#album.trackSections',
-      ],
-
-      compute(continuation, {
-        [input.myself()]: track,
-        [input('notFoundMode')]: notFoundMode,
-        ['#album.trackSections']: trackSections,
-      }) {
-        if (!trackSections) {
-          return continuation.raiseOutput({
-            ['#trackSection']: null,
-          });
-        }
-
-        const trackSection =
-          trackSections.find(({tracks}) => tracks.includes(track));
-
-        if (trackSection) {
-          return continuation.raiseOutput({
-            ['#trackSection']: trackSection,
-          });
-        } else if (notFoundMode === 'exit') {
-          return continuation.exit(null);
-        } else {
-          return continuation.raiseOutput({
-            ['#trackSection']: null,
-          });
-        }
-      },
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withHasUniqueCoverArt.js b/src/data/composite/things/track/withHasUniqueCoverArt.js
deleted file mode 100644
index 96078d5f..00000000
--- a/src/data/composite/things/track/withHasUniqueCoverArt.js
+++ /dev/null
@@ -1,61 +0,0 @@
-// Whether or not the track has "unique" cover artwork - a cover which is
-// specifically associated with this track in particular, rather than with
-// the track's album as a whole. This is typically used to select between
-// displaying the track artwork and a fallback, such as the album artwork
-// or a placeholder. (This property is named hasUniqueCoverArt instead of
-// the usual hasCoverArt to emphasize that it does not inherit from the
-// album.)
-
-import {input, templateCompositeFrom} from '#composite';
-import {empty} from '#sugar';
-
-import {withResolvedContribs} from '#composite/wiki-data';
-
-import withPropertyFromAlbum from './withPropertyFromAlbum.js';
-
-export default templateCompositeFrom({
-  annotation: 'withHasUniqueCoverArt',
-
-  outputs: ['#hasUniqueCoverArt'],
-
-  steps: () => [
-    {
-      dependencies: ['disableUniqueCoverArt'],
-      compute: (continuation, {disableUniqueCoverArt}) =>
-        (disableUniqueCoverArt
-          ? continuation.raiseOutput({
-              ['#hasUniqueCoverArt']: false,
-            })
-          : continuation()),
-    },
-
-    withResolvedContribs({from: 'coverArtistContribs'}),
-
-    {
-      dependencies: ['#resolvedContribs'],
-      compute: (continuation, {
-        ['#resolvedContribs']: contribsFromTrack,
-      }) =>
-        (empty(contribsFromTrack)
-          ? continuation()
-          : continuation.raiseOutput({
-              ['#hasUniqueCoverArt']: true,
-            })),
-    },
-
-    withPropertyFromAlbum({
-      property: input.value('trackCoverArtistContribs'),
-    }),
-
-    {
-      dependencies: ['#album.trackCoverArtistContribs'],
-      compute: (continuation, {
-        ['#album.trackCoverArtistContribs']: contribsFromAlbum,
-      }) =>
-        continuation.raiseOutput({
-          ['#hasUniqueCoverArt']:
-            !empty(contribsFromAlbum),
-        }),
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withOriginalRelease.js b/src/data/composite/things/track/withOriginalRelease.js
deleted file mode 100644
index c7f49657..00000000
--- a/src/data/composite/things/track/withOriginalRelease.js
+++ /dev/null
@@ -1,78 +0,0 @@
-// Just includes the original release of this track as a dependency.
-// If this track isn't a rerelease, then it'll provide null, unless the
-// {selfIfOriginal} option is set, in which case it'll provide this track
-// itself. This will early exit (with notFoundValue) if the original release
-// is specified by reference and that reference doesn't resolve to anything.
-
-import {input, templateCompositeFrom} from '#composite';
-import find from '#find';
-import {validateWikiData} from '#validators';
-
-import {exitWithoutDependency, withResultOfAvailabilityCheck}
-  from '#composite/control-flow';
-import {withResolvedReference} from '#composite/wiki-data';
-
-export default templateCompositeFrom({
-  annotation: `withOriginalRelease`,
-
-  inputs: {
-    selfIfOriginal: input({type: 'boolean', defaultValue: false}),
-
-    data: input({
-      validate: validateWikiData({referenceType: 'track'}),
-      defaultDependency: 'trackData',
-    }),
-
-    notFoundValue: input({defaultValue: null}),
-  },
-
-  outputs: ['#originalRelease'],
-
-  steps: () => [
-    withResultOfAvailabilityCheck({
-      from: 'originalReleaseTrack',
-    }),
-
-    {
-      dependencies: [
-        input.myself(),
-        input('selfIfOriginal'),
-        '#availability',
-      ],
-
-      compute: (continuation, {
-        [input.myself()]: track,
-        [input('selfIfOriginal')]: selfIfOriginal,
-        '#availability': availability,
-      }) =>
-        (availability
-          ? continuation()
-          : continuation.raiseOutput({
-              ['#originalRelease']:
-                (selfIfOriginal ? track : null),
-            })),
-    },
-
-    withResolvedReference({
-      ref: 'originalReleaseTrack',
-      data: input('data'),
-      find: input.value(find.track),
-    }),
-
-    exitWithoutDependency({
-      dependency: '#resolvedReference',
-      value: input('notFoundValue'),
-    }),
-
-    {
-      dependencies: ['#resolvedReference'],
-
-      compute: (continuation, {
-        ['#resolvedReference']: resolvedReference,
-      }) =>
-        continuation({
-          ['#originalRelease']: resolvedReference,
-        }),
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withOtherReleases.js b/src/data/composite/things/track/withOtherReleases.js
deleted file mode 100644
index f8c1c3f0..00000000
--- a/src/data/composite/things/track/withOtherReleases.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import {input, templateCompositeFrom} from '#composite';
-
-import {exitWithoutDependency} from '#composite/control-flow';
-
-import withOriginalRelease from './withOriginalRelease.js';
-
-export default templateCompositeFrom({
-  annotation: `withOtherReleases`,
-
-  outputs: ['#otherReleases'],
-
-  steps: () => [
-    exitWithoutDependency({
-      dependency: 'trackData',
-      mode: input.value('empty'),
-    }),
-
-    withOriginalRelease({
-      selfIfOriginal: input.value(true),
-      notFoundValue: input.value([]),
-    }),
-
-    {
-      dependencies: [input.myself(), '#originalRelease', 'trackData'],
-      compute: (continuation, {
-        [input.myself()]: thisTrack,
-        ['#originalRelease']: originalRelease,
-        trackData,
-      }) => continuation({
-        ['#otherReleases']:
-          (originalRelease === thisTrack
-            ? []
-            : [originalRelease])
-            .concat(trackData.filter(track =>
-              track !== originalRelease &&
-              track !== thisTrack &&
-              track.originalReleaseTrack === originalRelease)),
-      }),
-    },
-  ],
-});
diff --git a/src/data/composite/things/track/withPropertyFromAlbum.js b/src/data/composite/things/track/withPropertyFromAlbum.js
deleted file mode 100644
index b236a6e8..00000000
--- a/src/data/composite/things/track/withPropertyFromAlbum.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// Gets a single property from this track's album, providing it as the same
-// property name prefixed with '#album.' (by default). If the track's album
-// isn't available, then by default, the property will be provided as null;
-// set {notFoundMode: 'exit'} to early exit instead.
-
-import {input, templateCompositeFrom} from '#composite';
-import {is} from '#validators';
-
-import {withPropertyFromObject} from '#composite/data';
-
-import withAlbum from './withAlbum.js';
-
-export default templateCompositeFrom({
-  annotation: `withPropertyFromAlbum`,
-
-  inputs: {
-    property: input.staticValue({type: 'string'}),
-
-    notFoundMode: input({
-      validate: is('exit', 'null'),
-      defaultValue: 'null',
-    }),
-  },
-
-  outputs: ({
-    [input.staticValue('property')]: property,
-  }) => ['#album.' + property],
-
-  steps: () => [
-    withAlbum({
-      notFoundMode: input('notFoundMode'),
-    }),
-
-    withPropertyFromObject({
-      object: '#album',
-      property: input('property'),
-    }),
-
-    {
-      dependencies: ['#value', input.staticValue('property')],
-      compute: (continuation, {
-        ['#value']: value,
-        [input.staticValue('property')]: property,
-      }) => continuation({
-        ['#album.' + property]: value,
-      }),
-    },
-  ],
-});