« 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/things
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things')
-rw-r--r--src/data/things/adventure.js62
-rw-r--r--src/data/things/album.js179
-rw-r--r--src/data/things/art-tag.js3
-rw-r--r--src/data/things/artist.js19
-rw-r--r--src/data/things/artwork.js6
-rw-r--r--src/data/things/content.js8
-rw-r--r--src/data/things/flash.js36
-rw-r--r--src/data/things/group.js12
-rw-r--r--src/data/things/homepage-layout.js6
-rw-r--r--src/data/things/news-entry.js3
-rw-r--r--src/data/things/sorting-rule.js3
-rw-r--r--src/data/things/static-page.js3
-rw-r--r--src/data/things/track.js29
-rw-r--r--src/data/things/wiki-info.js10
14 files changed, 137 insertions, 242 deletions
diff --git a/src/data/things/adventure.js b/src/data/things/adventure.js
index ed5da39b..98b23f39 100644
--- a/src/data/things/adventure.js
+++ b/src/data/things/adventure.js
@@ -16,6 +16,7 @@ import {Flash, FlashAct} from './flash.js';
 
 export class Adventure extends Thing {
   static [Thing.referenceType] = 'adventure';
+  static [Thing.wikiData] = 'adventureData';
 
   static [Thing.getPropertyDescriptors] = ({FlashAct}) => ({
     // > Internal relationships
@@ -63,56 +64,41 @@ export class Adventure extends Thing {
         ? AdventureFlashAct
         : AdventureFlash),
 
-    save(results) {
-      const adventureData = [];
-      const flashActData = [];
-      const flashData = [];
+    connect({header: adventure, entries}) {
+      const acts = [];
 
-      for (const {header: adventure, entries} of results) {
-        const acts = [];
+      let thing, i;
+      for (i = 0; thing = entries[i]; i++) {
+        if (thing.isFlashAct) {
+          const act = thing;
+          const flashes = [];
 
-        let thing, i;
-        for (i = 0; thing = entries[i]; i++) {
-          if (thing.isFlashAct) {
-            const act = thing;
-            const flashes = [];
+          for (i++; thing = entries[i]; i++) {
+            if (thing.isFlash) {
+              const flash = thing;
 
-            for (i++; thing = entries[i]; i++) {
-              if (thing.isFlash) {
-                const flash = thing;
+              flash.act = act;
+              flashes.push(flash);
 
-                flash.act = act;
-                flashes.push(flash);
-                flashData.push(flash);
-
-                continue;
-              }
-
-              i--;
-              break;
+              continue;
             }
 
-            act.flashes = flashes;
-            acts.push(act);
-            flashActData.push(act);
-
-            continue;
+            i--;
+            break;
           }
 
-          if (thing.isFlash) {
-            throw new Error(`Flashes must be under a flash act`);
-          }
+          act.flashes = flashes;
+          acts.push(act);
+
+          continue;
         }
 
-        adventure.acts = acts;
-        adventureData.push(adventure);
+        if (thing.isFlash) {
+          throw new Error(`Flashes must be under a flash act`);
+        }
       }
 
-      return {
-        adventureData,
-        flashActData,
-        flashData,
-      };
+      adventure.acts = acts;
     },
   });
 }
diff --git a/src/data/things/album.js b/src/data/things/album.js
index c0042d25..defb8a87 100644
--- a/src/data/things/album.js
+++ b/src/data/things/album.js
@@ -78,11 +78,18 @@ import {
 } from '#composite/wiki-properties';
 
 import {withCoverArtDate, withTracks} from '#composite/things/album';
-import {withAlbum, withContinueCountingFrom, withStartCountingFrom}
+import {withContinueCountingFrom, withStartCountingFrom}
   from '#composite/things/track-section';
 
 export class Album extends Thing {
   static [Thing.referenceType] = 'album';
+  static [Thing.wikiData] = 'albumData';
+
+  static [Thing.constitutibleProperties] = [
+    'coverArtworks',
+    'wallpaperArtwork',
+    'bannerArtwork',
+  ];
 
   static [Thing.getPropertyDescriptors] = ({
     AdditionalFile,
@@ -569,20 +576,6 @@ export class Album extends Thing {
   };
 
   static [Thing.reverseSpecs] = {
-    albumsWhoseTracksInclude: {
-      bindTo: 'albumData',
-
-      referencing: album => [album],
-      referenced: album => album.tracks,
-    },
-
-    albumsWhoseTrackSectionsInclude: {
-      bindTo: 'albumData',
-
-      referencing: album => [album],
-      referenced: album => album.trackSections,
-    },
-
     albumsWhoseArtworksFeature: {
       bindTo: 'albumData',
 
@@ -882,101 +875,48 @@ export class Album extends Thing {
         ? TrackSection
         : Track),
 
-    save(results) {
-      const albumData = [];
-      const trackSectionData = [];
-      const trackData = [];
-
-      const artworkData = [];
-      const commentaryData = [];
-      const creditingSourceData = [];
-      const referencingSourceData = [];
-      const lyricsData = [];
-
-      for (const {header: album, entries} of results) {
-        const trackSections = [];
-
-        let currentTrackSection = new TrackSection();
-        let currentTrackSectionTracks = [];
-
-        Object.assign(currentTrackSection, {
-          name: `Default Track Section`,
-          isDefaultTrackSection: true,
-        });
-
-        const closeCurrentTrackSection = () => {
-          if (
-            currentTrackSection.isDefaultTrackSection &&
-            empty(currentTrackSectionTracks)
-          ) {
-            return;
-          }
-
-          currentTrackSection.tracks =
-            currentTrackSectionTracks;
-
-          trackSections.push(currentTrackSection);
-          trackSectionData.push(currentTrackSection);
-        };
-
-        for (const entry of entries) {
-          if (entry instanceof TrackSection) {
-            closeCurrentTrackSection();
-            currentTrackSection = entry;
-            currentTrackSectionTracks = [];
-            continue;
-          }
-
-          currentTrackSectionTracks.push(entry);
-          trackData.push(entry);
-
-          // Set the track's album before accessing its list of artworks.
-          // The existence of its artwork objects may depend on access to
-          // its album's 'Default Track Cover Artists'.
-          entry.album = album;
-
-          artworkData.push(...entry.trackArtworks);
-          commentaryData.push(...entry.commentary);
-          creditingSourceData.push(...entry.creditingSources);
-          referencingSourceData.push(...entry.referencingSources);
-
-          // TODO: As exposed, Track.lyrics tries to inherit from the main
-          // release, which is impossible before the data's been linked.
-          // We just use the update value here. But it's icky!
-          lyricsData.push(...CacheableObject.getUpdateValue(entry, 'lyrics') ?? []);
-        }
-
-        closeCurrentTrackSection();
+    connect({header: album, entries}) {
+      const trackSections = [];
 
-        albumData.push(album);
+      let currentTrackSection = new TrackSection();
+      let currentTrackSectionTracks = [];
 
-        artworkData.push(...album.coverArtworks);
+      Object.assign(currentTrackSection, {
+        name: `Default Track Section`,
+        isDefaultTrackSection: true,
+      });
 
-        if (album.bannerArtwork) {
-          artworkData.push(album.bannerArtwork);
+      const closeCurrentTrackSection = () => {
+        if (
+          currentTrackSection.isDefaultTrackSection &&
+          empty(currentTrackSectionTracks)
+        ) {
+          return;
         }
 
-        if (album.wallpaperArtwork) {
-          artworkData.push(album.wallpaperArtwork);
+        currentTrackSection.tracks = currentTrackSectionTracks;
+        currentTrackSection.album = album;
+
+        trackSections.push(currentTrackSection);
+      };
+
+      for (const entry of entries) {
+        if (entry instanceof TrackSection) {
+          closeCurrentTrackSection();
+          currentTrackSection = entry;
+          currentTrackSectionTracks = [];
+          continue;
         }
 
-        commentaryData.push(...album.commentary);
-        creditingSourceData.push(...album.creditingSources);
+        entry.album = album;
+        entry.trackSection = currentTrackSection;
 
-        album.trackSections = trackSections;
+        currentTrackSectionTracks.push(entry);
       }
 
-      return {
-        albumData,
-        trackSectionData,
-        trackData,
+      closeCurrentTrackSection();
 
-        artworkData,
-        commentaryData,
-        creditingSourceData,
-        referencingSourceData,
-        lyricsData,
-      };
+      album.trackSections = trackSections;
     },
 
     sort({albumData, trackData}) {
@@ -1041,10 +981,15 @@ export class Album extends Thing {
 export class TrackSection extends Thing {
   static [Thing.friendlyName] = `Track Section`;
   static [Thing.referenceType] = `track-section`;
+  static [Thing.wikiData] = 'trackSectionData';
 
   static [Thing.getPropertyDescriptors] = ({Track}) => ({
     // Update & expose
 
+    album: thing({
+      class: input.value(Album),
+    }),
+
     name: name('Unnamed Track Section'),
 
     unqualifiedDirectory: directory(),
@@ -1054,10 +999,8 @@ export class TrackSection extends Thing {
         validate: input.value(isDirectory),
       }),
 
-      withAlbum(),
-
       withPropertyFromObject({
-        object: '#album',
+        object: 'album',
         property: input.value('directorySuffix'),
       }),
 
@@ -1069,10 +1012,8 @@ export class TrackSection extends Thing {
         validate: input.value(isBoolean),
       }),
 
-      withAlbum(),
-
       withPropertyFromObject({
-        object: '#album',
+        object: 'album',
         property: input.value('suffixTrackDirectories'),
       }),
 
@@ -1084,10 +1025,8 @@ export class TrackSection extends Thing {
         validate: input.value(isColor),
       }),
 
-      withAlbum(),
-
       withPropertyFromObject({
-        object: '#album',
+        object: 'album',
         property: input.value('color'),
       }),
 
@@ -1109,10 +1048,8 @@ export class TrackSection extends Thing {
         validate: input.value(isBoolean),
       }),
 
-      withAlbum(),
-
       withPropertyFromObject({
-        object: '#album',
+        object: 'album',
         property: input.value('countTracksInArtistTotals'),
       }),
 
@@ -1123,11 +1060,6 @@ export class TrackSection extends Thing {
 
     description: contentString(),
 
-    album: [
-      withAlbum(),
-      exposeDependency({dependency: '#album'}),
-    ],
-
     tracks: thingList({
       class: input.value(Track),
     }),
@@ -1145,14 +1077,12 @@ export class TrackSection extends Thing {
     ],
 
     directory: [
-      withAlbum(),
-
       exitWithoutDependency({
-        dependency: '#album',
+        dependency: 'album',
       }),
 
       withPropertyFromObject({
-        object: '#album',
+        object: 'album',
         property: input.value('directory'),
       }),
 
@@ -1193,15 +1123,6 @@ export class TrackSection extends Thing {
     },
   };
 
-  static [Thing.reverseSpecs] = {
-    trackSectionsWhichInclude: {
-      bindTo: 'trackSectionData',
-
-      referencing: trackSection => [trackSection],
-      referenced: trackSection => trackSection.tracks,
-    },
-  };
-
   static [Thing.yamlDocumentSpec] = {
     fields: {
       'Section': {property: 'name'},
diff --git a/src/data/things/art-tag.js b/src/data/things/art-tag.js
index fff724cb..3570b2e7 100644
--- a/src/data/things/art-tag.js
+++ b/src/data/things/art-tag.js
@@ -40,6 +40,7 @@ import {withAllDescendantArtTags, withAncestorArtTagBaobabTree}
 export class ArtTag extends Thing {
   static [Thing.referenceType] = 'tag';
   static [Thing.friendlyName] = `Art Tag`;
+  static [Thing.wikiData] = 'artTagData';
 
   static [Thing.getPropertyDescriptors] = ({AdditionalName}) => ({
     // Update & expose
@@ -210,8 +211,6 @@ export class ArtTag extends Thing {
     documentMode: allTogether,
     documentThing: ArtTag,
 
-    save: (results) => ({artTagData: results}),
-
     sort({artTagData}) {
       sortAlphabetically(artTagData);
     },
diff --git a/src/data/things/artist.js b/src/data/things/artist.js
index 24c99698..a5601d60 100644
--- a/src/data/things/artist.js
+++ b/src/data/things/artist.js
@@ -41,7 +41,11 @@ import {artistTotalDuration} from '#composite/things/artist';
 
 export class Artist extends Thing {
   static [Thing.referenceType] = 'artist';
-  static [Thing.wikiDataArray] = 'artistData';
+  static [Thing.wikiData] = 'artistData';
+
+  static [Thing.constitutibleProperties] = [
+    'avatarArtwork', // from inline fields
+  ];
 
   static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
@@ -343,19 +347,6 @@ export class Artist extends Thing {
     documentMode: allInOne,
     documentThing: Artist,
 
-    save(results) {
-      const artists = results;
-      const artistAliases = artists.flatMap(artist => artist.artistAliases);
-      const artistData = [...artists, ...artistAliases];
-
-      const artworkData =
-        artistData
-          .filter(artist => artist.hasAvatar)
-          .map(artist => artist.avatarArtwork);
-
-      return {artistData, artworkData};
-    },
-
     sort({artistData}) {
       sortAlphabetically(artistData);
     },
diff --git a/src/data/things/artwork.js b/src/data/things/artwork.js
index 916aac0a..c1ae4f62 100644
--- a/src/data/things/artwork.js
+++ b/src/data/things/artwork.js
@@ -64,6 +64,12 @@ import {
 
 export class Artwork extends Thing {
   static [Thing.referenceType] = 'artwork';
+  static [Thing.wikiData] = 'artworkData';
+
+  static [Thing.constitutibleProperties] = [
+    // Contributions currently aren't being observed for constitution.
+    // 'artistContribs', // from attached artwork or thing
+  ];
 
   static [Thing.getPropertyDescriptors] = ({ArtTag}) => ({
     // Update & expose
diff --git a/src/data/things/content.js b/src/data/things/content.js
index a3dfc183..95836abd 100644
--- a/src/data/things/content.js
+++ b/src/data/things/content.js
@@ -154,6 +154,8 @@ export class ContentEntry extends Thing {
 }
 
 export class CommentaryEntry extends ContentEntry {
+  static [Thing.wikiData] = 'commentaryData';
+
   static [Thing.getPropertyDescriptors] = () => ({
     // Expose only
 
@@ -170,6 +172,8 @@ export class CommentaryEntry extends ContentEntry {
 }
 
 export class LyricsEntry extends ContentEntry {
+  static [Thing.wikiData] = 'lyricsData';
+
   static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
 
@@ -223,6 +227,8 @@ export class LyricsEntry extends ContentEntry {
 }
 
 export class CreditingSourcesEntry extends ContentEntry {
+  static [Thing.wikiData] = 'creditingSourceData';
+
   static [Thing.getPropertyDescriptors] = () => ({
     // Expose only
 
@@ -235,6 +241,8 @@ export class CreditingSourcesEntry extends ContentEntry {
 }
 
 export class ReferencingSourcesEntry extends ContentEntry {
+  static [Thing.wikiData] = 'referencingSourceData';
+
   static [Thing.getPropertyDescriptors] = () => ({
     // Expose only
 
diff --git a/src/data/things/flash.js b/src/data/things/flash.js
index 5c9023fa..19f6093e 100644
--- a/src/data/things/flash.js
+++ b/src/data/things/flash.js
@@ -46,6 +46,11 @@ import {
 
 export class Flash extends Thing {
   static [Thing.referenceType] = 'flash';
+  static [Thing.wikiData] = 'flashData';
+
+  static [Thing.constitutibleProperties] = [
+    'coverArtwork', // from inline fields
+  ];
 
   static [Thing.getPropertyDescriptors] = ({
     AdditionalName,
@@ -274,6 +279,7 @@ export class Flash extends Thing {
 export class FlashAct extends Thing {
   static [Thing.referenceType] = 'flash-act';
   static [Thing.friendlyName] = `Flash Act`;
+  static [Thing.wikiData] = 'flashActData';
 
   static [Thing.getPropertyDescriptors] = ({Flash, FlashSide}) => ({
     // Update & expose
@@ -355,6 +361,7 @@ export class FlashAct extends Thing {
 export class FlashSide extends Thing {
   static [Thing.referenceType] = 'flash-side';
   static [Thing.friendlyName] = `Flash Side`;
+  static [Thing.wikiData] = 'flashSideData';
 
   static [Thing.getPropertyDescriptors] = ({FlashAct}) => ({
     // Update & expose
@@ -421,15 +428,7 @@ export class FlashSide extends Thing {
         ? FlashAct
         : Flash),
 
-    save(results) {
-      const flashSideData = [];
-      const flashActData = [];
-      const flashData = [];
-
-      const artworkData = [];
-      const commentaryData = [];
-      const creditingSourceData = [];
-
+    connect(results) {
       let thing, i;
 
       for (i = 0; thing = results[i]; i++) {
@@ -449,11 +448,6 @@ export class FlashSide extends Thing {
                   flash.act = act;
                   flashes.push(flash);
 
-                  flashData.push(flash);
-                  artworkData.push(flash.coverArtwork);
-                  commentaryData.push(...flash.commentary);
-                  creditingSourceData.push(...flash.creditingSources);
-
                   continue;
                 }
 
@@ -465,8 +459,6 @@ export class FlashSide extends Thing {
               act.flashes = flashes;
               acts.push(act);
 
-              flashActData.push(act);
-
               continue;
             }
 
@@ -480,8 +472,6 @@ export class FlashSide extends Thing {
 
           side.acts = acts;
 
-          flashSideData.push(side);
-
           continue;
         }
 
@@ -493,16 +483,6 @@ export class FlashSide extends Thing {
           throw new Error(`Flashes must be under a side and act`);
         }
       }
-
-      return {
-        flashSideData,
-        flashActData,
-        flashData,
-
-        artworkData,
-        commentaryData,
-        creditingSourceData,
-      };
     },
 
     sort({flashData}) {
diff --git a/src/data/things/group.js b/src/data/things/group.js
index 0935dc93..ac051343 100644
--- a/src/data/things/group.js
+++ b/src/data/things/group.js
@@ -34,6 +34,7 @@ import {
 
 export class Group extends Thing {
   static [Thing.referenceType] = 'group';
+  static [Thing.wikiData] = 'groupData';
 
   static [Thing.getPropertyDescriptors] = ({Album, Artist, Series}) => ({
     // Update & expose
@@ -217,7 +218,7 @@ export class Group extends Thing {
         ? GroupCategory
         : Group),
 
-    save(results) {
+    connect(results) {
       let groupCategory;
       let groupRefs = [];
 
@@ -241,12 +242,6 @@ export class Group extends Thing {
       if (groupCategory) {
         Object.assign(groupCategory, {groups: groupRefs});
       }
-
-      const groupData = results.filter(x => x instanceof Group);
-      const groupCategoryData = results.filter(x => x instanceof GroupCategory);
-      const seriesData = groupData.flatMap(group => group.serieses);
-
-      return {groupData, groupCategoryData, seriesData};
     },
 
     // Groups aren't sorted at all, always preserving the order in the data
@@ -258,6 +253,7 @@ export class Group extends Thing {
 export class GroupCategory extends Thing {
   static [Thing.referenceType] = 'group-category';
   static [Thing.friendlyName] = `Group Category`;
+  static [Thing.wikiData] = 'groupCategoryData';
 
   static [Thing.getPropertyDescriptors] = ({Group}) => ({
     // Update & expose
@@ -310,6 +306,8 @@ export class GroupCategory extends Thing {
 }
 
 export class Series extends Thing {
+  static [Thing.wikiData] = 'seriesData';
+
   static [Thing.getPropertyDescriptors] = ({Album, Group}) => ({
     // Update & expose
 
diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js
index 7c97935e..5da13e37 100644
--- a/src/data/things/homepage-layout.js
+++ b/src/data/things/homepage-layout.js
@@ -32,6 +32,8 @@ import {
 
 export class HomepageLayout extends Thing {
   static [Thing.friendlyName] = `Homepage Layout`;
+  static [Thing.wikiData] = 'homepageLayout';
+  static [Thing.oneInstancePerWiki] = true;
 
   static [Thing.getPropertyDescriptors] = ({HomepageLayoutSection}) => ({
     // Update & expose
@@ -102,7 +104,7 @@ export class HomepageLayout extends Thing {
       return null;
     },
 
-    save(results) {
+    connect(results) {
       if (!empty(results) && !(results[0] instanceof HomepageLayout)) {
         throw new Error(`Expected 'Homepage' document at top of homepage layout file`);
       }
@@ -145,8 +147,6 @@ export class HomepageLayout extends Thing {
       closeCurrentSection();
 
       homepageLayout.sections = sections;
-
-      return {homepageLayout};
     },
   });
 }
diff --git a/src/data/things/news-entry.js b/src/data/things/news-entry.js
index 28289f53..e5467a46 100644
--- a/src/data/things/news-entry.js
+++ b/src/data/things/news-entry.js
@@ -12,6 +12,7 @@ import {contentString, directory, name, simpleDate}
 export class NewsEntry extends Thing {
   static [Thing.referenceType] = 'news-entry';
   static [Thing.friendlyName] = `News Entry`;
+  static [Thing.wikiData] = 'newsData';
 
   static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
@@ -72,8 +73,6 @@ export class NewsEntry extends Thing {
     documentMode: allInOne,
     documentThing: NewsEntry,
 
-    save: (results) => ({newsData: results}),
-
     sort({newsData}) {
       sortChronologically(newsData, {latestFirst: true});
     },
diff --git a/src/data/things/sorting-rule.js b/src/data/things/sorting-rule.js
index 8ed3861a..e113955f 100644
--- a/src/data/things/sorting-rule.js
+++ b/src/data/things/sorting-rule.js
@@ -38,6 +38,7 @@ function isSelectFollowingEntry(value) {
 
 export class SortingRule extends Thing {
   static [Thing.friendlyName] = `Sorting Rule`;
+  static [Thing.wikiData] = 'sortingRules';
 
   static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
@@ -77,8 +78,6 @@ export class SortingRule extends Thing {
       (document['Sort Documents']
         ? DocumentSortingRule
         : null),
-
-    save: (results) => ({sortingRules: results}),
   });
 
   check(opts) {
diff --git a/src/data/things/static-page.js b/src/data/things/static-page.js
index 28167df2..617bc940 100644
--- a/src/data/things/static-page.js
+++ b/src/data/things/static-page.js
@@ -15,6 +15,7 @@ import {contentString, directory, flag, name, simpleString}
 export class StaticPage extends Thing {
   static [Thing.referenceType] = 'static';
   static [Thing.friendlyName] = `Static Page`;
+  static [Thing.wikiData] = 'staticPageData';
 
   static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
@@ -86,8 +87,6 @@ export class StaticPage extends Thing {
     documentMode: onePerFile,
     documentThing: StaticPage,
 
-    save: (results) => ({staticPageData: results}),
-
     sort({staticPageData}) {
       sortAlphabetically(staticPageData);
     },
diff --git a/src/data/things/track.js b/src/data/things/track.js
index 0d565086..4a24a9e0 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -76,7 +76,6 @@ import {
   inheritContributionListFromMainRelease,
   inheritFromMainRelease,
   withAllReleases,
-  withContainingTrackSection,
   withCoverArtistContribs,
   withDate,
   withDirectorySuffix,
@@ -92,6 +91,16 @@ import {
 
 export class Track extends Thing {
   static [Thing.referenceType] = 'track';
+  static [Thing.wikiData] = 'trackData';
+
+  static [Thing.constitutibleProperties] = [
+    // Contributions currently aren't being observed for constitution.
+    // 'artistContribs', // from main release or album
+    // 'contributorContribs', // from main release
+    // 'coverArtistContribs', // from main release
+
+    'trackArtworks', // from inline fields
+  ];
 
   static [Thing.getPropertyDescriptors] = ({
     AdditionalFile,
@@ -103,6 +112,7 @@ export class Track extends Thing {
     CreditingSourcesEntry,
     LyricsEntry,
     ReferencingSourcesEntry,
+    TrackSection,
     WikiInfo,
   }) => ({
     // > Update & expose - Internal relationships
@@ -111,6 +121,10 @@ export class Track extends Thing {
       class: input.value(Album),
     }),
 
+    trackSection: thing({
+      class: input.value(TrackSection),
+    }),
+
     // > Update & expose - Identifying metadata
 
     name: name('Unnamed Track'),
@@ -263,10 +277,8 @@ export class Track extends Thing {
         validate: input.value(isBoolean),
       }),
 
-      withContainingTrackSection(),
-
       withPropertyFromObject({
-        object: '#trackSection',
+        object: 'trackSection',
         property: input.value('countTracksInArtistTotals'),
       }),
 
@@ -285,10 +297,8 @@ export class Track extends Thing {
         validate: input.value(isColor),
       }),
 
-      withContainingTrackSection(),
-
       withPropertyFromObject({
-        object: '#trackSection',
+        object: 'trackSection',
         property: input.value('color'),
       }),
 
@@ -510,6 +520,11 @@ export class Track extends Thing {
 
     commentatorArtists: commentatorArtists(),
 
+    directorySuffix: [
+      withDirectorySuffix(),
+      exposeDependency({dependency: '#directorySuffix'}),
+    ],
+
     date: [
       withDate(),
       exposeDependency({dependency: '#date'}),
diff --git a/src/data/things/wiki-info.js b/src/data/things/wiki-info.js
index 7fb6a350..89248d11 100644
--- a/src/data/things/wiki-info.js
+++ b/src/data/things/wiki-info.js
@@ -28,6 +28,8 @@ import {
 
 export class WikiInfo extends Thing {
   static [Thing.friendlyName] = `Wiki Info`;
+  static [Thing.wikiData] = 'wikiInfo';
+  static [Thing.oneInstancePerWiki] = true;
 
   static [Thing.getPropertyDescriptors] = ({Group}) => ({
     // Update & expose
@@ -168,13 +170,5 @@ export class WikiInfo extends Thing {
 
     documentMode: oneDocumentTotal,
     documentThing: WikiInfo,
-
-    save(wikiInfo) {
-      if (!wikiInfo) {
-        return;
-      }
-
-      return {wikiInfo};
-    },
   });
 }