« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.taprc3
-rw-r--r--coverage-map.js71
-rw-r--r--data-tests/index.js4
-rw-r--r--package-lock.json11357
-rw-r--r--package.json11
-rw-r--r--src/content/dependencies/generateAlbumInfoPage.js2
-rw-r--r--src/content/dependencies/generateAlbumStyleRules.js3
-rw-r--r--src/content/dependencies/generateAlbumTrackListItem.js20
-rw-r--r--src/content/dependencies/generateTrackInfoPage.js7
-rw-r--r--src/content/dependencies/generateTrackList.js59
-rw-r--r--src/content/dependencies/generateWikiHomeAlbumsRow.js2
-rw-r--r--src/content/dependencies/image.js1
-rw-r--r--src/content/dependencies/index.js15
-rw-r--r--src/data/composite/control-flow/exitWithoutDependency.js35
-rw-r--r--src/data/composite/control-flow/exitWithoutUpdateValue.js24
-rw-r--r--src/data/composite/control-flow/exposeConstant.js26
-rw-r--r--src/data/composite/control-flow/exposeDependency.js28
-rw-r--r--src/data/composite/control-flow/exposeDependencyOrContinue.js34
-rw-r--r--src/data/composite/control-flow/exposeUpdateValueOrContinue.js40
-rw-r--r--src/data/composite/control-flow/index.js9
-rw-r--r--src/data/composite/control-flow/inputAvailabilityCheckMode.js9
-rw-r--r--src/data/composite/control-flow/raiseOutputWithoutDependency.js39
-rw-r--r--src/data/composite/control-flow/raiseOutputWithoutUpdateValue.js47
-rw-r--r--src/data/composite/control-flow/withResultOfAvailabilityCheck.js66
-rw-r--r--src/data/composite/data/excludeFromList.js56
-rw-r--r--src/data/composite/data/fillMissingListItems.js51
-rw-r--r--src/data/composite/data/index.js8
-rw-r--r--src/data/composite/data/withFlattenedList.js47
-rw-r--r--src/data/composite/data/withPropertiesFromList.js92
-rw-r--r--src/data/composite/data/withPropertiesFromObject.js87
-rw-r--r--src/data/composite/data/withPropertyFromList.js56
-rw-r--r--src/data/composite/data/withPropertyFromObject.js69
-rw-r--r--src/data/composite/data/withUnflattenedList.js62
-rw-r--r--src/data/composite/things/album/index.js2
-rw-r--r--src/data/composite/things/album/withTrackSections.js128
-rw-r--r--src/data/composite/things/album/withTracks.js51
-rw-r--r--src/data/composite/things/track/exitWithoutUniqueCoverArt.js26
-rw-r--r--src/data/composite/things/track/index.js9
-rw-r--r--src/data/composite/things/track/inheritFromOriginalRelease.js43
-rw-r--r--src/data/composite/things/track/trackReverseReferenceList.js38
-rw-r--r--src/data/composite/things/track/withAlbum.js57
-rw-r--r--src/data/composite/things/track/withAlwaysReferenceByDirectory.js91
-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.js59
-rw-r--r--src/data/composite/things/track/withOtherReleases.js40
-rw-r--r--src/data/composite/things/track/withPropertyFromAlbum.js49
-rw-r--r--src/data/composite/wiki-data/exitWithoutContribs.js47
-rw-r--r--src/data/composite/wiki-data/index.js7
-rw-r--r--src/data/composite/wiki-data/inputThingClass.js23
-rw-r--r--src/data/composite/wiki-data/inputWikiData.js17
-rw-r--r--src/data/composite/wiki-data/withResolvedContribs.js77
-rw-r--r--src/data/composite/wiki-data/withResolvedReference.js73
-rw-r--r--src/data/composite/wiki-data/withResolvedReferenceList.js101
-rw-r--r--src/data/composite/wiki-data/withReverseReferenceList.js41
-rw-r--r--src/data/composite/wiki-properties/additionalFiles.js30
-rw-r--r--src/data/composite/wiki-properties/color.js12
-rw-r--r--src/data/composite/wiki-properties/commentary.js12
-rw-r--r--src/data/composite/wiki-properties/commentatorArtists.js55
-rw-r--r--src/data/composite/wiki-properties/contribsPresent.js30
-rw-r--r--src/data/composite/wiki-properties/contributionList.js35
-rw-r--r--src/data/composite/wiki-properties/dimensions.js13
-rw-r--r--src/data/composite/wiki-properties/directory.js23
-rw-r--r--src/data/composite/wiki-properties/duration.js13
-rw-r--r--src/data/composite/wiki-properties/externalFunction.js11
-rw-r--r--src/data/composite/wiki-properties/fileExtension.js13
-rw-r--r--src/data/composite/wiki-properties/flag.js19
-rw-r--r--src/data/composite/wiki-properties/index.js20
-rw-r--r--src/data/composite/wiki-properties/name.js11
-rw-r--r--src/data/composite/wiki-properties/referenceList.js47
-rw-r--r--src/data/composite/wiki-properties/reverseReferenceList.js30
-rw-r--r--src/data/composite/wiki-properties/simpleDate.js14
-rw-r--r--src/data/composite/wiki-properties/simpleString.js14
-rw-r--r--src/data/composite/wiki-properties/singleReference.js47
-rw-r--r--src/data/composite/wiki-properties/urls.js14
-rw-r--r--src/data/composite/wiki-properties/wikiData.js17
-rw-r--r--src/data/things/album.js278
-rw-r--r--src/data/things/art-tag.js47
-rw-r--r--src/data/things/artist.js95
-rw-r--r--src/data/things/cacheable-object.js65
-rw-r--r--src/data/things/composite.js1301
-rw-r--r--src/data/things/flash.js102
-rw-r--r--src/data/things/group.js72
-rw-r--r--src/data/things/homepage-layout.js109
-rw-r--r--src/data/things/index.js23
-rw-r--r--src/data/things/language.js25
-rw-r--r--src/data/things/news-entry.js15
-rw-r--r--src/data/things/static-page.js22
-rw-r--r--src/data/things/thing.js393
-rw-r--r--src/data/things/track.js745
-rw-r--r--src/data/things/validators.js89
-rw-r--r--src/data/things/wiki-info.js55
-rw-r--r--src/data/yaml.js209
-rw-r--r--src/find.js211
-rw-r--r--src/gen-thumbs.js15
-rw-r--r--src/listing-spec.js12
-rw-r--r--src/page/flash.js2
-rw-r--r--src/repl.js3
-rwxr-xr-xsrc/upd8.js24
-rw-r--r--src/util/cli.js8
-rw-r--r--src/util/html.js6
-rw-r--r--src/util/replacer.js5
-rw-r--r--src/util/sugar.js97
-rw-r--r--src/util/urls.js21
-rw-r--r--src/util/wiki-data.js70
-rw-r--r--src/write/build-modes/live-dev-server.js30
-rw-r--r--src/write/build-modes/static-build.js15
-rw-r--r--tap-snapshots/test/snapshot/generateAdditionalFilesList.js.test.cjs4
-rw-r--r--tap-snapshots/test/snapshot/generateAdditionalFilesShortcut.js.test.cjs4
-rw-r--r--tap-snapshots/test/snapshot/generateAlbumBanner.js.test.cjs6
-rw-r--r--tap-snapshots/test/snapshot/generateAlbumCoverArtwork.js.test.cjs4
-rw-r--r--tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs14
-rw-r--r--tap-snapshots/test/snapshot/generateAlbumSecondaryNav.js.test.cjs6
-rw-r--r--tap-snapshots/test/snapshot/generateAlbumSidebarGroupBox.js.test.cjs6
-rw-r--r--tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs4
-rw-r--r--tap-snapshots/test/snapshot/generateBanner.js.test.cjs4
-rw-r--r--tap-snapshots/test/snapshot/generateCoverArtwork.js.test.cjs4
-rw-r--r--tap-snapshots/test/snapshot/generatePreviousNextLinks.js.test.cjs10
-rw-r--r--tap-snapshots/test/snapshot/generateTrackCoverArtwork.js.test.cjs8
-rw-r--r--tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs8
-rw-r--r--tap-snapshots/test/snapshot/image.js.test.cjs30
-rw-r--r--tap-snapshots/test/snapshot/linkArtist.js.test.cjs4
-rw-r--r--tap-snapshots/test/snapshot/linkContribution.js.test.cjs12
-rw-r--r--tap-snapshots/test/snapshot/linkExternal.js.test.cjs10
-rw-r--r--tap-snapshots/test/snapshot/linkExternalFlash.js.test.cjs4
-rw-r--r--tap-snapshots/test/snapshot/linkTemplate.js.test.cjs10
-rw-r--r--tap-snapshots/test/snapshot/linkThing.js.test.cjs12
-rw-r--r--tap-snapshots/test/snapshot/transformContent.js.test.cjs22
-rw-r--r--test/lib/index.js3
-rw-r--r--test/lib/wiki-data.js24
-rw-r--r--test/unit/data/cacheable-object.js (renamed from test/unit/data/things/cacheable-object.js)0
-rw-r--r--test/unit/data/composite/control-flow/exposeConstant.js42
-rw-r--r--test/unit/data/composite/control-flow/exposeDependency.js64
-rw-r--r--test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js173
-rw-r--r--test/unit/data/composite/data/withPropertiesFromObject.js248
-rw-r--r--test/unit/data/composite/data/withPropertyFromObject.js122
-rw-r--r--test/unit/data/compositeFrom.js345
-rw-r--r--test/unit/data/templateCompositeFrom.js209
-rw-r--r--test/unit/data/things/album.js411
-rw-r--r--test/unit/data/things/art-tag.js71
-rw-r--r--test/unit/data/things/track.js589
142 files changed, 13911 insertions, 6767 deletions
diff --git a/.gitignore b/.gitignore
index 6f9fe6ba..a9dda76a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-node_modules
+/node_modules
+/.tap
 .DS_Store
-.nyc_output
diff --git a/.taprc b/.taprc
new file mode 100644
index 00000000..44e24f55
--- /dev/null
+++ b/.taprc
@@ -0,0 +1,3 @@
+coverage-map: coverage-map.js
+exclude:
+  - test/lib/*
diff --git a/coverage-map.js b/coverage-map.js
new file mode 100644
index 00000000..beff9e8a
--- /dev/null
+++ b/coverage-map.js
@@ -0,0 +1,71 @@
+// node-tap test -> src coverage map
+// https://node-tap.org/coverage/
+
+export default function map(F) {
+  let match;
+
+  // unit/content/...
+
+  match = F.match(/^test\/unit\/content\/(.*)$/);
+  if (match) {
+    const f = match[1];
+
+    match = f.match(/^dependencies\/(.*)\.js$/);
+    if (match) {
+      return `src/content/dependencies/${match[1]}.js`;
+    }
+  }
+
+  // unit/data/...
+
+  match = F.match(/^test\/unit\/data\/(.*)$/);
+  if (match) {
+    const f = match[1];
+
+    match = f.match(/^composite\/(.*)$/);
+    if (match) {
+      return `src/data/composite/${match[1]}`;
+    }
+
+    match = f.match(/^things\/(.*)\.js$/);
+    if (match) {
+      return `src/data/things/${match[1]}.js`;
+    }
+
+    match = f.match(/^cacheable-object\.js$/);
+    if (match) {
+      return `src/data/things/cacheable-object.js`;
+    }
+
+    match = f.match(/^(templateCompositeFrom|compositeFrom)\.js$/);
+    if (match) {
+      return `src/data/things/composite.js`;
+    }
+  }
+
+  // unit/util/...
+
+  match = F.match(/^test\/unit\/util\/(.*)$/);
+  if (match) {
+    const f = match[1];
+
+    switch (f) {
+      case 'html.js':
+        return 'src/util/html.js';
+    }
+  }
+
+  // snapshot/...
+
+  match = F.match(/^test\/snapshot\/(.*)$/);
+  if (match) {
+    const f = match[1];
+
+    match = f.match(/^(.*)\.js$/);
+    if (match) {
+      return `src/content/dependencies/${match[1]}.js`;
+    }
+  }
+
+  return null;
+}
diff --git a/data-tests/index.js b/data-tests/index.js
index b05de9ed..d0770907 100644
--- a/data-tests/index.js
+++ b/data-tests/index.js
@@ -3,7 +3,7 @@ import {fileURLToPath} from 'node:url';
 
 import chokidar from 'chokidar';
 
-import {color, logError, logInfo, logWarn, parseOptions} from '#cli';
+import {colors, logError, logInfo, logWarn, parseOptions} from '#cli';
 import {isMain} from '#node-utils';
 import {getContextAssignments} from '#repl';
 import {bindOpts, showAggregate} from '#sugar';
@@ -24,7 +24,7 @@ async function main() {
   }
 
   console.log(`HSMusic automated data tests`);
-  console.log(`${color.bright(color.yellow(`:star:`))} Now featuring quick-reloading! ${color.bright(color.cyan(`:earth:`))}`);
+  console.log(`${colors.bright(colors.yellow(`:star:`))} Now featuring quick-reloading! ${colors.bright(colors.cyan(`:earth:`))}`);
 
   // Watch adjacent files in data-tests directory
   const metaPath = fileURLToPath(import.meta.url);
diff --git a/package-lock.json b/package-lock.json
index edd3ad32..6433ea16 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,624 +24,1349 @@
             },
             "devDependencies": {
                 "chokidar": "^3.5.3",
-                "tap": "^16.3.4",
+                "tap": "^18.4.0",
                 "tcompare": "^6.0.0"
             }
         },
-        "node_modules/@ampproject/remapping": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
-            "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+        "node_modules/@alcalzone/ansi-tokenize": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.1.3.tgz",
+            "integrity": "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==",
             "dev": true,
             "dependencies": {
-                "@jridgewell/gen-mapping": "^0.1.0",
-                "@jridgewell/trace-mapping": "^0.3.9"
+                "ansi-styles": "^6.2.1",
+                "is-fullwidth-code-point": "^4.0.0"
             },
             "engines": {
-                "node": ">=6.0.0"
+                "node": ">=14.13.1"
             }
         },
-        "node_modules/@babel/code-frame": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
-            "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+        "node_modules/@alcalzone/ansi-tokenize/node_modules/ansi-styles": {
+            "version": "6.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+            "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
             "dev": true,
+            "engines": {
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+            }
+        },
+        "node_modules/@base2/pretty-print-object": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz",
+            "integrity": "sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==",
+            "dev": true
+        },
+        "node_modules/@bcoe/v8-coverage": {
+            "version": "0.2.3",
+            "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+            "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+            "dev": true
+        },
+        "node_modules/@cspotcode/source-map-support": {
+            "version": "0.8.1",
+            "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+            "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+            "dev": true,
+            "dependencies": {
+                "@jridgewell/trace-mapping": "0.3.9"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/@eslint-community/eslint-utils": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+            "dependencies": {
+                "eslint-visitor-keys": "^3.3.0"
+            },
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "peerDependencies": {
+                "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+            }
+        },
+        "node_modules/@eslint-community/regexpp": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
+            "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==",
+            "engines": {
+                "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+            }
+        },
+        "node_modules/@eslint/eslintrc": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
+            "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
             "dependencies": {
-                "@babel/highlight": "^7.18.6"
+                "ajv": "^6.12.4",
+                "debug": "^4.3.2",
+                "espree": "^9.5.1",
+                "globals": "^13.19.0",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.2.1",
+                "js-yaml": "^4.1.0",
+                "minimatch": "^3.1.2",
+                "strip-json-comments": "^3.1.1"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/eslint"
             }
         },
-        "node_modules/@babel/compat-data": {
-            "version": "7.21.0",
-            "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz",
-            "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==",
+        "node_modules/@eslint/js": {
+            "version": "8.37.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz",
+            "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==",
+            "engines": {
+                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+            }
+        },
+        "node_modules/@humanwhocodes/config-array": {
+            "version": "0.11.8",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+            "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+            "dependencies": {
+                "@humanwhocodes/object-schema": "^1.2.1",
+                "debug": "^4.1.1",
+                "minimatch": "^3.0.5"
+            },
+            "engines": {
+                "node": ">=10.10.0"
+            }
+        },
+        "node_modules/@humanwhocodes/module-importer": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+            "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+            "engines": {
+                "node": ">=12.22"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/nzakas"
+            }
+        },
+        "node_modules/@humanwhocodes/object-schema": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
+        },
+        "node_modules/@isaacs/cliui": {
+            "version": "8.0.2",
+            "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+            "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
             "dev": true,
+            "dependencies": {
+                "string-width": "^5.1.2",
+                "string-width-cjs": "npm:string-width@^4.2.0",
+                "strip-ansi": "^7.0.1",
+                "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+                "wrap-ansi": "^8.1.0",
+                "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+            },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=12"
             }
         },
-        "node_modules/@babel/core": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz",
-            "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==",
+        "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+            "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+            "dev": true,
+            "engines": {
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+            }
+        },
+        "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+            "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
             "dev": true,
             "dependencies": {
-                "@ampproject/remapping": "^2.2.0",
-                "@babel/code-frame": "^7.18.6",
-                "@babel/generator": "^7.21.3",
-                "@babel/helper-compilation-targets": "^7.20.7",
-                "@babel/helper-module-transforms": "^7.21.2",
-                "@babel/helpers": "^7.21.0",
-                "@babel/parser": "^7.21.3",
-                "@babel/template": "^7.20.7",
-                "@babel/traverse": "^7.21.3",
-                "@babel/types": "^7.21.3",
-                "convert-source-map": "^1.7.0",
-                "debug": "^4.1.0",
-                "gensync": "^1.0.0-beta.2",
-                "json5": "^2.2.2",
-                "semver": "^6.3.0"
+                "ansi-regex": "^6.0.1"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=12"
             },
             "funding": {
-                "type": "opencollective",
-                "url": "https://opencollective.com/babel"
+                "url": "https://github.com/chalk/strip-ansi?sponsor=1"
             }
         },
-        "node_modules/@babel/generator": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz",
-            "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==",
+        "node_modules/@istanbuljs/schema": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+            "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/@jridgewell/resolve-uri": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+            "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+            "dev": true,
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/@jridgewell/sourcemap-codec": {
+            "version": "1.4.15",
+            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+            "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+            "dev": true
+        },
+        "node_modules/@jridgewell/trace-mapping": {
+            "version": "0.3.9",
+            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+            "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
             "dev": true,
             "dependencies": {
-                "@babel/types": "^7.21.3",
-                "@jridgewell/gen-mapping": "^0.3.2",
-                "@jridgewell/trace-mapping": "^0.3.17",
-                "jsesc": "^2.5.1"
+                "@jridgewell/resolve-uri": "^3.0.3",
+                "@jridgewell/sourcemap-codec": "^1.4.10"
+            }
+        },
+        "node_modules/@nodelib/fs.scandir": {
+            "version": "2.1.5",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+            "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+            "dependencies": {
+                "@nodelib/fs.stat": "2.0.5",
+                "run-parallel": "^1.1.9"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@nodelib/fs.stat": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+            "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+            "engines": {
+                "node": ">= 8"
             }
         },
-        "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
-            "version": "0.3.2",
-            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
-            "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+        "node_modules/@nodelib/fs.walk": {
+            "version": "1.2.8",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+            "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+            "dependencies": {
+                "@nodelib/fs.scandir": "2.1.5",
+                "fastq": "^1.6.0"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@npmcli/agent": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.1.1.tgz",
+            "integrity": "sha512-6RlbiOAi6L6uUYF4/CDEkDZQnKw0XDsFJVrEpnib8rAx2WRMOsUyAdgnvDpX/fdkDWxtqE+NHwF465llI2wR0g==",
             "dev": true,
             "dependencies": {
-                "@jridgewell/set-array": "^1.0.1",
-                "@jridgewell/sourcemap-codec": "^1.4.10",
-                "@jridgewell/trace-mapping": "^0.3.9"
+                "http-proxy-agent": "^7.0.0",
+                "https-proxy-agent": "^7.0.1",
+                "lru-cache": "^10.0.1",
+                "socks-proxy-agent": "^8.0.1"
             },
             "engines": {
-                "node": ">=6.0.0"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/helper-compilation-targets": {
-            "version": "7.20.7",
-            "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
-            "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
+        "node_modules/@npmcli/agent/node_modules/agent-base": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
+            "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
             "dev": true,
             "dependencies": {
-                "@babel/compat-data": "^7.20.5",
-                "@babel/helper-validator-option": "^7.18.6",
-                "browserslist": "^4.21.3",
-                "lru-cache": "^5.1.1",
-                "semver": "^6.3.0"
+                "debug": "^4.3.4"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">= 14"
+            }
+        },
+        "node_modules/@npmcli/agent/node_modules/http-proxy-agent": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz",
+            "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==",
+            "dev": true,
+            "dependencies": {
+                "agent-base": "^7.1.0",
+                "debug": "^4.3.4"
             },
-            "peerDependencies": {
-                "@babel/core": "^7.0.0"
+            "engines": {
+                "node": ">= 14"
             }
         },
-        "node_modules/@babel/helper-environment-visitor": {
-            "version": "7.18.9",
-            "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
-            "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+        "node_modules/@npmcli/agent/node_modules/https-proxy-agent": {
+            "version": "7.0.2",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
+            "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
             "dev": true,
+            "dependencies": {
+                "agent-base": "^7.0.2",
+                "debug": "4"
+            },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">= 14"
             }
         },
-        "node_modules/@babel/helper-function-name": {
-            "version": "7.21.0",
-            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
-            "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+        "node_modules/@npmcli/agent/node_modules/socks-proxy-agent": {
+            "version": "8.0.2",
+            "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz",
+            "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==",
             "dev": true,
             "dependencies": {
-                "@babel/template": "^7.20.7",
-                "@babel/types": "^7.21.0"
+                "agent-base": "^7.0.2",
+                "debug": "^4.3.4",
+                "socks": "^2.7.1"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">= 14"
             }
         },
-        "node_modules/@babel/helper-hoist-variables": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
-            "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+        "node_modules/@npmcli/fs": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz",
+            "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==",
             "dev": true,
             "dependencies": {
-                "@babel/types": "^7.18.6"
+                "semver": "^7.3.5"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/helper-module-imports": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
-            "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+        "node_modules/@npmcli/git": {
+            "version": "5.0.3",
+            "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.3.tgz",
+            "integrity": "sha512-UZp9NwK+AynTrKvHn5k3KviW/hA5eENmFsu3iAPe7sWRt0lFUdsY/wXIYjpDFe7cdSNwOIzbObfwgt6eL5/2zw==",
             "dev": true,
             "dependencies": {
-                "@babel/types": "^7.18.6"
+                "@npmcli/promise-spawn": "^7.0.0",
+                "lru-cache": "^10.0.1",
+                "npm-pick-manifest": "^9.0.0",
+                "proc-log": "^3.0.0",
+                "promise-inflight": "^1.0.1",
+                "promise-retry": "^2.0.1",
+                "semver": "^7.3.5",
+                "which": "^4.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/helper-module-transforms": {
-            "version": "7.21.2",
-            "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz",
-            "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==",
+        "node_modules/@npmcli/git/node_modules/isexe": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+            "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=16"
+            }
+        },
+        "node_modules/@npmcli/git/node_modules/which": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+            "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
             "dev": true,
             "dependencies": {
-                "@babel/helper-environment-visitor": "^7.18.9",
-                "@babel/helper-module-imports": "^7.18.6",
-                "@babel/helper-simple-access": "^7.20.2",
-                "@babel/helper-split-export-declaration": "^7.18.6",
-                "@babel/helper-validator-identifier": "^7.19.1",
-                "@babel/template": "^7.20.7",
-                "@babel/traverse": "^7.21.2",
-                "@babel/types": "^7.21.2"
+                "isexe": "^3.1.1"
+            },
+            "bin": {
+                "node-which": "bin/which.js"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/helper-simple-access": {
-            "version": "7.20.2",
-            "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
-            "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+        "node_modules/@npmcli/installed-package-contents": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz",
+            "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==",
             "dev": true,
             "dependencies": {
-                "@babel/types": "^7.20.2"
+                "npm-bundled": "^3.0.0",
+                "npm-normalize-package-bin": "^3.0.0"
+            },
+            "bin": {
+                "installed-package-contents": "lib/index.js"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+            }
+        },
+        "node_modules/@npmcli/node-gyp": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz",
+            "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==",
+            "dev": true,
+            "engines": {
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/helper-split-export-declaration": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
-            "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+        "node_modules/@npmcli/promise-spawn": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.0.tgz",
+            "integrity": "sha512-wBqcGsMELZna0jDblGd7UXgOby45TQaMWmbFwWX+SEotk4HV6zG2t6rT9siyLhPk4P6YYqgfL1UO8nMWDBVJXQ==",
             "dev": true,
             "dependencies": {
-                "@babel/types": "^7.18.6"
+                "which": "^4.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/helper-string-parser": {
-            "version": "7.19.4",
-            "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
-            "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+        "node_modules/@npmcli/promise-spawn/node_modules/isexe": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+            "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
             "dev": true,
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16"
             }
         },
-        "node_modules/@babel/helper-validator-identifier": {
-            "version": "7.19.1",
-            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
-            "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+        "node_modules/@npmcli/promise-spawn/node_modules/which": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+            "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
             "dev": true,
+            "dependencies": {
+                "isexe": "^3.1.1"
+            },
+            "bin": {
+                "node-which": "bin/which.js"
+            },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/helper-validator-option": {
-            "version": "7.21.0",
-            "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
-            "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
+        "node_modules/@npmcli/run-script": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.1.tgz",
+            "integrity": "sha512-Od/JMrgkjZ8alyBE0IzeqZDiF1jgMez9Gkc/OYrCkHHiXNwM0wc6s7+h+xM7kYDZkS0tAoOLr9VvygyE5+2F7g==",
+            "dev": true,
+            "dependencies": {
+                "@npmcli/node-gyp": "^3.0.0",
+                "@npmcli/promise-spawn": "^7.0.0",
+                "node-gyp": "^9.0.0",
+                "read-package-json-fast": "^3.0.0",
+                "which": "^4.0.0"
+            },
+            "engines": {
+                "node": "^16.14.0 || >=18.0.0"
+            }
+        },
+        "node_modules/@npmcli/run-script/node_modules/isexe": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+            "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
             "dev": true,
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16"
             }
         },
-        "node_modules/@babel/helpers": {
-            "version": "7.21.0",
-            "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz",
-            "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==",
+        "node_modules/@npmcli/run-script/node_modules/which": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+            "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
             "dev": true,
             "dependencies": {
-                "@babel/template": "^7.20.7",
-                "@babel/traverse": "^7.21.0",
-                "@babel/types": "^7.21.0"
+                "isexe": "^3.1.1"
+            },
+            "bin": {
+                "node-which": "bin/which.js"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^16.13.0 || >=18.0.0"
+            }
+        },
+        "node_modules/@pkgjs/parseargs": {
+            "version": "0.11.0",
+            "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+            "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+            "dev": true,
+            "optional": true,
+            "engines": {
+                "node": ">=14"
             }
         },
-        "node_modules/@babel/highlight": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
-            "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+        "node_modules/@sigstore/bundle": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.1.0.tgz",
+            "integrity": "sha512-89uOo6yh/oxaU8AeOUnVrTdVMcGk9Q1hJa7Hkvalc6G3Z3CupWk4Xe9djSgJm9fMkH69s0P0cVHUoKSOemLdng==",
             "dev": true,
             "dependencies": {
-                "@babel/helper-validator-identifier": "^7.18.6",
-                "chalk": "^2.0.0",
-                "js-tokens": "^4.0.0"
+                "@sigstore/protobuf-specs": "^0.2.1"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^16.14.0 || >=18.0.0"
+            }
+        },
+        "node_modules/@sigstore/protobuf-specs": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz",
+            "integrity": "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==",
+            "dev": true,
+            "engines": {
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/highlight/node_modules/ansi-styles": {
-            "version": "3.2.1",
-            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-            "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+        "node_modules/@sigstore/sign": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.1.0.tgz",
+            "integrity": "sha512-4VRpfJxs+8eLqzLVrZngVNExVA/zAhVbi4UT4zmtLi4xRd7vz5qie834OgkrGsLlLB1B2nz/3wUxT1XAUBe8gw==",
             "dev": true,
             "dependencies": {
-                "color-convert": "^1.9.0"
+                "@sigstore/bundle": "^2.1.0",
+                "@sigstore/protobuf-specs": "^0.2.1",
+                "make-fetch-happen": "^13.0.0"
             },
             "engines": {
-                "node": ">=4"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/highlight/node_modules/chalk": {
-            "version": "2.4.2",
-            "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-            "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+        "node_modules/@sigstore/sign/node_modules/make-fetch-happen": {
+            "version": "13.0.0",
+            "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz",
+            "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==",
             "dev": true,
             "dependencies": {
-                "ansi-styles": "^3.2.1",
-                "escape-string-regexp": "^1.0.5",
-                "supports-color": "^5.3.0"
+                "@npmcli/agent": "^2.0.0",
+                "cacache": "^18.0.0",
+                "http-cache-semantics": "^4.1.1",
+                "is-lambda": "^1.0.1",
+                "minipass": "^7.0.2",
+                "minipass-fetch": "^3.0.0",
+                "minipass-flush": "^1.0.5",
+                "minipass-pipeline": "^1.2.4",
+                "negotiator": "^0.6.3",
+                "promise-retry": "^2.0.1",
+                "ssri": "^10.0.0"
             },
             "engines": {
-                "node": ">=4"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/highlight/node_modules/color-convert": {
-            "version": "1.9.3",
-            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-            "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+        "node_modules/@sigstore/tuf": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.2.0.tgz",
+            "integrity": "sha512-KKATZ5orWfqd9ZG6MN8PtCIx4eevWSuGRKQvofnWXRpyMyUEpmrzg5M5BrCpjM+NfZ0RbNGOh5tCz/P2uoRqOA==",
             "dev": true,
             "dependencies": {
-                "color-name": "1.1.3"
+                "@sigstore/protobuf-specs": "^0.2.1",
+                "tuf-js": "^2.1.0"
+            },
+            "engines": {
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/@babel/highlight/node_modules/color-name": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-            "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-            "dev": true
+        "node_modules/@tapjs/after": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/after/-/after-1.1.4.tgz",
+            "integrity": "sha512-TVjrOwpPZt/VfdYc+X4gF/TY06gDHfzP9lfSv7hcxSaUGtvlU0xLH1xsTZS1BKM+EX1qXrCA8RYaLblAniKmaQ==",
+            "dev": true,
+            "dependencies": {
+                "is-actual-promise": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
+            }
         },
-        "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-            "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+        "node_modules/@tapjs/after-each": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/after-each/-/after-each-1.1.4.tgz",
+            "integrity": "sha512-vcmPQi2wXi2obK2j1nXTDo6EV8uqXONGiaPAPsj+iELr7OB3vBR1FFOQ6GWAFw0Xh8EIIUs8CWyNHn40/kmyUg==",
             "dev": true,
+            "dependencies": {
+                "function-loop": "^4.0.0"
+            },
             "engines": {
-                "node": ">=0.8.0"
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@babel/highlight/node_modules/has-flag": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-            "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+        "node_modules/@tapjs/asserts": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/asserts/-/asserts-1.1.4.tgz",
+            "integrity": "sha512-5jhbvqJ88agvGEW27l/ucNK7WqQAsCCt6gTBJKdVIL8jOZz5jOVaN/UI6gqUHLO7SYxIl4SOh8N11OYizRSKfA==",
             "dev": true,
+            "dependencies": {
+                "is-actual-promise": "^1.0.0",
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
+            },
             "engines": {
-                "node": ">=4"
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@babel/highlight/node_modules/supports-color": {
-            "version": "5.5.0",
-            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-            "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+        "node_modules/@tapjs/before": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/before/-/before-1.1.4.tgz",
+            "integrity": "sha512-JnCg39toYCBMZKECL6dqXkpi5p9efxvug/vqMoW7XDpYSJRnRz25EUvTPFd1IE6SwVpJF2xRFL7EKUnxLN3JiQ==",
             "dev": true,
             "dependencies": {
-                "has-flag": "^3.0.0"
+                "is-actual-promise": "^1.0.0"
             },
             "engines": {
-                "node": ">=4"
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@babel/parser": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz",
-            "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==",
+        "node_modules/@tapjs/before-each": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/before-each/-/before-each-1.1.4.tgz",
+            "integrity": "sha512-DnwLTOmeifh571kvL3Ef94Ui0OpGzM/oIbjOaL9onHnLTR+cOO8yZALJp6zVg/pq/OzScDY3DQuazunolEVCQQ==",
             "dev": true,
-            "bin": {
-                "parser": "bin/babel-parser.js"
+            "dependencies": {
+                "function-loop": "^4.0.0"
             },
             "engines": {
-                "node": ">=6.0.0"
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@babel/template": {
-            "version": "7.20.7",
-            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
-            "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+        "node_modules/@tapjs/config": {
+            "version": "2.4.0",
+            "resolved": "https://registry.npmjs.org/@tapjs/config/-/config-2.4.0.tgz",
+            "integrity": "sha512-iz8n4GFY8FM1kKro4W6kZ3mQvzjddL4j8ta1B08q9ix8K5ysfHnbamjh2syORVRGo/dZNMnKvfXTxFzZ+WIbDg==",
             "dev": true,
             "dependencies": {
-                "@babel/code-frame": "^7.18.6",
-                "@babel/parser": "^7.20.7",
-                "@babel/types": "^7.20.7"
+                "chalk": "^5.2.0",
+                "jackspeak": "^2.3.6",
+                "polite-json": "^4.0.1",
+                "walk-up-path": "^3.0.1"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4",
+                "@tapjs/test": "1.3.4"
             }
         },
-        "node_modules/@babel/traverse": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz",
-            "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==",
+        "node_modules/@tapjs/config/node_modules/chalk": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+            "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+            "dev": true,
+            "engines": {
+                "node": "^12.17.0 || ^14.13 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
+            }
+        },
+        "node_modules/@tapjs/core": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/core/-/core-1.3.4.tgz",
+            "integrity": "sha512-EcINYx86gDzLeZAsHMckv4Fjd4TdYJ7KduvdhD0Qy4EhROjQnaY9lPQTQxT2uwaEjpWB2Pio3ahtLzNUT2lY1g==",
             "dev": true,
             "dependencies": {
-                "@babel/code-frame": "^7.18.6",
-                "@babel/generator": "^7.21.3",
-                "@babel/helper-environment-visitor": "^7.18.9",
-                "@babel/helper-function-name": "^7.21.0",
-                "@babel/helper-hoist-variables": "^7.18.6",
-                "@babel/helper-split-export-declaration": "^7.18.6",
-                "@babel/parser": "^7.21.3",
-                "@babel/types": "^7.21.3",
-                "debug": "^4.1.0",
-                "globals": "^11.1.0"
+                "@tapjs/processinfo": "^3.1.2",
+                "@tapjs/stack": "1.2.3",
+                "@tapjs/test": "1.3.4",
+                "async-hook-domain": "^4.0.1",
+                "is-actual-promise": "^1.0.0",
+                "jackspeak": "^2.3.6",
+                "minipass": "^7.0.3",
+                "signal-exit": "4.1",
+                "tap-parser": "15.2.0",
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16"
             }
         },
-        "node_modules/@babel/traverse/node_modules/globals": {
-            "version": "11.12.0",
-            "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
-            "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+        "node_modules/@tapjs/error-serdes": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/@tapjs/error-serdes/-/error-serdes-1.1.0.tgz",
+            "integrity": "sha512-RAdsafCQ9fyudLY4EQPhfWQvRNddvSoXKEsZQWZC6G5QfdB/BYnSqaXggK5TD0XZ79Ja0ex3uB+5kBaaeLKtQA==",
             "dev": true,
+            "dependencies": {
+                "minipass": "^7.0.3"
+            },
             "engines": {
-                "node": ">=4"
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@babel/types": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz",
-            "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==",
+        "node_modules/@tapjs/filter": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/filter/-/filter-1.2.4.tgz",
+            "integrity": "sha512-YHIjat67MuuO2SzSg2Hcwwm1Y1UJ1yvD20hyy6MYGrKG8vkaU1hSu4bBheRhJ2IyqJQVgSIM+raNctlN5Bpa/A==",
             "dev": true,
             "dependencies": {
-                "@babel/helper-string-parser": "^7.19.4",
-                "@babel/helper-validator-identifier": "^7.19.1",
-                "to-fast-properties": "^2.0.0"
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@eslint-community/eslint-utils": {
-            "version": "4.4.0",
-            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
-            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+        "node_modules/@tapjs/fixture": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/fixture/-/fixture-1.2.4.tgz",
+            "integrity": "sha512-7PHkg7fbKRWThU017qkw92dovreQct3LCArUJ9OdZWFoPYRwYND7CKB3/x7qtnNftBFZbRzf562miH0+TLDDTQ==",
+            "dev": true,
             "dependencies": {
-                "eslint-visitor-keys": "^3.3.0"
+                "mkdirp": "^3.0.0",
+                "rimraf": "^5.0.5"
             },
             "engines": {
-                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             },
             "peerDependencies": {
-                "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@eslint-community/regexpp": {
-            "version": "4.5.0",
-            "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
-            "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==",
+        "node_modules/@tapjs/fixture/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+            "dev": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0"
+            }
+        },
+        "node_modules/@tapjs/fixture/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+            "dev": true,
+            "dependencies": {
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
+            },
+            "bin": {
+                "glob": "dist/esm/bin.mjs"
+            },
             "engines": {
-                "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@eslint/eslintrc": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
-            "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+        "node_modules/@tapjs/fixture/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+            "dev": true,
             "dependencies": {
-                "ajv": "^6.12.4",
-                "debug": "^4.3.2",
-                "espree": "^9.5.1",
-                "globals": "^13.19.0",
-                "ignore": "^5.2.0",
-                "import-fresh": "^3.2.1",
-                "js-yaml": "^4.1.0",
-                "minimatch": "^3.1.2",
-                "strip-json-comments": "^3.1.1"
+                "brace-expansion": "^2.0.1"
             },
             "engines": {
-                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+                "node": ">=16 || 14 >=14.17"
             },
             "funding": {
-                "url": "https://opencollective.com/eslint"
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@eslint/js": {
-            "version": "8.37.0",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz",
-            "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==",
+        "node_modules/@tapjs/fixture/node_modules/rimraf": {
+            "version": "5.0.5",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+            "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
+            "dev": true,
+            "dependencies": {
+                "glob": "^10.3.7"
+            },
+            "bin": {
+                "rimraf": "dist/esm/bin.mjs"
+            },
             "engines": {
-                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+                "node": ">=14"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@humanwhocodes/config-array": {
-            "version": "0.11.8",
-            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
-            "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+        "node_modules/@tapjs/intercept": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/intercept/-/intercept-1.2.4.tgz",
+            "integrity": "sha512-aEPwa40DqJPmgnZRbED+hI1x3dSUn4o5rePW6I2ludRle3o1bHSSnucYsjhwNPz0LCpOH9q/UAivJPO66xyTBA==",
+            "dev": true,
             "dependencies": {
-                "@humanwhocodes/object-schema": "^1.2.1",
-                "debug": "^4.1.1",
-                "minimatch": "^3.0.5"
+                "@tapjs/after": "1.1.4",
+                "@tapjs/stack": "1.2.3"
             },
             "engines": {
-                "node": ">=10.10.0"
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@humanwhocodes/module-importer": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
-            "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+        "node_modules/@tapjs/mock": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/@tapjs/mock/-/mock-1.2.2.tgz",
+            "integrity": "sha512-5SgMRNaHgxjuna5YfVrT/l9bCTV4qePbqxNhwLWiL/l4fHMcF8CB7jMQ2IXsB8/0q9dKSuuxysOeiYSScNQcsA==",
+            "dev": true,
+            "dependencies": {
+                "@tapjs/after": "1.1.4",
+                "@tapjs/stack": "1.2.3",
+                "resolve-import": "^1.4.2",
+                "walk-up-path": "^3.0.1"
+            },
             "engines": {
-                "node": ">=12.22"
+                "node": ">=16"
             },
             "funding": {
-                "type": "github",
-                "url": "https://github.com/sponsors/nzakas"
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@humanwhocodes/object-schema": {
-            "version": "1.2.1",
-            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
-            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
+        "node_modules/@tapjs/node-serialize": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/node-serialize/-/node-serialize-1.1.4.tgz",
+            "integrity": "sha512-t0x4jC15jae4DviixIqb0v53eXkWdE3KkmKcf/eMGCqN7EL3lRyQRTOtjC3fJRWmdXYCGK/311DpoUfpgzL3sA==",
+            "dev": true,
+            "dependencies": {
+                "@tapjs/error-serdes": "1.1.0"
+            },
+            "engines": {
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
+            }
         },
-        "node_modules/@istanbuljs/load-nyc-config": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
-            "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+        "node_modules/@tapjs/processinfo": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/@tapjs/processinfo/-/processinfo-3.1.2.tgz",
+            "integrity": "sha512-O3lg1X7zy4sQs+jDYHu+njFQCC5hYJWRmmbLy9UVhgqQKZifS4DYqkoAedK3ixj5NQ1stMNmJGJxbEvJLw/NWA==",
             "dev": true,
             "dependencies": {
-                "camelcase": "^5.3.1",
-                "find-up": "^4.1.0",
-                "get-package-type": "^0.1.0",
-                "js-yaml": "^3.13.1",
-                "resolve-from": "^5.0.0"
+                "pirates": "^4.0.5",
+                "process-on-spawn": "^1.0.0",
+                "signal-exit": "^4.0.2",
+                "uuid": "^8.3.2"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=16.17"
             }
         },
-        "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
-            "version": "1.0.10",
-            "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-            "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+        "node_modules/@tapjs/reporter": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/@tapjs/reporter/-/reporter-1.3.0.tgz",
+            "integrity": "sha512-zjXwsZh895zUPM00w9q0W2u/y2ncTz4q/FYu3Jl8Ph0KcSTiGBob01Rj4+Uhhx0N5YwJxb4HOujRtAqhyqs7Gg==",
             "dev": true,
             "dependencies": {
-                "sprintf-js": "~1.0.2"
+                "@tapjs/config": "2.4.0",
+                "@tapjs/test": "1.3.4",
+                "chalk": "^5.2.0",
+                "ink": "^4.4.1",
+                "minipass": "^7.0.3",
+                "ms": "^2.1.3",
+                "patch-console": "^2.0.0",
+                "prismjs": "^1.29.0",
+                "prismjs-terminal": "^1.2.3",
+                "react": "^18.2.0",
+                "string-length": "^6.0.0",
+                "tcompare": "6.4.0"
+            },
+            "engines": {
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
-            "version": "3.14.1",
-            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-            "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+        "node_modules/@tapjs/reporter/node_modules/chalk": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+            "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+            "dev": true,
+            "engines": {
+                "node": "^12.17.0 || ^14.13 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
+            }
+        },
+        "node_modules/@tapjs/reporter/node_modules/ms": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+            "dev": true
+        },
+        "node_modules/@tapjs/run": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/@tapjs/run/-/run-1.4.0.tgz",
+            "integrity": "sha512-3LNRejFAos8iND30CiQV+RIdaiHBKjsLNq1BZ/nena7lcshKoQCFtiVpKMlqGAStMQgLygjgSo2uHbuSDD0Qww==",
+            "dev": true,
+            "dependencies": {
+                "@tapjs/after": "1.1.4",
+                "@tapjs/before": "1.1.4",
+                "@tapjs/config": "2.4.0",
+                "@tapjs/processinfo": "^3.1.2",
+                "@tapjs/reporter": "1.3.0",
+                "@tapjs/spawn": "1.1.4",
+                "@tapjs/stdin": "1.1.4",
+                "@tapjs/test": "1.3.4",
+                "c8": "^8.0.1",
+                "chokidar": "^3.5.3",
+                "foreground-child": "^3.1.1",
+                "glob": "^10.3.10",
+                "minipass": "^7.0.3",
+                "mkdirp": "^3.0.1",
+                "opener": "^1.5.2",
+                "pacote": "^17.0.3",
+                "path-scurry": "^1.9.2",
+                "resolve-import": "^1.4.2",
+                "rimraf": "^5.0.5",
+                "semver": "^7.5.4",
+                "signal-exit": "^4.1.0",
+                "tap-yaml": "2.2.0",
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0",
+                "ts-node": "npm:@isaacs/ts-node-temp-fork-for-pr-2009@^10.9.1",
+                "which": "^4.0.0"
+            },
+            "bin": {
+                "tap-run": "dist/esm/index.js"
+            },
+            "engines": {
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
+            }
+        },
+        "node_modules/@tapjs/run/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
             "dev": true,
             "dependencies": {
-                "argparse": "^1.0.7",
-                "esprima": "^4.0.0"
+                "balanced-match": "^1.0.0"
+            }
+        },
+        "node_modules/@tapjs/run/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+            "dev": true,
+            "dependencies": {
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
             },
             "bin": {
-                "js-yaml": "bin/js-yaml.js"
+                "glob": "dist/esm/bin.mjs"
+            },
+            "engines": {
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
-            "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+        "node_modules/@tapjs/run/node_modules/isexe": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+            "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
             "dev": true,
             "engines": {
-                "node": ">=8"
+                "node": ">=16"
             }
         },
-        "node_modules/@istanbuljs/schema": {
-            "version": "0.1.3",
-            "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
-            "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+        "node_modules/@tapjs/run/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
             "dev": true,
+            "dependencies": {
+                "brace-expansion": "^2.0.1"
+            },
             "engines": {
-                "node": ">=8"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@jridgewell/gen-mapping": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
-            "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+        "node_modules/@tapjs/run/node_modules/rimraf": {
+            "version": "5.0.5",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+            "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
             "dev": true,
             "dependencies": {
-                "@jridgewell/set-array": "^1.0.0",
-                "@jridgewell/sourcemap-codec": "^1.4.10"
+                "glob": "^10.3.7"
+            },
+            "bin": {
+                "rimraf": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">=6.0.0"
+                "node": ">=14"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@jridgewell/resolve-uri": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
-            "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+        "node_modules/@tapjs/run/node_modules/which": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+            "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
             "dev": true,
+            "dependencies": {
+                "isexe": "^3.1.1"
+            },
+            "bin": {
+                "node-which": "bin/which.js"
+            },
             "engines": {
-                "node": ">=6.0.0"
+                "node": "^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/@jridgewell/set-array": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
-            "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+        "node_modules/@tapjs/snapshot": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/snapshot/-/snapshot-1.2.4.tgz",
+            "integrity": "sha512-8pStZczbArIC6+s8TblHTs/Mr5RGApWZA91Eey5UuU5MX3IPUw77MPQpPOoh2zrefa8VZRmHM7IgQq8SKyYjyQ==",
             "dev": true,
+            "dependencies": {
+                "is-actual-promise": "^1.0.0",
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
+            },
             "engines": {
-                "node": ">=6.0.0"
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
-        "node_modules/@jridgewell/sourcemap-codec": {
-            "version": "1.4.14",
-            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
-            "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
-            "dev": true
+        "node_modules/@tapjs/spawn": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/spawn/-/spawn-1.1.4.tgz",
+            "integrity": "sha512-H3/VBi/Zfnb53PbpNmT/OYhIdqk8k6pGnM+WNLB8KBzwLa23q75P0jSYAEhzX3sZO+JIiaHACj/SxvttFapDtg==",
+            "dev": true,
+            "engines": {
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
+            }
         },
-        "node_modules/@jridgewell/trace-mapping": {
-            "version": "0.3.17",
-            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
-            "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+        "node_modules/@tapjs/stack": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/@tapjs/stack/-/stack-1.2.3.tgz",
+            "integrity": "sha512-LY7Rxse2QY+DczTCoqOA4rxjqhnCgXYZeynrhzOsiut6IVnDWnqjUvZMq1XYnk5G69lhgG5lTDHmZrKP33BKgg==",
             "dev": true,
             "dependencies": {
-                "@jridgewell/resolve-uri": "3.1.0",
-                "@jridgewell/sourcemap-codec": "1.4.14"
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@nodelib/fs.scandir": {
-            "version": "2.1.5",
-            "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
-            "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+        "node_modules/@tapjs/stdin": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/stdin/-/stdin-1.1.4.tgz",
+            "integrity": "sha512-yQzeiWaWRFd5jXVy3F0Q4inQqVmEGynFfWz2cbQYJFm/CNCcKFM1t4uIRRqtNdfJwSrr19m8Lq0qqfT7pHV/yg==",
+            "dev": true,
+            "engines": {
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
+            }
+        },
+        "node_modules/@tapjs/test": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/test/-/test-1.3.4.tgz",
+            "integrity": "sha512-ud2T10OhxdQw4f7Wo4G+5/Vyw5JYgfb5bDmKo0B3xmMgVvIFpUS/4V2Zq+59DZGXmEgjO0KPhb8NvOpOHAy/fg==",
+            "dev": true,
+            "dependencies": {
+                "@tapjs/after": "1.1.4",
+                "@tapjs/after-each": "1.1.4",
+                "@tapjs/asserts": "1.1.4",
+                "@tapjs/before": "1.1.4",
+                "@tapjs/before-each": "1.1.4",
+                "@tapjs/filter": "1.2.4",
+                "@tapjs/fixture": "1.2.4",
+                "@tapjs/intercept": "1.2.4",
+                "@tapjs/mock": "1.2.2",
+                "@tapjs/node-serialize": "1.1.4",
+                "@tapjs/snapshot": "1.2.4",
+                "@tapjs/spawn": "1.1.4",
+                "@tapjs/stdin": "1.1.4",
+                "@tapjs/typescript": "1.2.4",
+                "@tapjs/worker": "1.1.4",
+                "glob": "^10.3.10",
+                "jackspeak": "^2.3.6",
+                "mkdirp": "^3.0.0",
+                "resolve-import": "^1.4.1",
+                "rimraf": "^5.0.5",
+                "sync-content": "^1.0.1",
+                "tap-parser": "15.2.0",
+                "ts-node": "npm:@isaacs/ts-node-temp-fork-for-pr-2009@^10.9.1",
+                "tshy": "^1.2.2",
+                "typescript": "5.2"
+            },
+            "bin": {
+                "generate-tap-test-class": "scripts/build.mjs"
+            },
+            "engines": {
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
+            }
+        },
+        "node_modules/@tapjs/test/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+            "dev": true,
             "dependencies": {
-                "@nodelib/fs.stat": "2.0.5",
-                "run-parallel": "^1.1.9"
+                "balanced-match": "^1.0.0"
+            }
+        },
+        "node_modules/@tapjs/test/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+            "dev": true,
+            "dependencies": {
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
+            },
+            "bin": {
+                "glob": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">= 8"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@nodelib/fs.stat": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
-            "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+        "node_modules/@tapjs/test/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+            "dev": true,
+            "dependencies": {
+                "brace-expansion": "^2.0.1"
+            },
             "engines": {
-                "node": ">= 8"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/@nodelib/fs.walk": {
-            "version": "1.2.8",
-            "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
-            "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+        "node_modules/@tapjs/test/node_modules/rimraf": {
+            "version": "5.0.5",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+            "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
+            "dev": true,
             "dependencies": {
-                "@nodelib/fs.scandir": "2.1.5",
-                "fastq": "^1.6.0"
+                "glob": "^10.3.7"
+            },
+            "bin": {
+                "rimraf": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">= 8"
+                "node": ">=14"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/@tapjs/typescript": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/typescript/-/typescript-1.2.4.tgz",
+            "integrity": "sha512-exhSckFlKLr0RFHKYBJb3N6CftoafH5GwNeAWN0yua+FmzwDleGvgKThW3l/xeOF7BeCq/m4zu9HWrwjkPaDhQ==",
+            "dev": true,
+            "dependencies": {
+                "ts-node": "npm:@isaacs/ts-node-temp-fork-for-pr-2009@^10.9.1"
+            },
+            "engines": {
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
             }
         },
+        "node_modules/@tapjs/worker": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/worker/-/worker-1.1.4.tgz",
+            "integrity": "sha512-HcaafOWghXpMtLaCk8BOIMQcphZU2Gi0OSUb6vzgxKQ4iQxTsBkJSnZ1+4F8Qed9EWZ9n6zaggjy7/fDLVdJRg==",
+            "dev": true,
+            "engines": {
+                "node": ">=16"
+            },
+            "peerDependencies": {
+                "@tapjs/core": "1.3.4"
+            }
+        },
+        "node_modules/@tootallnate/once": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+            "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+            "dev": true,
+            "engines": {
+                "node": ">= 10"
+            }
+        },
+        "node_modules/@tsconfig/node14": {
+            "version": "14.1.0",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-14.1.0.tgz",
+            "integrity": "sha512-VmsCG04YR58ciHBeJKBDNMWWfYbyP8FekWVuTlpstaUPlat1D0x/tXzkWP7yCMU0eSz9V4OZU0LBWTFJ3xZf6w==",
+            "dev": true
+        },
+        "node_modules/@tsconfig/node16": {
+            "version": "16.1.1",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-16.1.1.tgz",
+            "integrity": "sha512-+pio93ejHN4nINX4pXqfnR/fPLRtJBaT4ORaa5RH0Oc1zoYmo2B2koG+M328CQhHKn1Wj6FcOxCDFXAot9NhvA==",
+            "dev": true
+        },
+        "node_modules/@tsconfig/node18": {
+            "version": "18.2.2",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.2.tgz",
+            "integrity": "sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==",
+            "dev": true
+        },
+        "node_modules/@tsconfig/node20": {
+            "version": "20.1.2",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.2.tgz",
+            "integrity": "sha512-madaWq2k+LYMEhmcp0fs+OGaLFk0OenpHa4gmI4VEmCKX4PJntQ6fnnGADVFrVkBj0wIdAlQnK/MrlYTHsa1gQ==",
+            "dev": true
+        },
+        "node_modules/@tufjs/canonical-json": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
+            "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==",
+            "dev": true,
+            "engines": {
+                "node": "^16.14.0 || >=18.0.0"
+            }
+        },
+        "node_modules/@tufjs/models": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.0.tgz",
+            "integrity": "sha512-c8nj8BaOExmZKO2DXhDfegyhSGcG9E/mPN3U13L+/PsoWm1uaGiHHjxqSHQiasDBQwDA3aHuw9+9spYAP1qvvg==",
+            "dev": true,
+            "dependencies": {
+                "@tufjs/canonical-json": "2.0.0",
+                "minimatch": "^9.0.3"
+            },
+            "engines": {
+                "node": "^16.14.0 || >=18.0.0"
+            }
+        },
+        "node_modules/@tufjs/models/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+            "dev": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0"
+            }
+        },
+        "node_modules/@tufjs/models/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+            "dev": true,
+            "dependencies": {
+                "brace-expansion": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/@types/istanbul-lib-coverage": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+            "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+            "dev": true
+        },
+        "node_modules/@types/node": {
+            "version": "20.8.0",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.0.tgz",
+            "integrity": "sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==",
+            "dev": true,
+            "peer": true
+        },
+        "node_modules/abbrev": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+            "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+            "dev": true
+        },
         "node_modules/acorn": {
             "version": "8.8.2",
             "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
@@ -661,6 +1386,39 @@
                 "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
             }
         },
+        "node_modules/acorn-walk": {
+            "version": "8.2.0",
+            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+            "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/agent-base": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+            "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+            "dev": true,
+            "dependencies": {
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/agentkeepalive": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
+            "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
+            "dev": true,
+            "dependencies": {
+                "humanize-ms": "^1.2.1"
+            },
+            "engines": {
+                "node": ">= 8.0.0"
+            }
+        },
         "node_modules/aggregate-error": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@@ -674,6 +1432,15 @@
                 "node": ">=8"
             }
         },
+        "node_modules/aggregate-error/node_modules/indent-string": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+            "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
         "node_modules/ajv": {
             "version": "6.12.6",
             "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -689,6 +1456,33 @@
                 "url": "https://github.com/sponsors/epoberezkin"
             }
         },
+        "node_modules/ansi-escapes": {
+            "version": "6.2.0",
+            "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz",
+            "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==",
+            "dev": true,
+            "dependencies": {
+                "type-fest": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=14.16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/ansi-escapes/node_modules/type-fest": {
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz",
+            "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
+            "dev": true,
+            "engines": {
+                "node": ">=14.16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
         "node_modules/ansi-regex": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -724,22 +1518,29 @@
                 "node": ">= 8"
             }
         },
-        "node_modules/append-transform": {
+        "node_modules/aproba": {
             "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz",
-            "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==",
+            "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+            "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+            "dev": true
+        },
+        "node_modules/are-we-there-yet": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
+            "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
             "dev": true,
             "dependencies": {
-                "default-require-extensions": "^3.0.0"
+                "delegates": "^1.0.0",
+                "readable-stream": "^3.6.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
             }
         },
-        "node_modules/archy": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
-            "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==",
+        "node_modules/arg": {
+            "version": "4.1.3",
+            "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+            "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
             "dev": true
         },
         "node_modules/argparse": {
@@ -748,12 +1549,24 @@
             "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
         },
         "node_modules/async-hook-domain": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.4.tgz",
-            "integrity": "sha512-14LjCmlK1PK8eDtTezR6WX8TMaYNIzBIsd2D1sGoGjgx0BuNMMoSdk7i/drlbtamy0AWv9yv2tkB+ASdmeqFIw==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-4.0.1.tgz",
+            "integrity": "sha512-bSktexGodAjfHWIrSrrqxqWzf1hWBZBpmPNZv+TYUMyWa2eoefFc6q6H1+KtdHYSz35lrhWdmXt/XK9wNEZvww==",
             "dev": true,
             "engines": {
-                "node": ">=10"
+                "node": ">=16"
+            }
+        },
+        "node_modules/auto-bind": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz",
+            "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==",
+            "dev": true,
+            "engines": {
+                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
         "node_modules/balanced-match": {
@@ -770,15 +1583,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/bind-obj-methods": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz",
-            "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==",
-            "dev": true,
-            "engines": {
-                "node": ">=10"
-            }
-        },
         "node_modules/brace-expansion": {
             "version": "1.1.11",
             "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -800,87 +1604,136 @@
                 "node": ">=8"
             }
         },
-        "node_modules/browserslist": {
-            "version": "4.21.5",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
-            "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+        "node_modules/builtins": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
+            "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
             "dev": true,
-            "funding": [
-                {
-                    "type": "opencollective",
-                    "url": "https://opencollective.com/browserslist"
-                },
-                {
-                    "type": "tidelift",
-                    "url": "https://tidelift.com/funding/github/npm/browserslist"
-                }
-            ],
             "dependencies": {
-                "caniuse-lite": "^1.0.30001449",
-                "electron-to-chromium": "^1.4.284",
-                "node-releases": "^2.0.8",
-                "update-browserslist-db": "^1.0.10"
+                "semver": "^7.0.0"
+            }
+        },
+        "node_modules/c8": {
+            "version": "8.0.1",
+            "resolved": "https://registry.npmjs.org/c8/-/c8-8.0.1.tgz",
+            "integrity": "sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==",
+            "dev": true,
+            "dependencies": {
+                "@bcoe/v8-coverage": "^0.2.3",
+                "@istanbuljs/schema": "^0.1.3",
+                "find-up": "^5.0.0",
+                "foreground-child": "^2.0.0",
+                "istanbul-lib-coverage": "^3.2.0",
+                "istanbul-lib-report": "^3.0.1",
+                "istanbul-reports": "^3.1.6",
+                "rimraf": "^3.0.2",
+                "test-exclude": "^6.0.0",
+                "v8-to-istanbul": "^9.0.0",
+                "yargs": "^17.7.2",
+                "yargs-parser": "^21.1.1"
             },
             "bin": {
-                "browserslist": "cli.js"
+                "c8": "bin/c8.js"
             },
             "engines": {
-                "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+                "node": ">=12"
             }
         },
-        "node_modules/buffer-from": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
-            "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+        "node_modules/c8/node_modules/foreground-child": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
+            "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+            "dev": true,
+            "dependencies": {
+                "cross-spawn": "^7.0.0",
+                "signal-exit": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            }
+        },
+        "node_modules/c8/node_modules/signal-exit": {
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
             "dev": true
         },
-        "node_modules/caching-transform": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
-            "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==",
+        "node_modules/cacache": {
+            "version": "18.0.0",
+            "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.0.tgz",
+            "integrity": "sha512-I7mVOPl3PUCeRub1U8YoGz2Lqv9WOBpobZ8RyWFXmReuILz+3OAyTa5oH3QPdtKZD7N0Yk00aLfzn0qvp8dZ1w==",
             "dev": true,
             "dependencies": {
-                "hasha": "^5.0.0",
-                "make-dir": "^3.0.0",
-                "package-hash": "^4.0.0",
-                "write-file-atomic": "^3.0.0"
+                "@npmcli/fs": "^3.1.0",
+                "fs-minipass": "^3.0.0",
+                "glob": "^10.2.2",
+                "lru-cache": "^10.0.1",
+                "minipass": "^7.0.3",
+                "minipass-collect": "^1.0.2",
+                "minipass-flush": "^1.0.5",
+                "minipass-pipeline": "^1.2.4",
+                "p-map": "^4.0.0",
+                "ssri": "^10.0.0",
+                "tar": "^6.1.11",
+                "unique-filename": "^3.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/callsites": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
-            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-            "engines": {
-                "node": ">=6"
+        "node_modules/cacache/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+            "dev": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0"
             }
         },
-        "node_modules/camelcase": {
-            "version": "5.3.1",
-            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-            "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+        "node_modules/cacache/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
             "dev": true,
+            "dependencies": {
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
+            },
+            "bin": {
+                "glob": "dist/esm/bin.mjs"
+            },
             "engines": {
-                "node": ">=6"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/caniuse-lite": {
-            "version": "1.0.30001469",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz",
-            "integrity": "sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g==",
+        "node_modules/cacache/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
             "dev": true,
-            "funding": [
-                {
-                    "type": "opencollective",
-                    "url": "https://opencollective.com/browserslist"
-                },
-                {
-                    "type": "tidelift",
-                    "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
-                }
-            ]
+            "dependencies": {
+                "brace-expansion": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/callsites": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+            "engines": {
+                "node": ">=6"
+            }
         },
         "node_modules/chalk": {
             "version": "4.1.2",
@@ -936,11 +1789,35 @@
                 "node": ">= 6"
             }
         },
+        "node_modules/chownr": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+            "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=10"
+            }
+        },
         "node_modules/chroma-js": {
             "version": "2.4.2",
             "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz",
             "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A=="
         },
+        "node_modules/ci-info": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+            "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/sibiraj-s"
+                }
+            ],
+            "engines": {
+                "node": ">=8"
+            }
+        },
         "node_modules/clean-stack": {
             "version": "2.2.0",
             "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -950,15 +1827,147 @@
                 "node": ">=6"
             }
         },
+        "node_modules/cli-boxes": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+            "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
+            "dev": true,
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/cli-cursor": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
+            "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+            "dev": true,
+            "dependencies": {
+                "restore-cursor": "^4.0.0"
+            },
+            "engines": {
+                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/cli-truncate": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz",
+            "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==",
+            "dev": true,
+            "dependencies": {
+                "slice-ansi": "^5.0.0",
+                "string-width": "^5.0.0"
+            },
+            "engines": {
+                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/cli-truncate/node_modules/ansi-styles": {
+            "version": "6.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+            "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+            "dev": true,
+            "engines": {
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+            }
+        },
+        "node_modules/cli-truncate/node_modules/slice-ansi": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+            "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+            "dev": true,
+            "dependencies": {
+                "ansi-styles": "^6.0.0",
+                "is-fullwidth-code-point": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+            }
+        },
         "node_modules/cliui": {
-            "version": "7.0.4",
-            "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-            "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+            "version": "8.0.1",
+            "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+            "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
             "dev": true,
             "dependencies": {
                 "string-width": "^4.2.0",
-                "strip-ansi": "^6.0.0",
+                "strip-ansi": "^6.0.1",
                 "wrap-ansi": "^7.0.0"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/cliui/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "dev": true
+        },
+        "node_modules/cliui/node_modules/is-fullwidth-code-point": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/cliui/node_modules/string-width": {
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+            "dev": true,
+            "dependencies": {
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/cliui/node_modules/wrap-ansi": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+            "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+            "dev": true,
+            "dependencies": {
+                "ansi-styles": "^4.0.0",
+                "string-width": "^4.1.0",
+                "strip-ansi": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+            }
+        },
+        "node_modules/code-excerpt": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz",
+            "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==",
+            "dev": true,
+            "dependencies": {
+                "convert-to-spaces": "^2.0.1"
+            },
+            "engines": {
+                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
             }
         },
         "node_modules/color-convert": {
@@ -991,23 +2000,32 @@
             "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
             "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="
         },
-        "node_modules/commondir": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-            "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
-            "dev": true
-        },
         "node_modules/concat-map": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
             "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
         },
+        "node_modules/console-control-strings": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+            "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+            "dev": true
+        },
         "node_modules/convert-source-map": {
             "version": "1.9.0",
             "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
             "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
             "dev": true
         },
+        "node_modules/convert-to-spaces": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz",
+            "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==",
+            "dev": true,
+            "engines": {
+                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+            }
+        },
         "node_modules/cross-spawn": {
             "version": "7.0.3",
             "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1037,34 +2055,16 @@
                 }
             }
         },
-        "node_modules/decamelize": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-            "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
-            "dev": true,
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
         "node_modules/deep-is": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
             "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
         },
-        "node_modules/default-require-extensions": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz",
-            "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==",
-            "dev": true,
-            "dependencies": {
-                "strip-bom": "^4.0.0"
-            },
-            "engines": {
-                "node": ">=8"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
+        "node_modules/delegates": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+            "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+            "dev": true
         },
         "node_modules/diff": {
             "version": "4.0.2",
@@ -1086,22 +2086,41 @@
                 "node": ">=6.0.0"
             }
         },
-        "node_modules/electron-to-chromium": {
-            "version": "1.4.340",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.340.tgz",
-            "integrity": "sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg==",
+        "node_modules/eastasianwidth": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+            "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
             "dev": true
         },
         "node_modules/emoji-regex": {
-            "version": "8.0.0",
-            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "version": "9.2.2",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+            "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
             "dev": true
         },
-        "node_modules/es6-error": {
-            "version": "4.1.1",
-            "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
-            "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+        "node_modules/encoding": {
+            "version": "0.1.13",
+            "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+            "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+            "dev": true,
+            "optional": true,
+            "dependencies": {
+                "iconv-lite": "^0.6.2"
+            }
+        },
+        "node_modules/env-paths": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+            "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+            "dev": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/err-code": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+            "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
             "dev": true
         },
         "node_modules/escalade": {
@@ -1203,63 +2222,6 @@
                 "url": "https://opencollective.com/eslint"
             }
         },
-        "node_modules/eslint/node_modules/find-up": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
-            "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
-            "dependencies": {
-                "locate-path": "^6.0.0",
-                "path-exists": "^4.0.0"
-            },
-            "engines": {
-                "node": ">=10"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        },
-        "node_modules/eslint/node_modules/locate-path": {
-            "version": "6.0.0",
-            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
-            "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
-            "dependencies": {
-                "p-locate": "^5.0.0"
-            },
-            "engines": {
-                "node": ">=10"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        },
-        "node_modules/eslint/node_modules/p-limit": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
-            "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
-            "dependencies": {
-                "yocto-queue": "^0.1.0"
-            },
-            "engines": {
-                "node": ">=10"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        },
-        "node_modules/eslint/node_modules/p-locate": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
-            "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
-            "dependencies": {
-                "p-limit": "^3.0.2"
-            },
-            "engines": {
-                "node": ">=10"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        },
         "node_modules/espree": {
             "version": "9.5.1",
             "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
@@ -1276,19 +2238,6 @@
                 "url": "https://opencollective.com/eslint"
             }
         },
-        "node_modules/esprima": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-            "dev": true,
-            "bin": {
-                "esparse": "bin/esparse.js",
-                "esvalidate": "bin/esvalidate.js"
-            },
-            "engines": {
-                "node": ">=4"
-            }
-        },
         "node_modules/esquery": {
             "version": "1.5.0",
             "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
@@ -1328,9 +2277,18 @@
             }
         },
         "node_modules/events-to-array": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz",
-            "integrity": "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==",
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-2.0.3.tgz",
+            "integrity": "sha512-f/qE2gImHRa4Cp2y1stEOSgw8wTFyUdVJX7G//bMwbaV9JqISFxg99NbmVQeP7YLnDUZ2un851jlaDrlpmGehQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/exponential-backoff": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz",
+            "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==",
             "dev": true
         },
         "node_modules/fast-deep-equal": {
@@ -1379,42 +2337,21 @@
                 "node": ">=8"
             }
         },
-        "node_modules/find-cache-dir": {
-            "version": "3.3.2",
-            "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
-            "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
-            "dev": true,
-            "dependencies": {
-                "commondir": "^1.0.1",
-                "make-dir": "^3.0.2",
-                "pkg-dir": "^4.1.0"
-            },
-            "engines": {
-                "node": ">=8"
-            },
-            "funding": {
-                "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
-            }
-        },
         "node_modules/find-up": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-            "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-            "dev": true,
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+            "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
             "dependencies": {
-                "locate-path": "^5.0.0",
+                "locate-path": "^6.0.0",
                 "path-exists": "^4.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/findit": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz",
-            "integrity": "sha512-ENZS237/Hr8bjczn5eKuBohLgaD0JyUd0arxretR1f9RO46vZHA1b2y0VorgGV3WaOT3c+78P8h7v4JGJ1i/rg==",
-            "dev": true
-        },
         "node_modules/flat-cache": {
             "version": "3.0.4",
             "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -1433,16 +2370,19 @@
             "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg=="
         },
         "node_modules/foreground-child": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
-            "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
+            "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
             "dev": true,
             "dependencies": {
                 "cross-spawn": "^7.0.0",
-                "signal-exit": "^3.0.2"
+                "signal-exit": "^4.0.1"
             },
             "engines": {
-                "node": ">=8.0.0"
+                "node": ">=14"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
         "node_modules/fromentries": {
@@ -1465,11 +2405,17 @@
                 }
             ]
         },
-        "node_modules/fs-exists-cached": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz",
-            "integrity": "sha512-kSxoARUDn4F2RPXX48UXnaFKwVU7Ivd/6qpzZL29MCDmr9sTvybv4gFCp+qaI4fM9m0z9fgz/yJvi56GAz+BZg==",
-            "dev": true
+        "node_modules/fs-minipass": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
+            "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
+            "dev": true,
+            "dependencies": {
+                "minipass": "^7.0.3"
+            },
+            "engines": {
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+            }
         },
         "node_modules/fs.realpath": {
             "version": "1.0.0",
@@ -1490,19 +2436,70 @@
                 "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
             }
         },
+        "node_modules/function-bind": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+            "dev": true
+        },
         "node_modules/function-loop": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz",
-            "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-4.0.0.tgz",
+            "integrity": "sha512-f34iQBedYF3XcI93uewZZOnyscDragxgTK/eTvVB74k3fCD0ZorOi5BV9GS4M8rz/JoNi0Kl3qX5Y9MH3S/CLQ==",
+            "dev": true
+        },
+        "node_modules/gauge": {
+            "version": "4.0.4",
+            "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
+            "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+            "dev": true,
+            "dependencies": {
+                "aproba": "^1.0.3 || ^2.0.0",
+                "color-support": "^1.1.3",
+                "console-control-strings": "^1.1.0",
+                "has-unicode": "^2.0.1",
+                "signal-exit": "^3.0.7",
+                "string-width": "^4.2.3",
+                "strip-ansi": "^6.0.1",
+                "wide-align": "^1.1.5"
+            },
+            "engines": {
+                "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+            }
+        },
+        "node_modules/gauge/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
             "dev": true
         },
-        "node_modules/gensync": {
-            "version": "1.0.0-beta.2",
-            "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
-            "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+        "node_modules/gauge/node_modules/is-fullwidth-code-point": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
             "dev": true,
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=8"
+            }
+        },
+        "node_modules/gauge/node_modules/signal-exit": {
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+            "dev": true
+        },
+        "node_modules/gauge/node_modules/string-width": {
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+            "dev": true,
+            "dependencies": {
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
+            },
+            "engines": {
+                "node": ">=8"
             }
         },
         "node_modules/get-caller-file": {
@@ -1514,15 +2511,6 @@
                 "node": "6.* || 8.* || >= 10.*"
             }
         },
-        "node_modules/get-package-type": {
-            "version": "0.1.0",
-            "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
-            "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
-            "dev": true,
-            "engines": {
-                "node": ">=8.0.0"
-            }
-        },
         "node_modules/glob": {
             "version": "7.2.3",
             "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -1578,39 +2566,32 @@
             "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
             "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
         },
-        "node_modules/has-flag": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/hasha": {
-            "version": "5.2.2",
-            "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
-            "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
+        "node_modules/has": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
             "dev": true,
             "dependencies": {
-                "is-stream": "^2.0.0",
-                "type-fest": "^0.8.0"
+                "function-bind": "^1.1.1"
             },
             "engines": {
-                "node": ">=8"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "node": ">= 0.4.0"
             }
         },
-        "node_modules/hasha/node_modules/type-fest": {
-            "version": "0.8.1",
-            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
-            "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
-            "dev": true,
+        "node_modules/has-flag": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
             "engines": {
                 "node": ">=8"
             }
         },
+        "node_modules/has-unicode": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+            "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+            "dev": true
+        },
         "node_modules/he": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -1619,12 +2600,79 @@
                 "he": "bin/he"
             }
         },
+        "node_modules/hosted-git-info": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz",
+            "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==",
+            "dev": true,
+            "dependencies": {
+                "lru-cache": "^10.0.1"
+            },
+            "engines": {
+                "node": "^16.14.0 || >=18.0.0"
+            }
+        },
         "node_modules/html-escaper": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
             "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
             "dev": true
         },
+        "node_modules/http-cache-semantics": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+            "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
+            "dev": true
+        },
+        "node_modules/http-proxy-agent": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+            "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+            "dev": true,
+            "dependencies": {
+                "@tootallnate/once": "2",
+                "agent-base": "6",
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/https-proxy-agent": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+            "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+            "dev": true,
+            "dependencies": {
+                "agent-base": "6",
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/humanize-ms": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+            "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+            "dev": true,
+            "dependencies": {
+                "ms": "^2.0.0"
+            }
+        },
+        "node_modules/iconv-lite": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+            "dev": true,
+            "optional": true,
+            "dependencies": {
+                "safer-buffer": ">= 2.1.2 < 3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/ignore": {
             "version": "5.2.4",
             "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -1633,6 +2681,42 @@
                 "node": ">= 4"
             }
         },
+        "node_modules/ignore-walk": {
+            "version": "6.0.3",
+            "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.3.tgz",
+            "integrity": "sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==",
+            "dev": true,
+            "dependencies": {
+                "minimatch": "^9.0.0"
+            },
+            "engines": {
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+            }
+        },
+        "node_modules/ignore-walk/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+            "dev": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0"
+            }
+        },
+        "node_modules/ignore-walk/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+            "dev": true,
+            "dependencies": {
+                "brace-expansion": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
         "node_modules/image-size": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz",
@@ -1671,12 +2755,15 @@
             }
         },
         "node_modules/indent-string": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
-            "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+            "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
             "dev": true,
             "engines": {
-                "node": ">=8"
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
         "node_modules/inflight": {
@@ -1693,6 +2780,97 @@
             "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
             "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
         },
+        "node_modules/ink": {
+            "version": "4.4.1",
+            "resolved": "https://registry.npmjs.org/ink/-/ink-4.4.1.tgz",
+            "integrity": "sha512-rXckvqPBB0Krifk5rn/5LvQGmyXwCUpBfmTwbkQNBY9JY8RSl3b8OftBNEYxg4+SWUhEKcPifgope28uL9inlA==",
+            "dev": true,
+            "dependencies": {
+                "@alcalzone/ansi-tokenize": "^0.1.3",
+                "ansi-escapes": "^6.0.0",
+                "auto-bind": "^5.0.1",
+                "chalk": "^5.2.0",
+                "cli-boxes": "^3.0.0",
+                "cli-cursor": "^4.0.0",
+                "cli-truncate": "^3.1.0",
+                "code-excerpt": "^4.0.0",
+                "indent-string": "^5.0.0",
+                "is-ci": "^3.0.1",
+                "is-lower-case": "^2.0.2",
+                "is-upper-case": "^2.0.2",
+                "lodash": "^4.17.21",
+                "patch-console": "^2.0.0",
+                "react-reconciler": "^0.29.0",
+                "scheduler": "^0.23.0",
+                "signal-exit": "^3.0.7",
+                "slice-ansi": "^6.0.0",
+                "stack-utils": "^2.0.6",
+                "string-width": "^5.1.2",
+                "type-fest": "^0.12.0",
+                "widest-line": "^4.0.1",
+                "wrap-ansi": "^8.1.0",
+                "ws": "^8.12.0",
+                "yoga-wasm-web": "~0.3.3"
+            },
+            "engines": {
+                "node": ">=14.16"
+            },
+            "peerDependencies": {
+                "@types/react": ">=18.0.0",
+                "react": ">=18.0.0",
+                "react-devtools-core": "^4.19.1"
+            },
+            "peerDependenciesMeta": {
+                "@types/react": {
+                    "optional": true
+                },
+                "react-devtools-core": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/ink/node_modules/chalk": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+            "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+            "dev": true,
+            "engines": {
+                "node": "^12.17.0 || ^14.13 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
+            }
+        },
+        "node_modules/ink/node_modules/signal-exit": {
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+            "dev": true
+        },
+        "node_modules/ink/node_modules/type-fest": {
+            "version": "0.12.0",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz",
+            "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==",
+            "dev": true,
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/ip": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
+            "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
+            "dev": true
+        },
+        "node_modules/is-actual-promise": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-actual-promise/-/is-actual-promise-1.0.0.tgz",
+            "integrity": "sha512-DWSmKTiEoY3Y9LGHG9TVnFgydCCu+3fLJi4rv3fpi0gL/lKoILekh/oF/nO3/Lq1l5Rqo+tQt5TWzxMmYIhWyg==",
+            "dev": true
+        },
         "node_modules/is-binary-path": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -1705,6 +2883,30 @@
                 "node": ">=8"
             }
         },
+        "node_modules/is-ci": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
+            "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+            "dev": true,
+            "dependencies": {
+                "ci-info": "^3.2.0"
+            },
+            "bin": {
+                "is-ci": "bin.js"
+            }
+        },
+        "node_modules/is-core-module": {
+            "version": "2.13.0",
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+            "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+            "dev": true,
+            "dependencies": {
+                "has": "^1.0.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/is-extglob": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -1714,12 +2916,15 @@
             }
         },
         "node_modules/is-fullwidth-code-point": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+            "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
             "dev": true,
             "engines": {
-                "node": ">=8"
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
         "node_modules/is-glob": {
@@ -1733,6 +2938,21 @@
                 "node": ">=0.10.0"
             }
         },
+        "node_modules/is-lambda": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
+            "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+            "dev": true
+        },
+        "node_modules/is-lower-case": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz",
+            "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==",
+            "dev": true,
+            "dependencies": {
+                "tslib": "^2.0.3"
+            }
+        },
         "node_modules/is-number": {
             "version": "7.0.0",
             "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -1750,31 +2970,22 @@
                 "node": ">=8"
             }
         },
-        "node_modules/is-stream": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-            "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+        "node_modules/is-plain-object": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+            "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
             "dev": true,
             "engines": {
-                "node": ">=8"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "node": ">=0.10.0"
             }
         },
-        "node_modules/is-typedarray": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-            "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
-            "dev": true
-        },
-        "node_modules/is-windows": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-            "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+        "node_modules/is-upper-case": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz",
+            "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==",
             "dev": true,
-            "engines": {
-                "node": ">=0.10.0"
+            "dependencies": {
+                "tslib": "^2.0.3"
             }
         },
         "node_modules/isexe": {
@@ -1791,82 +3002,24 @@
                 "node": ">=8"
             }
         },
-        "node_modules/istanbul-lib-hook": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz",
-            "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==",
-            "dev": true,
-            "dependencies": {
-                "append-transform": "^2.0.0"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/istanbul-lib-instrument": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
-            "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
-            "dev": true,
-            "dependencies": {
-                "@babel/core": "^7.7.5",
-                "@istanbuljs/schema": "^0.1.2",
-                "istanbul-lib-coverage": "^3.0.0",
-                "semver": "^6.3.0"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/istanbul-lib-processinfo": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz",
-            "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==",
-            "dev": true,
-            "dependencies": {
-                "archy": "^1.0.0",
-                "cross-spawn": "^7.0.3",
-                "istanbul-lib-coverage": "^3.2.0",
-                "p-map": "^3.0.0",
-                "rimraf": "^3.0.0",
-                "uuid": "^8.3.2"
-            },
-            "engines": {
-                "node": ">=8"
-            }
-        },
         "node_modules/istanbul-lib-report": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
-            "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+            "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
             "dev": true,
             "dependencies": {
                 "istanbul-lib-coverage": "^3.0.0",
-                "make-dir": "^3.0.0",
+                "make-dir": "^4.0.0",
                 "supports-color": "^7.1.0"
             },
             "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/istanbul-lib-source-maps": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
-            "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
-            "dev": true,
-            "dependencies": {
-                "debug": "^4.1.1",
-                "istanbul-lib-coverage": "^3.0.0",
-                "source-map": "^0.6.1"
-            },
-            "engines": {
                 "node": ">=10"
             }
         },
         "node_modules/istanbul-reports": {
-            "version": "3.1.5",
-            "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
-            "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+            "version": "3.1.6",
+            "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
+            "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
             "dev": true,
             "dependencies": {
                 "html-escaper": "^2.0.0",
@@ -1877,15 +3030,21 @@
             }
         },
         "node_modules/jackspeak": {
-            "version": "1.4.2",
-            "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.2.tgz",
-            "integrity": "sha512-GHeGTmnuaHnvS+ZctRB01bfxARuu9wW83ENbuiweu07SFcVlZrJpcshSre/keGT7YGBhLHg/+rXCNSrsEHKU4Q==",
+            "version": "2.3.6",
+            "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
+            "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
             "dev": true,
             "dependencies": {
-                "cliui": "^7.0.4"
+                "@isaacs/cliui": "^8.0.2"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=14"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            },
+            "optionalDependencies": {
+                "@pkgjs/parseargs": "^0.11.0"
             }
         },
         "node_modules/js-sdsl": {
@@ -1914,16 +3073,13 @@
                 "js-yaml": "bin/js-yaml.js"
             }
         },
-        "node_modules/jsesc": {
-            "version": "2.5.2",
-            "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
-            "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+        "node_modules/json-parse-even-better-errors": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz",
+            "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==",
             "dev": true,
-            "bin": {
-                "jsesc": "bin/jsesc"
-            },
             "engines": {
-                "node": ">=4"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
         "node_modules/json-schema-traverse": {
@@ -1936,17 +3092,14 @@
             "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
             "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
         },
-        "node_modules/json5": {
-            "version": "2.2.3",
-            "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
-            "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+        "node_modules/jsonparse": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+            "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
             "dev": true,
-            "bin": {
-                "json5": "lib/cli.js"
-            },
-            "engines": {
-                "node": ">=6"
-            }
+            "engines": [
+                "node >= 0.2.0"
+            ]
         },
         "node_modules/levn": {
             "version": "0.4.1",
@@ -1960,61 +3113,24 @@
                 "node": ">= 0.8.0"
             }
         },
-        "node_modules/libtap": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.4.0.tgz",
-            "integrity": "sha512-STLFynswQ2A6W14JkabgGetBNk6INL1REgJ9UeNKw5llXroC2cGLgKTqavv0sl8OLVztLLipVKMcQ7yeUcqpmg==",
-            "dev": true,
+        "node_modules/locate-path": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+            "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
             "dependencies": {
-                "async-hook-domain": "^2.0.4",
-                "bind-obj-methods": "^3.0.0",
-                "diff": "^4.0.2",
-                "function-loop": "^2.0.1",
-                "minipass": "^3.1.5",
-                "own-or": "^1.0.0",
-                "own-or-env": "^1.0.2",
-                "signal-exit": "^3.0.4",
-                "stack-utils": "^2.0.4",
-                "tap-parser": "^11.0.0",
-                "tap-yaml": "^1.0.0",
-                "tcompare": "^5.0.6",
-                "trivial-deferred": "^1.0.1"
+                "p-locate": "^5.0.0"
             },
             "engines": {
                 "node": ">=10"
             },
             "funding": {
-                "url": "https://github.com/sponsors/isaacs"
-            }
-        },
-        "node_modules/libtap/node_modules/tcompare": {
-            "version": "5.0.7",
-            "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz",
-            "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==",
-            "dev": true,
-            "dependencies": {
-                "diff": "^4.0.2"
-            },
-            "engines": {
-                "node": ">=10"
-            }
-        },
-        "node_modules/locate-path": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-            "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-            "dev": true,
-            "dependencies": {
-                "p-locate": "^4.1.0"
-            },
-            "engines": {
-                "node": ">=8"
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/lodash.flattendeep": {
-            "version": "4.4.0",
-            "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
-            "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
+        "node_modules/lodash": {
+            "version": "4.17.21",
+            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+            "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
             "dev": true
         },
         "node_modules/lodash.merge": {
@@ -2022,1864 +3138,1668 @@
             "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
             "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
         },
-        "node_modules/lru-cache": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-            "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+        "node_modules/loose-envify": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+            "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
             "dev": true,
             "dependencies": {
-                "yallist": "^3.0.2"
+                "js-tokens": "^3.0.0 || ^4.0.0"
+            },
+            "bin": {
+                "loose-envify": "cli.js"
             }
         },
-        "node_modules/lru-cache/node_modules/yallist": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-            "dev": true
+        "node_modules/lru-cache": {
+            "version": "10.0.1",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz",
+            "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==",
+            "dev": true,
+            "engines": {
+                "node": "14 || >=16.14"
+            }
         },
         "node_modules/make-dir": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
-            "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+            "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
             "dev": true,
             "dependencies": {
-                "semver": "^6.0.0"
+                "semver": "^7.5.3"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=10"
             },
             "funding": {
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/marked": {
-            "version": "5.0.2",
-            "resolved": "https://registry.npmjs.org/marked/-/marked-5.0.2.tgz",
-            "integrity": "sha512-TXksm9GwqXCRNbFUZmMtqNLvy3K2cQHuWmyBDLOrY1e6i9UvZpOTJXoz7fBjYkJkaUFzV9hBFxMuZSyQt8R6KQ==",
-            "bin": {
-                "marked": "bin/marked.js"
+        "node_modules/make-error": {
+            "version": "1.3.6",
+            "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+            "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+            "dev": true
+        },
+        "node_modules/make-fetch-happen": {
+            "version": "11.1.1",
+            "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz",
+            "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==",
+            "dev": true,
+            "dependencies": {
+                "agentkeepalive": "^4.2.1",
+                "cacache": "^17.0.0",
+                "http-cache-semantics": "^4.1.1",
+                "http-proxy-agent": "^5.0.0",
+                "https-proxy-agent": "^5.0.0",
+                "is-lambda": "^1.0.1",
+                "lru-cache": "^7.7.1",
+                "minipass": "^5.0.0",
+                "minipass-fetch": "^3.0.0",
+                "minipass-flush": "^1.0.5",
+                "minipass-pipeline": "^1.2.4",
+                "negotiator": "^0.6.3",
+                "promise-retry": "^2.0.1",
+                "socks-proxy-agent": "^7.0.0",
+                "ssri": "^10.0.0"
             },
             "engines": {
-                "node": ">= 18"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/minimatch": {
-            "version": "3.1.2",
-            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+        "node_modules/make-fetch-happen/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+            "dev": true,
             "dependencies": {
-                "brace-expansion": "^1.1.7"
-            },
-            "engines": {
-                "node": "*"
+                "balanced-match": "^1.0.0"
             }
         },
-        "node_modules/minipass": {
-            "version": "3.3.6",
-            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
-            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+        "node_modules/make-fetch-happen/node_modules/cacache": {
+            "version": "17.1.4",
+            "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz",
+            "integrity": "sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==",
             "dev": true,
             "dependencies": {
-                "yallist": "^4.0.0"
+                "@npmcli/fs": "^3.1.0",
+                "fs-minipass": "^3.0.0",
+                "glob": "^10.2.2",
+                "lru-cache": "^7.7.1",
+                "minipass": "^7.0.3",
+                "minipass-collect": "^1.0.2",
+                "minipass-flush": "^1.0.5",
+                "minipass-pipeline": "^1.2.4",
+                "p-map": "^4.0.0",
+                "ssri": "^10.0.0",
+                "tar": "^6.1.11",
+                "unique-filename": "^3.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/mkdirp": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-            "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+        "node_modules/make-fetch-happen/node_modules/cacache/node_modules/minipass": {
+            "version": "7.0.4",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+            "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
             "dev": true,
-            "bin": {
-                "mkdirp": "bin/cmd.js"
-            },
             "engines": {
-                "node": ">=10"
+                "node": ">=16 || 14 >=14.17"
             }
         },
-        "node_modules/ms": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
-        },
-        "node_modules/natural-compare": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
-            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
-        },
-        "node_modules/node-preload": {
-            "version": "0.2.1",
-            "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
-            "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==",
+        "node_modules/make-fetch-happen/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
             "dev": true,
             "dependencies": {
-                "process-on-spawn": "^1.0.0"
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
+            },
+            "bin": {
+                "glob": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/node-releases": {
-            "version": "2.0.10",
-            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
-            "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
-            "dev": true
-        },
-        "node_modules/normalize-path": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-            "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+        "node_modules/make-fetch-happen/node_modules/lru-cache": {
+            "version": "7.18.3",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+            "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
             "dev": true,
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=12"
             }
         },
-        "node_modules/nyc": {
-            "version": "15.1.0",
-            "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
-            "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==",
+        "node_modules/make-fetch-happen/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
             "dev": true,
             "dependencies": {
-                "@istanbuljs/load-nyc-config": "^1.0.0",
-                "@istanbuljs/schema": "^0.1.2",
-                "caching-transform": "^4.0.0",
-                "convert-source-map": "^1.7.0",
-                "decamelize": "^1.2.0",
-                "find-cache-dir": "^3.2.0",
-                "find-up": "^4.1.0",
-                "foreground-child": "^2.0.0",
-                "get-package-type": "^0.1.0",
-                "glob": "^7.1.6",
-                "istanbul-lib-coverage": "^3.0.0",
-                "istanbul-lib-hook": "^3.0.0",
-                "istanbul-lib-instrument": "^4.0.0",
-                "istanbul-lib-processinfo": "^2.0.2",
-                "istanbul-lib-report": "^3.0.0",
-                "istanbul-lib-source-maps": "^4.0.0",
-                "istanbul-reports": "^3.0.2",
-                "make-dir": "^3.0.0",
-                "node-preload": "^0.2.1",
-                "p-map": "^3.0.0",
-                "process-on-spawn": "^1.0.0",
-                "resolve-from": "^5.0.0",
-                "rimraf": "^3.0.0",
-                "signal-exit": "^3.0.2",
-                "spawn-wrap": "^2.0.0",
-                "test-exclude": "^6.0.0",
-                "yargs": "^15.0.2"
-            },
-            "bin": {
-                "nyc": "bin/nyc.js"
+                "brace-expansion": "^2.0.1"
             },
             "engines": {
-                "node": ">=8.9"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/nyc/node_modules/resolve-from": {
+        "node_modules/make-fetch-happen/node_modules/minipass": {
             "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
-            "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+            "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
             "dev": true,
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/once": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-            "dependencies": {
-                "wrappy": "1"
+        "node_modules/marked": {
+            "version": "5.0.2",
+            "resolved": "https://registry.npmjs.org/marked/-/marked-5.0.2.tgz",
+            "integrity": "sha512-TXksm9GwqXCRNbFUZmMtqNLvy3K2cQHuWmyBDLOrY1e6i9UvZpOTJXoz7fBjYkJkaUFzV9hBFxMuZSyQt8R6KQ==",
+            "bin": {
+                "marked": "bin/marked.js"
+            },
+            "engines": {
+                "node": ">= 18"
             }
         },
-        "node_modules/opener": {
-            "version": "1.5.2",
-            "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
-            "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+        "node_modules/mimic-fn": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
             "dev": true,
-            "bin": {
-                "opener": "bin/opener-bin.js"
+            "engines": {
+                "node": ">=6"
             }
         },
-        "node_modules/optionator": {
-            "version": "0.9.1",
-            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
-            "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+        "node_modules/minimatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
             "dependencies": {
-                "deep-is": "^0.1.3",
-                "fast-levenshtein": "^2.0.6",
-                "levn": "^0.4.1",
-                "prelude-ls": "^1.2.1",
-                "type-check": "^0.4.0",
-                "word-wrap": "^1.2.3"
+                "brace-expansion": "^1.1.7"
             },
             "engines": {
-                "node": ">= 0.8.0"
+                "node": "*"
             }
         },
-        "node_modules/own-or": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz",
-            "integrity": "sha512-NfZr5+Tdf6MB8UI9GLvKRs4cXY8/yB0w3xtt84xFdWy8hkGjn+JFc60VhzS/hFRfbyxFcGYMTjnF4Me+RbbqrA==",
-            "dev": true
-        },
-        "node_modules/own-or-env": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.2.tgz",
-            "integrity": "sha512-NQ7v0fliWtK7Lkb+WdFqe6ky9XAzYmlkXthQrBbzlYbmFKoAYbDDcwmOm6q8kOuwSRXW8bdL5ORksploUJmWgw==",
+        "node_modules/minipass": {
+            "version": "7.0.4",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+            "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
             "dev": true,
-            "dependencies": {
-                "own-or": "^1.0.0"
+            "engines": {
+                "node": ">=16 || 14 >=14.17"
             }
         },
-        "node_modules/p-limit": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-            "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+        "node_modules/minipass-collect": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+            "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
             "dev": true,
             "dependencies": {
-                "p-try": "^2.0.0"
+                "minipass": "^3.0.0"
             },
             "engines": {
-                "node": ">=6"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "node": ">= 8"
             }
         },
-        "node_modules/p-locate": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-            "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+        "node_modules/minipass-collect/node_modules/minipass": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
             "dev": true,
             "dependencies": {
-                "p-limit": "^2.2.0"
+                "yallist": "^4.0.0"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/p-map": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
-            "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+        "node_modules/minipass-fetch": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz",
+            "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==",
             "dev": true,
             "dependencies": {
-                "aggregate-error": "^3.0.0"
+                "minipass": "^7.0.3",
+                "minipass-sized": "^1.0.3",
+                "minizlib": "^2.1.2"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+            },
+            "optionalDependencies": {
+                "encoding": "^0.1.13"
             }
         },
-        "node_modules/p-try": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+        "node_modules/minipass-flush": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+            "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
             "dev": true,
+            "dependencies": {
+                "minipass": "^3.0.0"
+            },
             "engines": {
-                "node": ">=6"
+                "node": ">= 8"
             }
         },
-        "node_modules/package-hash": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz",
-            "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==",
+        "node_modules/minipass-flush/node_modules/minipass": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
             "dev": true,
             "dependencies": {
-                "graceful-fs": "^4.1.15",
-                "hasha": "^5.0.0",
-                "lodash.flattendeep": "^4.4.0",
-                "release-zalgo": "^1.0.0"
+                "yallist": "^4.0.0"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/parent-module": {
+        "node_modules/minipass-json-stream": {
             "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
-            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+            "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz",
+            "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==",
+            "dev": true,
             "dependencies": {
-                "callsites": "^3.0.0"
-            },
-            "engines": {
-                "node": ">=6"
+                "jsonparse": "^1.3.1",
+                "minipass": "^3.0.0"
             }
         },
-        "node_modules/path-exists": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-            "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+        "node_modules/minipass-json-stream/node_modules/minipass": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+            "dev": true,
+            "dependencies": {
+                "yallist": "^4.0.0"
+            },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/path-is-absolute": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/path-key": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
-            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+        "node_modules/minipass-pipeline": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+            "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+            "dev": true,
+            "dependencies": {
+                "minipass": "^3.0.0"
+            },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/picocolors": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-            "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
-            "dev": true
-        },
-        "node_modules/picomatch": {
-            "version": "2.3.1",
-            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+        "node_modules/minipass-pipeline/node_modules/minipass": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
             "dev": true,
-            "engines": {
-                "node": ">=8.6"
+            "dependencies": {
+                "yallist": "^4.0.0"
             },
-            "funding": {
-                "url": "https://github.com/sponsors/jonschlinkert"
+            "engines": {
+                "node": ">=8"
             }
         },
-        "node_modules/pkg-dir": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
-            "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+        "node_modules/minipass-sized": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
+            "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
             "dev": true,
             "dependencies": {
-                "find-up": "^4.0.0"
+                "minipass": "^3.0.0"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/prelude-ls": {
-            "version": "1.2.1",
-            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
-            "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
-            "engines": {
-                "node": ">= 0.8.0"
-            }
-        },
-        "node_modules/process-on-spawn": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz",
-            "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==",
+        "node_modules/minipass-sized/node_modules/minipass": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
             "dev": true,
             "dependencies": {
-                "fromentries": "^1.2.0"
+                "yallist": "^4.0.0"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/punycode": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-            "engines": {
-                "node": ">=6"
-            }
-        },
-        "node_modules/queue": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
-            "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
-            "dependencies": {
-                "inherits": "~2.0.3"
-            }
-        },
-        "node_modules/queue-microtask": {
-            "version": "1.2.3",
-            "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
-            "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
-            "funding": [
-                {
-                    "type": "github",
-                    "url": "https://github.com/sponsors/feross"
-                },
-                {
-                    "type": "patreon",
-                    "url": "https://www.patreon.com/feross"
-                },
-                {
-                    "type": "consulting",
-                    "url": "https://feross.org/support"
-                }
-            ]
-        },
-        "node_modules/readdirp": {
-            "version": "3.6.0",
-            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
-            "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+        "node_modules/minizlib": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+            "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
             "dev": true,
             "dependencies": {
-                "picomatch": "^2.2.1"
+                "minipass": "^3.0.0",
+                "yallist": "^4.0.0"
             },
             "engines": {
-                "node": ">=8.10.0"
+                "node": ">= 8"
             }
         },
-        "node_modules/release-zalgo": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
-            "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==",
+        "node_modules/minizlib/node_modules/minipass": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
             "dev": true,
             "dependencies": {
-                "es6-error": "^4.0.1"
+                "yallist": "^4.0.0"
             },
             "engines": {
-                "node": ">=4"
+                "node": ">=8"
             }
         },
-        "node_modules/require-directory": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+        "node_modules/mkdirp": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+            "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
             "dev": true,
+            "bin": {
+                "mkdirp": "dist/cjs/src/bin.js"
+            },
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/require-main-filename": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-            "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-            "dev": true
+        "node_modules/ms": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
         },
-        "node_modules/resolve-from": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
-            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-            "engines": {
-                "node": ">=4"
-            }
+        "node_modules/natural-compare": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
         },
-        "node_modules/reusify": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-            "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+        "node_modules/negotiator": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+            "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+            "dev": true,
             "engines": {
-                "iojs": ">=1.0.0",
-                "node": ">=0.10.0"
+                "node": ">= 0.6"
             }
         },
-        "node_modules/rimraf": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-            "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+        "node_modules/node-gyp": {
+            "version": "9.4.0",
+            "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz",
+            "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==",
+            "dev": true,
             "dependencies": {
-                "glob": "^7.1.3"
+                "env-paths": "^2.2.0",
+                "exponential-backoff": "^3.1.1",
+                "glob": "^7.1.4",
+                "graceful-fs": "^4.2.6",
+                "make-fetch-happen": "^11.0.3",
+                "nopt": "^6.0.0",
+                "npmlog": "^6.0.0",
+                "rimraf": "^3.0.2",
+                "semver": "^7.3.5",
+                "tar": "^6.1.2",
+                "which": "^2.0.2"
             },
             "bin": {
-                "rimraf": "bin.js"
+                "node-gyp": "bin/node-gyp.js"
             },
-            "funding": {
-                "url": "https://github.com/sponsors/isaacs"
-            }
-        },
-        "node_modules/run-parallel": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
-            "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-            "funding": [
-                {
-                    "type": "github",
-                    "url": "https://github.com/sponsors/feross"
-                },
-                {
-                    "type": "patreon",
-                    "url": "https://www.patreon.com/feross"
-                },
-                {
-                    "type": "consulting",
-                    "url": "https://feross.org/support"
-                }
-            ],
-            "dependencies": {
-                "queue-microtask": "^1.2.2"
+            "engines": {
+                "node": "^12.13 || ^14.13 || >=16"
             }
         },
-        "node_modules/semver": {
-            "version": "6.3.0",
-            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+        "node_modules/nopt": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz",
+            "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==",
             "dev": true,
+            "dependencies": {
+                "abbrev": "^1.0.0"
+            },
             "bin": {
-                "semver": "bin/semver.js"
+                "nopt": "bin/nopt.js"
+            },
+            "engines": {
+                "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
             }
         },
-        "node_modules/set-blocking": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-            "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
-            "dev": true
-        },
-        "node_modules/shebang-command": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
-            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+        "node_modules/normalize-package-data": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz",
+            "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==",
+            "dev": true,
             "dependencies": {
-                "shebang-regex": "^3.0.0"
+                "hosted-git-info": "^7.0.0",
+                "is-core-module": "^2.8.1",
+                "semver": "^7.3.5",
+                "validate-npm-package-license": "^3.0.4"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/shebang-regex": {
+        "node_modules/normalize-path": {
             "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/signal-exit": {
-            "version": "3.0.7",
-            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-            "dev": true
-        },
-        "node_modules/source-map": {
-            "version": "0.6.1",
-            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+            "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
             "dev": true,
             "engines": {
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/source-map-support": {
-            "version": "0.5.21",
-            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
-            "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
-            "dev": true,
-            "dependencies": {
-                "buffer-from": "^1.0.0",
-                "source-map": "^0.6.0"
-            }
-        },
-        "node_modules/spawn-wrap": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
-            "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==",
+        "node_modules/npm-bundled": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz",
+            "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==",
             "dev": true,
             "dependencies": {
-                "foreground-child": "^2.0.0",
-                "is-windows": "^1.0.2",
-                "make-dir": "^3.0.0",
-                "rimraf": "^3.0.0",
-                "signal-exit": "^3.0.2",
-                "which": "^2.0.1"
+                "npm-normalize-package-bin": "^3.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/sprintf-js": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-            "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
-            "dev": true
-        },
-        "node_modules/stack-utils": {
-            "version": "2.0.6",
-            "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
-            "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+        "node_modules/npm-install-checks": {
+            "version": "6.2.0",
+            "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.2.0.tgz",
+            "integrity": "sha512-744wat5wAAHsxa4590mWO0tJ8PKxR8ORZsH9wGpQc3nWTzozMAgBN/XyqYw7mg3yqLM8dLwEnwSfKMmXAjF69g==",
             "dev": true,
             "dependencies": {
-                "escape-string-regexp": "^2.0.0"
+                "semver": "^7.1.1"
             },
             "engines": {
-                "node": ">=10"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/stack-utils/node_modules/escape-string-regexp": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
-            "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+        "node_modules/npm-normalize-package-bin": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz",
+            "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==",
             "dev": true,
             "engines": {
-                "node": ">=8"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/string-width": {
-            "version": "4.2.3",
-            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+        "node_modules/npm-package-arg": {
+            "version": "11.0.1",
+            "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz",
+            "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==",
             "dev": true,
             "dependencies": {
-                "emoji-regex": "^8.0.0",
-                "is-fullwidth-code-point": "^3.0.0",
-                "strip-ansi": "^6.0.1"
+                "hosted-git-info": "^7.0.0",
+                "proc-log": "^3.0.0",
+                "semver": "^7.3.5",
+                "validate-npm-package-name": "^5.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/strip-ansi": {
-            "version": "6.0.1",
-            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+        "node_modules/npm-packlist": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.0.tgz",
+            "integrity": "sha512-ErAGFB5kJUciPy1mmx/C2YFbvxoJ0QJ9uwkCZOeR6CqLLISPZBOiFModAbSXnjjlwW5lOhuhXva+fURsSGJqyw==",
+            "dev": true,
             "dependencies": {
-                "ansi-regex": "^5.0.1"
+                "ignore-walk": "^6.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/strip-bom": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
-            "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+        "node_modules/npm-pick-manifest": {
+            "version": "9.0.0",
+            "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz",
+            "integrity": "sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==",
             "dev": true,
-            "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/strip-json-comments": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
-            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-            "engines": {
-                "node": ">=8"
+            "dependencies": {
+                "npm-install-checks": "^6.0.0",
+                "npm-normalize-package-bin": "^3.0.0",
+                "npm-package-arg": "^11.0.0",
+                "semver": "^7.3.5"
             },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+            "engines": {
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/striptags": {
-            "version": "4.0.0-alpha.4",
-            "resolved": "https://registry.npmjs.org/striptags/-/striptags-4.0.0-alpha.4.tgz",
-            "integrity": "sha512-/0jWyVWhpg9ciRHfjKYBpMHXct/HrFRfsR2HU77nGPbc8SPcVSIHZlZR/0TG3MyPq2C+HiHuwx8BlbcdI/cNbw=="
-        },
-        "node_modules/supports-color": {
-            "version": "7.2.0",
-            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-            "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+        "node_modules/npm-registry-fetch": {
+            "version": "16.0.0",
+            "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.0.0.tgz",
+            "integrity": "sha512-JFCpAPUpvpwfSydv99u85yhP68rNIxSFmDpNbNnRWKSe3gpjHnWL8v320gATwRzjtgmZ9Jfe37+ZPOLZPwz6BQ==",
+            "dev": true,
             "dependencies": {
-                "has-flag": "^4.0.0"
+                "make-fetch-happen": "^13.0.0",
+                "minipass": "^7.0.2",
+                "minipass-fetch": "^3.0.0",
+                "minipass-json-stream": "^1.0.1",
+                "minizlib": "^2.1.2",
+                "npm-package-arg": "^11.0.0",
+                "proc-log": "^3.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/tap": {
-            "version": "16.3.4",
-            "resolved": "https://registry.npmjs.org/tap/-/tap-16.3.4.tgz",
-            "integrity": "sha512-SAexdt2ZF4XBgye6TPucFI2y7VE0qeFXlXucJIV1XDPCs+iJodk0MYacr1zR6Ycltzz7PYg8zrblDXKbAZM2LQ==",
-            "bundleDependencies": [
-                "ink",
-                "treport",
-                "@types/react",
-                "@isaacs/import-jsx",
-                "react"
-            ],
+        "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": {
+            "version": "13.0.0",
+            "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz",
+            "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==",
             "dev": true,
             "dependencies": {
-                "@isaacs/import-jsx": "^4.0.1",
-                "@types/react": "^17.0.52",
-                "chokidar": "^3.3.0",
-                "findit": "^2.0.0",
-                "foreground-child": "^2.0.0",
-                "fs-exists-cached": "^1.0.0",
-                "glob": "^7.2.3",
-                "ink": "^3.2.0",
-                "isexe": "^2.0.0",
-                "istanbul-lib-processinfo": "^2.0.3",
-                "jackspeak": "^1.4.2",
-                "libtap": "^1.4.0",
-                "minipass": "^3.3.4",
-                "mkdirp": "^1.0.4",
-                "nyc": "^15.1.0",
-                "opener": "^1.5.1",
-                "react": "^17.0.2",
-                "rimraf": "^3.0.0",
-                "signal-exit": "^3.0.6",
-                "source-map-support": "^0.5.16",
-                "tap-mocha-reporter": "^5.0.3",
-                "tap-parser": "^11.0.2",
-                "tap-yaml": "^1.0.2",
-                "tcompare": "^5.0.7",
-                "treport": "^3.0.4",
-                "which": "^2.0.2"
-            },
-            "bin": {
-                "tap": "bin/run.js"
+                "@npmcli/agent": "^2.0.0",
+                "cacache": "^18.0.0",
+                "http-cache-semantics": "^4.1.1",
+                "is-lambda": "^1.0.1",
+                "minipass": "^7.0.2",
+                "minipass-fetch": "^3.0.0",
+                "minipass-flush": "^1.0.5",
+                "minipass-pipeline": "^1.2.4",
+                "negotiator": "^0.6.3",
+                "promise-retry": "^2.0.1",
+                "ssri": "^10.0.0"
             },
             "engines": {
-                "node": ">=12"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/isaacs"
-            },
-            "peerDependencies": {
-                "coveralls": "^3.1.1",
-                "flow-remove-types": ">=2.112.0",
-                "ts-node": ">=8.5.2",
-                "typescript": ">=3.7.2"
-            },
-            "peerDependenciesMeta": {
-                "coveralls": {
-                    "optional": true
-                },
-                "flow-remove-types": {
-                    "optional": true
-                },
-                "ts-node": {
-                    "optional": true
-                },
-                "typescript": {
-                    "optional": true
-                }
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/tap-mocha-reporter": {
-            "version": "5.0.3",
-            "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.3.tgz",
-            "integrity": "sha512-6zlGkaV4J+XMRFkN0X+yuw6xHbE9jyCZ3WUKfw4KxMyRGOpYSRuuQTRJyWX88WWuLdVTuFbxzwXhXuS2XE6o0g==",
+        "node_modules/npmlog": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
+            "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
             "dev": true,
             "dependencies": {
-                "color-support": "^1.1.0",
-                "debug": "^4.1.1",
-                "diff": "^4.0.1",
-                "escape-string-regexp": "^2.0.0",
-                "glob": "^7.0.5",
-                "tap-parser": "^11.0.0",
-                "tap-yaml": "^1.0.0",
-                "unicode-length": "^2.0.2"
-            },
-            "bin": {
-                "tap-mocha-reporter": "index.js"
+                "are-we-there-yet": "^3.0.0",
+                "console-control-strings": "^1.1.0",
+                "gauge": "^4.0.3",
+                "set-blocking": "^2.0.0"
             },
             "engines": {
-                "node": ">= 8"
+                "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
             }
         },
-        "node_modules/tap-mocha-reporter/node_modules/escape-string-regexp": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
-            "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
-            "dev": true,
-            "engines": {
-                "node": ">=8"
+        "node_modules/once": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+            "dependencies": {
+                "wrappy": "1"
             }
         },
-        "node_modules/tap-parser": {
-            "version": "11.0.2",
-            "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-11.0.2.tgz",
-            "integrity": "sha512-6qGlC956rcORw+fg7Fv1iCRAY8/bU9UabUAhs3mXRH6eRmVZcNPLheSXCYaVaYeSwx5xa/1HXZb1537YSvwDZg==",
+        "node_modules/onetime": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+            "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
             "dev": true,
             "dependencies": {
-                "events-to-array": "^1.0.1",
-                "minipass": "^3.1.6",
-                "tap-yaml": "^1.0.0"
-            },
-            "bin": {
-                "tap-parser": "bin/cmd.js"
+                "mimic-fn": "^2.1.0"
             },
             "engines": {
-                "node": ">= 8"
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap-yaml": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.2.tgz",
-            "integrity": "sha512-GegASpuqBnRNdT1U+yuUPZ8rEU64pL35WPBpCISWwff4dErS2/438barz7WFJl4Nzh3Y05tfPidZnH+GaV1wMg==",
+        "node_modules/opener": {
+            "version": "1.5.2",
+            "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
+            "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
             "dev": true,
-            "dependencies": {
-                "yaml": "^1.10.2"
+            "bin": {
+                "opener": "bin/opener-bin.js"
             }
         },
-        "node_modules/tap/node_modules/@ampproject/remapping": {
-            "version": "2.1.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "Apache-2.0",
+        "node_modules/optionator": {
+            "version": "0.9.1",
+            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+            "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
             "dependencies": {
-                "@jridgewell/trace-mapping": "^0.3.0"
+                "deep-is": "^0.1.3",
+                "fast-levenshtein": "^2.0.6",
+                "levn": "^0.4.1",
+                "prelude-ls": "^1.2.1",
+                "type-check": "^0.4.0",
+                "word-wrap": "^1.2.3"
             },
             "engines": {
-                "node": ">=6.0.0"
+                "node": ">= 0.8.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/code-frame": {
-            "version": "7.16.7",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/p-limit": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+            "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
             "dependencies": {
-                "@babel/highlight": "^7.16.7"
+                "yocto-queue": "^0.1.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/@babel/compat-data": {
-            "version": "7.17.7",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/p-locate": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+            "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+            "dependencies": {
+                "p-limit": "^3.0.2"
+            },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/@babel/core": {
-            "version": "7.17.8",
+        "node_modules/p-map": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+            "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@ampproject/remapping": "^2.1.0",
-                "@babel/code-frame": "^7.16.7",
-                "@babel/generator": "^7.17.7",
-                "@babel/helper-compilation-targets": "^7.17.7",
-                "@babel/helper-module-transforms": "^7.17.7",
-                "@babel/helpers": "^7.17.8",
-                "@babel/parser": "^7.17.8",
-                "@babel/template": "^7.16.7",
-                "@babel/traverse": "^7.17.3",
-                "@babel/types": "^7.17.0",
-                "convert-source-map": "^1.7.0",
-                "debug": "^4.1.0",
-                "gensync": "^1.0.0-beta.2",
-                "json5": "^2.1.2",
-                "semver": "^6.3.0"
+                "aggregate-error": "^3.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=10"
             },
             "funding": {
-                "type": "opencollective",
-                "url": "https://opencollective.com/babel"
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/@babel/generator": {
-            "version": "7.17.7",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@babel/types": "^7.17.0",
-                "jsesc": "^2.5.1",
-                "source-map": "^0.5.0"
+        "node_modules/pacote": {
+            "version": "17.0.4",
+            "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.4.tgz",
+            "integrity": "sha512-eGdLHrV/g5b5MtD5cTPyss+JxOlaOloSMG3UwPMAvL8ywaLJ6beONPF40K4KKl/UI6q5hTKCJq5rCu8tkF+7Dg==",
+            "dev": true,
+            "dependencies": {
+                "@npmcli/git": "^5.0.0",
+                "@npmcli/installed-package-contents": "^2.0.1",
+                "@npmcli/promise-spawn": "^7.0.0",
+                "@npmcli/run-script": "^7.0.0",
+                "cacache": "^18.0.0",
+                "fs-minipass": "^3.0.0",
+                "minipass": "^7.0.2",
+                "npm-package-arg": "^11.0.0",
+                "npm-packlist": "^8.0.0",
+                "npm-pick-manifest": "^9.0.0",
+                "npm-registry-fetch": "^16.0.0",
+                "proc-log": "^3.0.0",
+                "promise-retry": "^2.0.1",
+                "read-package-json": "^7.0.0",
+                "read-package-json-fast": "^3.0.0",
+                "sigstore": "^2.0.0",
+                "ssri": "^10.0.0",
+                "tar": "^6.1.11"
+            },
+            "bin": {
+                "pacote": "lib/bin.js"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-annotate-as-pure": {
-            "version": "7.16.7",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/parent-module": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
             "dependencies": {
-                "@babel/types": "^7.16.7"
+                "callsites": "^3.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=6"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-compilation-targets": {
-            "version": "7.17.7",
+        "node_modules/patch-console": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz",
+            "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@babel/compat-data": "^7.17.7",
-                "@babel/helper-validator-option": "^7.16.7",
-                "browserslist": "^4.17.5",
-                "semver": "^6.3.0"
-            },
             "engines": {
-                "node": ">=6.9.0"
-            },
-            "peerDependencies": {
-                "@babel/core": "^7.0.0"
+                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-environment-visitor": {
-            "version": "7.16.7",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@babel/types": "^7.16.7"
-            },
+        "node_modules/path-exists": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+            "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-function-name": {
-            "version": "7.16.7",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@babel/helper-get-function-arity": "^7.16.7",
-                "@babel/template": "^7.16.7",
-                "@babel/types": "^7.16.7"
-            },
+        "node_modules/path-is-absolute": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=0.10.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-get-function-arity": {
-            "version": "7.16.7",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@babel/types": "^7.16.7"
-            },
+        "node_modules/path-key": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-hoist-variables": {
-            "version": "7.16.7",
+        "node_modules/path-scurry": {
+            "version": "1.10.1",
+            "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
+            "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/types": "^7.16.7"
+                "lru-cache": "^9.1.1 || ^10.0.0",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-module-imports": {
-            "version": "7.16.7",
+        "node_modules/picomatch": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@babel/types": "^7.16.7"
-            },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=8.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/jonschlinkert"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-module-transforms": {
-            "version": "7.17.7",
+        "node_modules/pirates": {
+            "version": "4.0.6",
+            "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+            "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@babel/helper-environment-visitor": "^7.16.7",
-                "@babel/helper-module-imports": "^7.16.7",
-                "@babel/helper-simple-access": "^7.17.7",
-                "@babel/helper-split-export-declaration": "^7.16.7",
-                "@babel/helper-validator-identifier": "^7.16.7",
-                "@babel/template": "^7.16.7",
-                "@babel/traverse": "^7.17.3",
-                "@babel/types": "^7.17.0"
-            },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">= 6"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-plugin-utils": {
-            "version": "7.16.7",
+        "node_modules/polite-json": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/polite-json/-/polite-json-4.0.1.tgz",
+            "integrity": "sha512-8LI5ZeCPBEb4uBbcYKNVwk4jgqNx1yHReWoW4H4uUihWlSqZsUDfSITrRhjliuPgxsNPFhNSudGO2Zu4cbWinQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/prelude-ls": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+            "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+            "engines": {
+                "node": ">= 0.8.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-simple-access": {
-            "version": "7.17.7",
+        "node_modules/prismjs": {
+            "version": "1.29.0",
+            "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
+            "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@babel/types": "^7.17.0"
-            },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=6"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-split-export-declaration": {
-            "version": "7.16.7",
+        "node_modules/prismjs-terminal": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/prismjs-terminal/-/prismjs-terminal-1.2.3.tgz",
+            "integrity": "sha512-xc0zuJ5FMqvW+DpiRkvxURlz98DdfDsZcFHdO699+oL+ykbFfgI7O4VDEgUyc07BSL2NHl3zdb8m/tZ/aaqUrw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/types": "^7.16.7"
+                "chalk": "^5.2.0",
+                "prismjs": "^1.29.0",
+                "string-length": "^6.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-validator-identifier": {
-            "version": "7.16.7",
+        "node_modules/prismjs-terminal/node_modules/chalk": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+            "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^12.17.0 || ^14.13 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/@babel/helper-validator-option": {
-            "version": "7.16.7",
+        "node_modules/proc-log": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
+            "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=6.9.0"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/helpers": {
-            "version": "7.17.8",
+        "node_modules/process-on-spawn": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz",
+            "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/template": "^7.16.7",
-                "@babel/traverse": "^7.17.3",
-                "@babel/types": "^7.17.0"
+                "fromentries": "^1.2.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/@babel/highlight": {
-            "version": "7.16.10",
+        "node_modules/promise-inflight": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+            "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+            "dev": true
+        },
+        "node_modules/promise-retry": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+            "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/helper-validator-identifier": "^7.16.7",
-                "chalk": "^2.0.0",
-                "js-tokens": "^4.0.0"
+                "err-code": "^2.0.2",
+                "retry": "^0.12.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=10"
             }
         },
-        "node_modules/tap/node_modules/@babel/parser": {
-            "version": "7.17.8",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "bin": {
-                "parser": "bin/babel-parser.js"
-            },
+        "node_modules/punycode": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
             "engines": {
-                "node": ">=6.0.0"
+                "node": ">=6"
+            }
+        },
+        "node_modules/queue": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
+            "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
+            "dependencies": {
+                "inherits": "~2.0.3"
             }
         },
-        "node_modules/tap/node_modules/@babel/plugin-proposal-object-rest-spread": {
-            "version": "7.17.3",
+        "node_modules/queue-microtask": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+            "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ]
+        },
+        "node_modules/react": {
+            "version": "18.2.0",
+            "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+            "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/compat-data": "^7.17.0",
-                "@babel/helper-compilation-targets": "^7.16.7",
-                "@babel/helper-plugin-utils": "^7.16.7",
-                "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-                "@babel/plugin-transform-parameters": "^7.16.7"
+                "loose-envify": "^1.1.0"
             },
             "engines": {
-                "node": ">=6.9.0"
-            },
-            "peerDependencies": {
-                "@babel/core": "^7.0.0-0"
+                "node": ">=0.10.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/plugin-syntax-jsx": {
-            "version": "7.16.7",
+        "node_modules/react-dom": {
+            "version": "18.2.0",
+            "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+            "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+            "peer": true,
             "dependencies": {
-                "@babel/helper-plugin-utils": "^7.16.7"
-            },
-            "engines": {
-                "node": ">=6.9.0"
+                "loose-envify": "^1.1.0",
+                "scheduler": "^0.23.0"
             },
             "peerDependencies": {
-                "@babel/core": "^7.0.0-0"
+                "react": "^18.2.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/plugin-syntax-object-rest-spread": {
-            "version": "7.8.3",
+        "node_modules/react-element-to-jsx-string": {
+            "version": "15.0.0",
+            "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz",
+            "integrity": "sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/helper-plugin-utils": "^7.8.0"
+                "@base2/pretty-print-object": "1.0.1",
+                "is-plain-object": "5.0.0",
+                "react-is": "18.1.0"
             },
             "peerDependencies": {
-                "@babel/core": "^7.0.0-0"
+                "react": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0",
+                "react-dom": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/plugin-transform-destructuring": {
-            "version": "7.17.7",
+        "node_modules/react-is": {
+            "version": "18.1.0",
+            "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz",
+            "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==",
+            "dev": true
+        },
+        "node_modules/react-reconciler": {
+            "version": "0.29.0",
+            "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.0.tgz",
+            "integrity": "sha512-wa0fGj7Zht1EYMRhKWwoo1H9GApxYLBuhoAuXN0TlltESAjDssB+Apf0T/DngVqaMyPypDmabL37vw/2aRM98Q==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/helper-plugin-utils": "^7.16.7"
+                "loose-envify": "^1.1.0",
+                "scheduler": "^0.23.0"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=0.10.0"
             },
             "peerDependencies": {
-                "@babel/core": "^7.0.0-0"
+                "react": "^18.2.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/plugin-transform-parameters": {
-            "version": "7.16.7",
+        "node_modules/read-package-json": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.0.tgz",
+            "integrity": "sha512-uL4Z10OKV4p6vbdvIXB+OzhInYtIozl/VxUBPgNkBuUi2DeRonnuspmaVAMcrkmfjKGNmRndyQAbE7/AmzGwFg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/helper-plugin-utils": "^7.16.7"
+                "glob": "^10.2.2",
+                "json-parse-even-better-errors": "^3.0.0",
+                "normalize-package-data": "^6.0.0",
+                "npm-normalize-package-bin": "^3.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
-            },
-            "peerDependencies": {
-                "@babel/core": "^7.0.0-0"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/plugin-transform-react-jsx": {
-            "version": "7.17.3",
+        "node_modules/read-package-json-fast": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz",
+            "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/helper-annotate-as-pure": "^7.16.7",
-                "@babel/helper-module-imports": "^7.16.7",
-                "@babel/helper-plugin-utils": "^7.16.7",
-                "@babel/plugin-syntax-jsx": "^7.16.7",
-                "@babel/types": "^7.17.0"
+                "json-parse-even-better-errors": "^3.0.0",
+                "npm-normalize-package-bin": "^3.0.0"
             },
             "engines": {
-                "node": ">=6.9.0"
-            },
-            "peerDependencies": {
-                "@babel/core": "^7.0.0-0"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+            }
+        },
+        "node_modules/read-package-json/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+            "dev": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0"
             }
         },
-        "node_modules/tap/node_modules/@babel/template": {
-            "version": "7.16.7",
+        "node_modules/read-package-json/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/code-frame": "^7.16.7",
-                "@babel/parser": "^7.16.7",
-                "@babel/types": "^7.16.7"
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
+            },
+            "bin": {
+                "glob": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/@babel/traverse": {
-            "version": "7.17.3",
+        "node_modules/read-package-json/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/code-frame": "^7.16.7",
-                "@babel/generator": "^7.17.3",
-                "@babel/helper-environment-visitor": "^7.16.7",
-                "@babel/helper-function-name": "^7.16.7",
-                "@babel/helper-hoist-variables": "^7.16.7",
-                "@babel/helper-split-export-declaration": "^7.16.7",
-                "@babel/parser": "^7.17.3",
-                "@babel/types": "^7.17.0",
-                "debug": "^4.1.0",
-                "globals": "^11.1.0"
+                "brace-expansion": "^2.0.1"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/@babel/types": {
-            "version": "7.17.0",
+        "node_modules/readable-stream": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+            "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/helper-validator-identifier": "^7.16.7",
-                "to-fast-properties": "^2.0.0"
+                "inherits": "^2.0.3",
+                "string_decoder": "^1.1.1",
+                "util-deprecate": "^1.0.1"
             },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">= 6"
             }
         },
-        "node_modules/tap/node_modules/@isaacs/import-jsx": {
-            "version": "4.0.1",
+        "node_modules/readdirp": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+            "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@babel/core": "^7.5.5",
-                "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
-                "@babel/plugin-transform-destructuring": "^7.5.0",
-                "@babel/plugin-transform-react-jsx": "^7.3.0",
-                "caller-path": "^3.0.1",
-                "find-cache-dir": "^3.2.0",
-                "make-dir": "^3.0.2",
-                "resolve-from": "^3.0.0",
-                "rimraf": "^3.0.0"
+                "picomatch": "^2.2.1"
             },
             "engines": {
-                "node": ">=10"
+                "node": ">=8.10.0"
             }
         },
-        "node_modules/tap/node_modules/@jridgewell/resolve-uri": {
-            "version": "3.0.5",
+        "node_modules/require-directory": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=6.0.0"
+                "node": ">=0.10.0"
             }
         },
-        "node_modules/tap/node_modules/@jridgewell/sourcemap-codec": {
-            "version": "1.4.11",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
+        "node_modules/resolve-from": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+            "engines": {
+                "node": ">=4"
+            }
         },
-        "node_modules/tap/node_modules/@jridgewell/trace-mapping": {
-            "version": "0.3.4",
+        "node_modules/resolve-import": {
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/resolve-import/-/resolve-import-1.4.2.tgz",
+            "integrity": "sha512-ayUU3E2yeFu8ZewNEHbGorcPmHjOmCY8b50wloum8eQUuNExSyddRoWYaX0X6lj3XSufi2WUlXY3mkMcF5ISmw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@jridgewell/resolve-uri": "^3.0.3",
-                "@jridgewell/sourcemap-codec": "^1.4.10"
+                "glob": "^10.3.3",
+                "walk-up-path": "^3.0.1"
+            },
+            "engines": {
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/@types/prop-types": {
-            "version": "15.7.4",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/@types/react": {
-            "version": "17.0.52",
+        "node_modules/resolve-import/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "@types/prop-types": "*",
-                "@types/scheduler": "*",
-                "csstype": "^3.0.2"
+                "balanced-match": "^1.0.0"
             }
         },
-        "node_modules/tap/node_modules/@types/scheduler": {
-            "version": "0.16.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/@types/yoga-layout": {
-            "version": "1.9.2",
+        "node_modules/resolve-import/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/ansi-escapes": {
-            "version": "4.3.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "type-fest": "^0.21.3"
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
+            },
+            "bin": {
+                "glob": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=16 || 14 >=14.17"
             },
             "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/ansi-escapes/node_modules/type-fest": {
-            "version": "0.21.3",
+        "node_modules/resolve-import/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
             "dev": true,
-            "inBundle": true,
-            "license": "(MIT OR CC0-1.0)",
+            "dependencies": {
+                "brace-expansion": "^2.0.1"
+            },
             "engines": {
-                "node": ">=10"
+                "node": ">=16 || 14 >=14.17"
             },
             "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        },
-        "node_modules/tap/node_modules/ansi-regex": {
-            "version": "5.0.1",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "engines": {
-                "node": ">=8"
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/ansi-styles": {
-            "version": "3.2.1",
+        "node_modules/restore-cursor": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
+            "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-convert": "^1.9.0"
+                "onetime": "^5.1.0",
+                "signal-exit": "^3.0.2"
             },
             "engines": {
-                "node": ">=4"
+                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/ansicolors": {
-            "version": "0.3.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
+        "node_modules/restore-cursor/node_modules/signal-exit": {
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+            "dev": true
         },
-        "node_modules/tap/node_modules/astral-regex": {
-            "version": "2.0.0",
+        "node_modules/retry": {
+            "version": "0.12.0",
+            "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+            "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=8"
+                "node": ">= 4"
             }
         },
-        "node_modules/tap/node_modules/auto-bind": {
-            "version": "4.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/reusify": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+            "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
             "engines": {
-                "node": ">=8"
+                "iojs": ">=1.0.0",
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/rimraf": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+            "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
             },
             "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/balanced-match": {
-            "version": "1.0.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/brace-expansion": {
-            "version": "1.1.11",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/run-parallel": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+            "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ],
             "dependencies": {
-                "balanced-match": "^1.0.0",
-                "concat-map": "0.0.1"
+                "queue-microtask": "^1.2.2"
             }
         },
-        "node_modules/tap/node_modules/browserslist": {
-            "version": "4.20.2",
+        "node_modules/safe-buffer": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
             "dev": true,
             "funding": [
                 {
-                    "type": "opencollective",
-                    "url": "https://opencollective.com/browserslist"
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
                 },
                 {
-                    "type": "tidelift",
-                    "url": "https://tidelift.com/funding/github/npm/browserslist"
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
                 }
-            ],
-            "inBundle": true,
-            "license": "MIT",
+            ]
+        },
+        "node_modules/safer-buffer": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+            "dev": true,
+            "optional": true
+        },
+        "node_modules/scheduler": {
+            "version": "0.23.0",
+            "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
+            "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+            "dev": true,
             "dependencies": {
-                "caniuse-lite": "^1.0.30001317",
-                "electron-to-chromium": "^1.4.84",
-                "escalade": "^3.1.1",
-                "node-releases": "^2.0.2",
-                "picocolors": "^1.0.0"
+                "loose-envify": "^1.1.0"
+            }
+        },
+        "node_modules/semver": {
+            "version": "7.5.4",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+            "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+            "dev": true,
+            "dependencies": {
+                "lru-cache": "^6.0.0"
             },
             "bin": {
-                "browserslist": "cli.js"
+                "semver": "bin/semver.js"
             },
             "engines": {
-                "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+                "node": ">=10"
             }
         },
-        "node_modules/tap/node_modules/caller-callsite": {
-            "version": "4.1.0",
+        "node_modules/semver/node_modules/lru-cache": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+            "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "callsites": "^3.1.0"
+                "yallist": "^4.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=10"
             }
         },
-        "node_modules/tap/node_modules/caller-path": {
-            "version": "3.0.1",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/set-blocking": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+            "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+            "dev": true
+        },
+        "node_modules/shebang-command": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
             "dependencies": {
-                "caller-callsite": "^4.1.0"
+                "shebang-regex": "^3.0.0"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/callsites": {
-            "version": "3.1.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/shebang-regex": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
             "engines": {
-                "node": ">=6"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/caniuse-lite": {
-            "version": "1.0.30001319",
+        "node_modules/signal-exit": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+            "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
             "dev": true,
-            "funding": [
-                {
-                    "type": "opencollective",
-                    "url": "https://opencollective.com/browserslist"
-                },
-                {
-                    "type": "tidelift",
-                    "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
-                }
-            ],
-            "inBundle": true,
-            "license": "CC-BY-4.0"
+            "engines": {
+                "node": ">=14"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
         },
-        "node_modules/tap/node_modules/cardinal": {
-            "version": "2.1.1",
+        "node_modules/sigstore": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.1.0.tgz",
+            "integrity": "sha512-kPIj+ZLkyI3QaM0qX8V/nSsweYND3W448pwkDgS6CQ74MfhEkIR8ToK5Iyx46KJYRjseVcD3Rp9zAmUAj6ZjPw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "ansicolors": "~0.3.2",
-                "redeyed": "~2.1.0"
+                "@sigstore/bundle": "^2.1.0",
+                "@sigstore/protobuf-specs": "^0.2.1",
+                "@sigstore/sign": "^2.1.0",
+                "@sigstore/tuf": "^2.1.0"
             },
-            "bin": {
-                "cdl": "bin/cdl.js"
+            "engines": {
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/chalk": {
-            "version": "2.4.2",
+        "node_modules/slice-ansi": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-6.0.0.tgz",
+            "integrity": "sha512-6bn4hRfkTvDfUoEQYkERg0BVF1D0vrX9HEkMl08uDiNWvVvjylLHvZFZWkDo6wjT8tUctbYl1nCOuE66ZTaUtA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "ansi-styles": "^3.2.1",
-                "escape-string-regexp": "^1.0.5",
-                "supports-color": "^5.3.0"
+                "ansi-styles": "^6.2.1",
+                "is-fullwidth-code-point": "^4.0.0"
             },
             "engines": {
-                "node": ">=4"
+                "node": ">=14.16"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/slice-ansi?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/ci-info": {
-            "version": "2.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/cli-boxes": {
-            "version": "2.2.1",
+        "node_modules/slice-ansi/node_modules/ansi-styles": {
+            "version": "6.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+            "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=6"
+                "node": ">=12"
             },
             "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/cli-cursor": {
-            "version": "3.1.0",
+        "node_modules/smart-buffer": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+            "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "restore-cursor": "^3.1.0"
-            },
             "engines": {
-                "node": ">=8"
+                "node": ">= 6.0.0",
+                "npm": ">= 3.0.0"
             }
         },
-        "node_modules/tap/node_modules/cli-truncate": {
-            "version": "2.1.0",
+        "node_modules/socks": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+            "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "slice-ansi": "^3.0.0",
-                "string-width": "^4.2.0"
+                "ip": "^2.0.0",
+                "smart-buffer": "^4.2.0"
             },
             "engines": {
-                "node": ">=8"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "node": ">= 10.13.0",
+                "npm": ">= 3.0.0"
             }
         },
-        "node_modules/tap/node_modules/code-excerpt": {
-            "version": "3.0.0",
+        "node_modules/socks-proxy-agent": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
+            "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "convert-to-spaces": "^1.0.1"
+                "agent-base": "^6.0.2",
+                "debug": "^4.3.3",
+                "socks": "^2.6.2"
             },
             "engines": {
-                "node": ">=10"
+                "node": ">= 10"
             }
         },
-        "node_modules/tap/node_modules/color-convert": {
-            "version": "1.9.3",
+        "node_modules/spdx-correct": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+            "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-name": "1.1.3"
+                "spdx-expression-parse": "^3.0.0",
+                "spdx-license-ids": "^3.0.0"
             }
         },
-        "node_modules/tap/node_modules/color-name": {
-            "version": "1.1.3",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/commondir": {
-            "version": "1.0.1",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/concat-map": {
-            "version": "0.0.1",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
+        "node_modules/spdx-exceptions": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+            "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+            "dev": true
         },
-        "node_modules/tap/node_modules/convert-source-map": {
-            "version": "1.8.0",
+        "node_modules/spdx-expression-parse": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+            "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "safe-buffer": "~5.1.1"
+                "spdx-exceptions": "^2.1.0",
+                "spdx-license-ids": "^3.0.0"
             }
         },
-        "node_modules/tap/node_modules/convert-to-spaces": {
-            "version": "1.0.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "engines": {
-                "node": ">= 4"
-            }
-        },
-        "node_modules/tap/node_modules/csstype": {
-            "version": "3.0.11",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
+        "node_modules/spdx-license-ids": {
+            "version": "3.0.15",
+            "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.15.tgz",
+            "integrity": "sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==",
+            "dev": true
         },
-        "node_modules/tap/node_modules/debug": {
-            "version": "4.3.4",
+        "node_modules/ssri": {
+            "version": "10.0.5",
+            "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz",
+            "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "ms": "2.1.2"
+                "minipass": "^7.0.3"
             },
             "engines": {
-                "node": ">=6.0"
-            },
-            "peerDependenciesMeta": {
-                "supports-color": {
-                    "optional": true
-                }
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/electron-to-chromium": {
-            "version": "1.4.89",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC"
-        },
-        "node_modules/tap/node_modules/emoji-regex": {
-            "version": "8.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/escalade": {
-            "version": "3.1.1",
+        "node_modules/stack-utils": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
+            "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+            "dependencies": {
+                "escape-string-regexp": "^2.0.0"
+            },
             "engines": {
-                "node": ">=6"
+                "node": ">=10"
             }
         },
-        "node_modules/tap/node_modules/escape-string-regexp": {
-            "version": "1.0.5",
+        "node_modules/stack-utils/node_modules/escape-string-regexp": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+            "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=0.8.0"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/esprima": {
-            "version": "4.0.1",
+        "node_modules/string_decoder": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+            "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
             "dev": true,
-            "inBundle": true,
-            "license": "BSD-2-Clause",
-            "bin": {
-                "esparse": "bin/esparse.js",
-                "esvalidate": "bin/esvalidate.js"
-            },
-            "engines": {
-                "node": ">=4"
+            "dependencies": {
+                "safe-buffer": "~5.2.0"
             }
         },
-        "node_modules/tap/node_modules/events-to-array": {
-            "version": "1.1.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC"
-        },
-        "node_modules/tap/node_modules/find-cache-dir": {
-            "version": "3.3.2",
+        "node_modules/string-length": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/string-length/-/string-length-6.0.0.tgz",
+            "integrity": "sha512-1U361pxZHEQ+FeSjzqRpV+cu2vTzYeWeafXFLykiFlv4Vc0n3njgU8HrMbyik5uwm77naWMuVG8fhEF+Ovb1Kg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "commondir": "^1.0.1",
-                "make-dir": "^3.0.2",
-                "pkg-dir": "^4.1.0"
+                "strip-ansi": "^7.1.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=16"
             },
             "funding": {
-                "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/find-up": {
-            "version": "4.1.0",
+        "node_modules/string-length/node_modules/ansi-regex": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+            "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "locate-path": "^5.0.0",
-                "path-exists": "^4.0.0"
-            },
             "engines": {
-                "node": ">=8"
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-regex?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/fs.realpath": {
-            "version": "1.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC"
-        },
-        "node_modules/tap/node_modules/gensync": {
-            "version": "1.0.0-beta.2",
+        "node_modules/string-length/node_modules/strip-ansi": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+            "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+            "dependencies": {
+                "ansi-regex": "^6.0.1"
+            },
             "engines": {
-                "node": ">=6.9.0"
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/strip-ansi?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/glob": {
-            "version": "7.2.3",
+        "node_modules/string-width": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+            "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
             "dev": true,
-            "inBundle": true,
-            "license": "ISC",
             "dependencies": {
-                "fs.realpath": "^1.0.0",
-                "inflight": "^1.0.4",
-                "inherits": "2",
-                "minimatch": "^3.1.1",
-                "once": "^1.3.0",
-                "path-is-absolute": "^1.0.0"
+                "eastasianwidth": "^0.2.0",
+                "emoji-regex": "^9.2.2",
+                "strip-ansi": "^7.0.1"
             },
             "engines": {
-                "node": "*"
+                "node": ">=12"
             },
             "funding": {
-                "url": "https://github.com/sponsors/isaacs"
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/globals": {
-            "version": "11.12.0",
+        "node_modules/string-width-cjs": {
+            "name": "string-width",
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+            "dependencies": {
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
+            },
             "engines": {
-                "node": ">=4"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/has-flag": {
-            "version": "3.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "engines": {
-                "node": ">=4"
-            }
+        "node_modules/string-width-cjs/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "dev": true
         },
-        "node_modules/tap/node_modules/indent-string": {
-            "version": "4.0.0",
+        "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/inflight": {
-            "version": "1.0.6",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC",
-            "dependencies": {
-                "once": "^1.3.0",
-                "wrappy": "1"
-            }
-        },
-        "node_modules/tap/node_modules/inherits": {
-            "version": "2.0.4",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC"
-        },
-        "node_modules/tap/node_modules/ink": {
-            "version": "3.2.0",
+        "node_modules/string-width/node_modules/ansi-regex": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+            "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "ansi-escapes": "^4.2.1",
-                "auto-bind": "4.0.0",
-                "chalk": "^4.1.0",
-                "cli-boxes": "^2.2.0",
-                "cli-cursor": "^3.1.0",
-                "cli-truncate": "^2.1.0",
-                "code-excerpt": "^3.0.0",
-                "indent-string": "^4.0.0",
-                "is-ci": "^2.0.0",
-                "lodash": "^4.17.20",
-                "patch-console": "^1.0.0",
-                "react-devtools-core": "^4.19.1",
-                "react-reconciler": "^0.26.2",
-                "scheduler": "^0.20.2",
-                "signal-exit": "^3.0.2",
-                "slice-ansi": "^3.0.0",
-                "stack-utils": "^2.0.2",
-                "string-width": "^4.2.2",
-                "type-fest": "^0.12.0",
-                "widest-line": "^3.1.0",
-                "wrap-ansi": "^6.2.0",
-                "ws": "^7.5.5",
-                "yoga-layout-prebuilt": "^1.9.6"
-            },
             "engines": {
-                "node": ">=10"
-            },
-            "peerDependencies": {
-                "@types/react": ">=16.8.0",
-                "react": ">=16.8.0"
+                "node": ">=12"
             },
-            "peerDependenciesMeta": {
-                "@types/react": {
-                    "optional": true
-                }
+            "funding": {
+                "url": "https://github.com/chalk/ansi-regex?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/ink/node_modules/ansi-styles": {
-            "version": "4.3.0",
+        "node_modules/string-width/node_modules/strip-ansi": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+            "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-convert": "^2.0.1"
+                "ansi-regex": "^6.0.1"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=12"
             },
             "funding": {
-                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+                "url": "https://github.com/chalk/strip-ansi?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/ink/node_modules/chalk": {
-            "version": "4.1.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/strip-ansi": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
             "dependencies": {
-                "ansi-styles": "^4.1.0",
-                "supports-color": "^7.1.0"
+                "ansi-regex": "^5.0.1"
             },
             "engines": {
-                "node": ">=10"
-            },
-            "funding": {
-                "url": "https://github.com/chalk/chalk?sponsor=1"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/ink/node_modules/color-convert": {
-            "version": "2.0.1",
+        "node_modules/strip-ansi-cjs": {
+            "name": "strip-ansi",
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-name": "~1.1.4"
+                "ansi-regex": "^5.0.1"
             },
             "engines": {
-                "node": ">=7.0.0"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/ink/node_modules/color-name": {
-            "version": "1.1.4",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/ink/node_modules/has-flag": {
-            "version": "4.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/strip-json-comments": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+            "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
             "engines": {
                 "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/ink/node_modules/supports-color": {
+        "node_modules/striptags": {
+            "version": "4.0.0-alpha.4",
+            "resolved": "https://registry.npmjs.org/striptags/-/striptags-4.0.0-alpha.4.tgz",
+            "integrity": "sha512-/0jWyVWhpg9ciRHfjKYBpMHXct/HrFRfsR2HU77nGPbc8SPcVSIHZlZR/0TG3MyPq2C+HiHuwx8BlbcdI/cNbw=="
+        },
+        "node_modules/supports-color": {
             "version": "7.2.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+            "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
             "dependencies": {
                 "has-flag": "^4.0.0"
             },
@@ -3887,760 +4807,794 @@
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/is-ci": {
-            "version": "2.0.0",
+        "node_modules/sync-content": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/sync-content/-/sync-content-1.0.2.tgz",
+            "integrity": "sha512-znd3rYiiSxU3WteWyS9a6FXkTA/Wjk8WQsOyzHbineeL837dLn3DA4MRhsIX3qGcxDMH6+uuFV4axztssk7wEQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "ci-info": "^2.0.0"
+                "glob": "^10.2.6",
+                "mkdirp": "^3.0.1",
+                "path-scurry": "^1.9.2",
+                "rimraf": "^5.0.1"
             },
             "bin": {
-                "is-ci": "bin.js"
-            }
-        },
-        "node_modules/tap/node_modules/is-fullwidth-code-point": {
-            "version": "3.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+                "sync-content": "dist/mjs/bin.mjs"
+            },
             "engines": {
-                "node": ">=8"
+                "node": ">=14"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/js-tokens": {
-            "version": "4.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/jsesc": {
-            "version": "2.5.2",
+        "node_modules/sync-content/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "bin": {
-                "jsesc": "bin/jsesc"
-            },
-            "engines": {
-                "node": ">=4"
+            "dependencies": {
+                "balanced-match": "^1.0.0"
             }
         },
-        "node_modules/tap/node_modules/json5": {
-            "version": "2.2.3",
+        "node_modules/sync-content/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+            "dependencies": {
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
+            },
             "bin": {
-                "json5": "lib/cli.js"
+                "glob": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">=6"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/locate-path": {
-            "version": "5.0.0",
+        "node_modules/sync-content/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "p-locate": "^4.1.0"
+                "brace-expansion": "^2.0.1"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/lodash": {
-            "version": "4.17.21",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/loose-envify": {
-            "version": "1.4.0",
+        "node_modules/sync-content/node_modules/rimraf": {
+            "version": "5.0.5",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+            "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "js-tokens": "^3.0.0 || ^4.0.0"
+                "glob": "^10.3.7"
             },
             "bin": {
-                "loose-envify": "cli.js"
-            }
-        },
-        "node_modules/tap/node_modules/make-dir": {
-            "version": "3.1.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "semver": "^6.0.0"
+                "rimraf": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=14"
             },
             "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/mimic-fn": {
-            "version": "2.1.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/tap": {
+            "version": "18.4.0",
+            "resolved": "https://registry.npmjs.org/tap/-/tap-18.4.0.tgz",
+            "integrity": "sha512-42bqz0KpoDg8F6Gs5zrTVOELq5ShaK86rCsRG6C6uJM7nUANCB3GW9Dmvy3BGHRll4wAwr+SA+iM0tvBQtrilg==",
+            "dev": true,
+            "dependencies": {
+                "@tapjs/after": "1.1.4",
+                "@tapjs/after-each": "1.1.4",
+                "@tapjs/asserts": "1.1.4",
+                "@tapjs/before": "1.1.4",
+                "@tapjs/before-each": "1.1.4",
+                "@tapjs/core": "1.3.4",
+                "@tapjs/filter": "1.2.4",
+                "@tapjs/fixture": "1.2.4",
+                "@tapjs/intercept": "1.2.4",
+                "@tapjs/mock": "1.2.2",
+                "@tapjs/node-serialize": "1.1.4",
+                "@tapjs/run": "1.4.0",
+                "@tapjs/snapshot": "1.2.4",
+                "@tapjs/spawn": "1.1.4",
+                "@tapjs/stdin": "1.1.4",
+                "@tapjs/test": "1.3.4",
+                "@tapjs/typescript": "1.2.4",
+                "@tapjs/worker": "1.1.4"
+            },
+            "bin": {
+                "tap": "dist/esm/run.mjs"
+            },
             "engines": {
-                "node": ">=6"
+                "node": ">=16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/minimatch": {
-            "version": "3.1.2",
+        "node_modules/tap-parser": {
+            "version": "15.2.0",
+            "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-15.2.0.tgz",
+            "integrity": "sha512-bDBR7cuVLfsmmc7ruerZXVBlDtJwqqWzqlO9BFNgw6gprpzjnjyfdc+fsW6mNUYSoxdVEeY7NFgrgGa81EuQ5w==",
             "dev": true,
-            "inBundle": true,
-            "license": "ISC",
             "dependencies": {
-                "brace-expansion": "^1.1.7"
+                "events-to-array": "^2.0.3",
+                "tap-yaml": "2.2.0"
+            },
+            "bin": {
+                "tap-parser": "bin/cmd.cjs"
             },
             "engines": {
-                "node": "*"
+                "node": ">=16"
             }
         },
-        "node_modules/tap/node_modules/minipass": {
-            "version": "3.3.4",
+        "node_modules/tap-yaml": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-2.2.0.tgz",
+            "integrity": "sha512-o8I7WDNiGpuF04tGAVaNYY5rX9waCtqw9A7Y0YVSQBGcFwNUJWUPLkr2lbhgLRTxc+Tpnw4xUXlIanZc+ZAGnw==",
             "dev": true,
-            "inBundle": true,
-            "license": "ISC",
             "dependencies": {
-                "yallist": "^4.0.0"
+                "yaml": "^2.3.0",
+                "yaml-types": "^0.3.0"
             },
             "engines": {
-                "node": ">=8"
-            }
-        },
-        "node_modules/tap/node_modules/ms": {
-            "version": "2.1.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/node-releases": {
-            "version": "2.0.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/object-assign": {
-            "version": "4.1.1",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "engines": {
-                "node": ">=0.10.0"
-            }
-        },
-        "node_modules/tap/node_modules/once": {
-            "version": "1.4.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC",
-            "dependencies": {
-                "wrappy": "1"
+                "node": ">=16"
             }
         },
-        "node_modules/tap/node_modules/onetime": {
-            "version": "5.1.2",
+        "node_modules/tar": {
+            "version": "6.2.0",
+            "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
+            "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "mimic-fn": "^2.1.0"
+                "chownr": "^2.0.0",
+                "fs-minipass": "^2.0.0",
+                "minipass": "^5.0.0",
+                "minizlib": "^2.1.1",
+                "mkdirp": "^1.0.3",
+                "yallist": "^4.0.0"
             },
             "engines": {
-                "node": ">=6"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "node": ">=10"
             }
         },
-        "node_modules/tap/node_modules/p-limit": {
-            "version": "2.3.0",
+        "node_modules/tar/node_modules/fs-minipass": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+            "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "p-try": "^2.0.0"
+                "minipass": "^3.0.0"
             },
             "engines": {
-                "node": ">=6"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
+                "node": ">= 8"
             }
         },
-        "node_modules/tap/node_modules/p-locate": {
-            "version": "4.1.0",
+        "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": {
+            "version": "3.3.6",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "p-limit": "^2.2.0"
+                "yallist": "^4.0.0"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/p-try": {
-            "version": "2.2.0",
+        "node_modules/tar/node_modules/minipass": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+            "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=6"
+                "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/patch-console": {
-            "version": "1.0.0",
+        "node_modules/tar/node_modules/mkdirp": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+            "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+            "bin": {
+                "mkdirp": "bin/cmd.js"
+            },
             "engines": {
                 "node": ">=10"
             }
         },
-        "node_modules/tap/node_modules/path-exists": {
-            "version": "4.0.0",
+        "node_modules/tcompare": {
+            "version": "6.4.0",
+            "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-6.4.0.tgz",
+            "integrity": "sha512-MR0TPvFaEQ53jgMP43aHr3wKGKKPi6Th3nxHoIsBVL0AxjKdfyrIIWvYt7u30NNs57Vc6UP5ooq/sD69IhQPzw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+            "dependencies": {
+                "diff": "^5.1.0",
+                "react-element-to-jsx-string": "^15.0.0"
+            },
             "engines": {
-                "node": ">=8"
+                "node": ">=16"
             }
         },
-        "node_modules/tap/node_modules/path-is-absolute": {
-            "version": "1.0.1",
+        "node_modules/tcompare/node_modules/diff": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
+            "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=0.3.1"
             }
         },
-        "node_modules/tap/node_modules/picocolors": {
-            "version": "1.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC"
-        },
-        "node_modules/tap/node_modules/pkg-dir": {
-            "version": "4.2.0",
+        "node_modules/test-exclude": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+            "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "find-up": "^4.0.0"
+                "@istanbuljs/schema": "^0.1.2",
+                "glob": "^7.1.4",
+                "minimatch": "^3.0.4"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/punycode": {
-            "version": "2.1.1",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "engines": {
-                "node": ">=6"
-            }
+        "node_modules/text-table": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+            "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
         },
-        "node_modules/tap/node_modules/react": {
-            "version": "17.0.2",
+        "node_modules/to-regex-range": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+            "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "loose-envify": "^1.1.0",
-                "object-assign": "^4.1.1"
+                "is-number": "^7.0.0"
             },
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=8.0"
             }
         },
-        "node_modules/tap/node_modules/react-devtools-core": {
-            "version": "4.24.1",
+        "node_modules/trivial-deferred": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-2.0.0.tgz",
+            "integrity": "sha512-iGbM7X2slv9ORDVj2y2FFUq3cP/ypbtu2nQ8S38ufjL0glBABvmR9pTdsib1XtS2LUhhLMbelaBUaf/s5J3dSw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "shell-quote": "^1.6.1",
-                "ws": "^7"
+            "engines": {
+                "node": ">= 8"
             }
         },
-        "node_modules/tap/node_modules/react-reconciler": {
-            "version": "0.26.2",
+        "node_modules/ts-node": {
+            "name": "@isaacs/ts-node-temp-fork-for-pr-2009",
+            "version": "10.9.1",
+            "resolved": "https://registry.npmjs.org/@isaacs/ts-node-temp-fork-for-pr-2009/-/ts-node-temp-fork-for-pr-2009-10.9.1.tgz",
+            "integrity": "sha512-MY4rUonz835NsTbd4dcgKZvZFYX9IkLnYFZV9M7GQV8t39fawafLin/Qw6VXD4yfMs4HcBq8P3ddeU0QHMH1YQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "loose-envify": "^1.1.0",
-                "object-assign": "^4.1.1",
-                "scheduler": "^0.20.2"
+                "@cspotcode/source-map-support": "^0.8.0",
+                "@tsconfig/node14": "*",
+                "@tsconfig/node16": "*",
+                "@tsconfig/node18": "*",
+                "@tsconfig/node20": "*",
+                "acorn": "^8.4.1",
+                "acorn-walk": "^8.1.1",
+                "arg": "^4.1.0",
+                "diff": "^4.0.1",
+                "make-error": "^1.1.1",
+                "v8-compile-cache-lib": "^3.0.1"
             },
-            "engines": {
-                "node": ">=0.10.0"
+            "bin": {
+                "ts-node": "dist/bin.js",
+                "ts-node-cwd": "dist/bin-cwd.js",
+                "ts-node-esm": "dist/bin-esm.js",
+                "ts-node-script": "dist/bin-script.js",
+                "ts-node-transpile-only": "dist/bin-transpile.js"
             },
             "peerDependencies": {
-                "react": "^17.0.2"
+                "@swc/core": ">=1.2.50",
+                "@swc/wasm": ">=1.2.50",
+                "@types/node": "*",
+                "typescript": ">=4.2"
+            },
+            "peerDependenciesMeta": {
+                "@swc/core": {
+                    "optional": true
+                },
+                "@swc/wasm": {
+                    "optional": true
+                }
             }
         },
-        "node_modules/tap/node_modules/redeyed": {
-            "version": "2.1.1",
+        "node_modules/tshy": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/tshy/-/tshy-1.2.2.tgz",
+            "integrity": "sha512-y5ItK4DKLYO+hba7h5sOaCYygNtF44qytZGyjZSE6CQSVfzUfZ2qn/GmXu737amwfCKG9EizPw3oPBWrisF1uw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "esprima": "~4.0.0"
+                "chalk": "^5.3.0",
+                "foreground-child": "^3.1.1",
+                "mkdirp": "^3.0.1",
+                "resolve-import": "^1.4.1",
+                "rimraf": "^5.0.1",
+                "sync-content": "^1.0.2",
+                "typescript": "5.2",
+                "walk-up-path": "^3.0.1"
+            },
+            "bin": {
+                "tshy": "dist/esm/index.js"
+            },
+            "engines": {
+                "node": "16 >=16.17 || 18 >=18.16.0 || >=20.6.1"
             }
         },
-        "node_modules/tap/node_modules/resolve-from": {
-            "version": "3.0.0",
+        "node_modules/tshy/node_modules/brace-expansion": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "engines": {
-                "node": ">=4"
+            "dependencies": {
+                "balanced-match": "^1.0.0"
             }
         },
-        "node_modules/tap/node_modules/restore-cursor": {
-            "version": "3.1.0",
+        "node_modules/tshy/node_modules/chalk": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+            "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "onetime": "^5.1.0",
-                "signal-exit": "^3.0.2"
-            },
             "engines": {
-                "node": ">=8"
+                "node": "^12.17.0 || ^14.13 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/chalk?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/rimraf": {
-            "version": "3.0.2",
+        "node_modules/tshy/node_modules/glob": {
+            "version": "10.3.10",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+            "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
             "dev": true,
-            "inBundle": true,
-            "license": "ISC",
             "dependencies": {
-                "glob": "^7.1.3"
+                "foreground-child": "^3.1.0",
+                "jackspeak": "^2.3.5",
+                "minimatch": "^9.0.1",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                "path-scurry": "^1.10.1"
             },
             "bin": {
-                "rimraf": "bin.js"
+                "glob": "dist/esm/bin.mjs"
+            },
+            "engines": {
+                "node": ">=16 || 14 >=14.17"
             },
             "funding": {
                 "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/safe-buffer": {
-            "version": "5.1.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/scheduler": {
-            "version": "0.20.2",
+        "node_modules/tshy/node_modules/minimatch": {
+            "version": "9.0.3",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "loose-envify": "^1.1.0",
-                "object-assign": "^4.1.1"
-            }
-        },
-        "node_modules/tap/node_modules/semver": {
-            "version": "6.3.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC",
-            "bin": {
-                "semver": "bin/semver.js"
-            }
-        },
-        "node_modules/tap/node_modules/shell-quote": {
-            "version": "1.7.3",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/signal-exit": {
-            "version": "3.0.7",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC"
-        },
-        "node_modules/tap/node_modules/slice-ansi": {
-            "version": "3.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "ansi-styles": "^4.0.0",
-                "astral-regex": "^2.0.0",
-                "is-fullwidth-code-point": "^3.0.0"
+                "brace-expansion": "^2.0.1"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=16 || 14 >=14.17"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/slice-ansi/node_modules/ansi-styles": {
-            "version": "4.3.0",
+        "node_modules/tshy/node_modules/rimraf": {
+            "version": "5.0.5",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+            "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-convert": "^2.0.1"
+                "glob": "^10.3.7"
+            },
+            "bin": {
+                "rimraf": "dist/esm/bin.mjs"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=14"
             },
             "funding": {
-                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+                "url": "https://github.com/sponsors/isaacs"
             }
         },
-        "node_modules/tap/node_modules/slice-ansi/node_modules/color-convert": {
-            "version": "2.0.1",
+        "node_modules/tslib": {
+            "version": "2.6.2",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+            "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+            "dev": true
+        },
+        "node_modules/tuf-js": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.1.0.tgz",
+            "integrity": "sha512-eD7YPPjVlMzdggrOeE8zwoegUaG/rt6Bt3jwoQPunRiNVzgcCE009UDFJKJjG+Gk9wFu6W/Vi+P5d/5QpdD9jA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-name": "~1.1.4"
+                "@tufjs/models": "2.0.0",
+                "debug": "^4.3.4",
+                "make-fetch-happen": "^13.0.0"
             },
             "engines": {
-                "node": ">=7.0.0"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/slice-ansi/node_modules/color-name": {
-            "version": "1.1.4",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/source-map": {
-            "version": "0.5.7",
+        "node_modules/tuf-js/node_modules/make-fetch-happen": {
+            "version": "13.0.0",
+            "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz",
+            "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==",
             "dev": true,
-            "inBundle": true,
-            "license": "BSD-3-Clause",
+            "dependencies": {
+                "@npmcli/agent": "^2.0.0",
+                "cacache": "^18.0.0",
+                "http-cache-semantics": "^4.1.1",
+                "is-lambda": "^1.0.1",
+                "minipass": "^7.0.2",
+                "minipass-fetch": "^3.0.0",
+                "minipass-flush": "^1.0.5",
+                "minipass-pipeline": "^1.2.4",
+                "negotiator": "^0.6.3",
+                "promise-retry": "^2.0.1",
+                "ssri": "^10.0.0"
+            },
             "engines": {
-                "node": ">=0.10.0"
+                "node": "^16.14.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/stack-utils": {
-            "version": "2.0.5",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/type-check": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+            "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
             "dependencies": {
-                "escape-string-regexp": "^2.0.0"
+                "prelude-ls": "^1.2.1"
             },
             "engines": {
-                "node": ">=10"
+                "node": ">= 0.8.0"
             }
         },
-        "node_modules/tap/node_modules/stack-utils/node_modules/escape-string-regexp": {
-            "version": "2.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/type-fest": {
+            "version": "0.20.2",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+            "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
             "engines": {
-                "node": ">=8"
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/string-width": {
-            "version": "4.2.3",
+        "node_modules/typescript": {
+            "version": "5.2.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+            "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "emoji-regex": "^8.0.0",
-                "is-fullwidth-code-point": "^3.0.0",
-                "strip-ansi": "^6.0.1"
+            "bin": {
+                "tsc": "bin/tsc",
+                "tsserver": "bin/tsserver"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=14.17"
             }
         },
-        "node_modules/tap/node_modules/strip-ansi": {
-            "version": "6.0.1",
+        "node_modules/unique-filename": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz",
+            "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "ansi-regex": "^5.0.1"
+                "unique-slug": "^4.0.0"
             },
             "engines": {
-                "node": ">=8"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/supports-color": {
-            "version": "5.5.0",
+        "node_modules/unique-slug": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz",
+            "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "has-flag": "^3.0.0"
+                "imurmurhash": "^0.1.4"
             },
             "engines": {
-                "node": ">=4"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/tap-parser": {
-            "version": "11.0.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/uri-js": {
+            "version": "4.4.1",
+            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+            "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
             "dependencies": {
-                "events-to-array": "^1.0.1",
-                "minipass": "^3.1.6",
-                "tap-yaml": "^1.0.0"
-            },
-            "bin": {
-                "tap-parser": "bin/cmd.js"
-            },
-            "engines": {
-                "node": ">= 8"
+                "punycode": "^2.1.0"
             }
         },
-        "node_modules/tap/node_modules/tap-yaml": {
+        "node_modules/util-deprecate": {
             "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+            "dev": true
+        },
+        "node_modules/uuid": {
+            "version": "8.3.2",
+            "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+            "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
             "dev": true,
-            "inBundle": true,
-            "license": "ISC",
-            "dependencies": {
-                "yaml": "^1.10.2"
+            "bin": {
+                "uuid": "dist/bin/uuid"
             }
         },
-        "node_modules/tap/node_modules/tcompare": {
-            "version": "5.0.7",
-            "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz",
-            "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==",
+        "node_modules/v8-compile-cache-lib": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+            "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+            "dev": true
+        },
+        "node_modules/v8-to-istanbul": {
+            "version": "9.1.0",
+            "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
+            "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
             "dev": true,
             "dependencies": {
-                "diff": "^4.0.2"
+                "@jridgewell/trace-mapping": "^0.3.12",
+                "@types/istanbul-lib-coverage": "^2.0.1",
+                "convert-source-map": "^1.6.0"
             },
             "engines": {
-                "node": ">=10"
+                "node": ">=10.12.0"
             }
         },
-        "node_modules/tap/node_modules/to-fast-properties": {
-            "version": "2.0.0",
+        "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": {
+            "version": "0.3.19",
+            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
+            "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "engines": {
-                "node": ">=4"
+            "dependencies": {
+                "@jridgewell/resolve-uri": "^3.1.0",
+                "@jridgewell/sourcemap-codec": "^1.4.14"
             }
         },
-        "node_modules/tap/node_modules/treport": {
+        "node_modules/validate-npm-package-license": {
             "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+            "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
             "dev": true,
-            "inBundle": true,
-            "license": "ISC",
             "dependencies": {
-                "@isaacs/import-jsx": "^4.0.1",
-                "cardinal": "^2.1.1",
-                "chalk": "^3.0.0",
-                "ink": "^3.2.0",
-                "ms": "^2.1.2",
-                "tap-parser": "^11.0.0",
-                "tap-yaml": "^1.0.0",
-                "unicode-length": "^2.0.2"
-            },
-            "peerDependencies": {
-                "react": "^17.0.2"
+                "spdx-correct": "^3.0.0",
+                "spdx-expression-parse": "^3.0.0"
             }
         },
-        "node_modules/tap/node_modules/treport/node_modules/ansi-styles": {
-            "version": "4.3.0",
+        "node_modules/validate-npm-package-name": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz",
+            "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-convert": "^2.0.1"
+                "builtins": "^5.0.0"
             },
             "engines": {
-                "node": ">=8"
-            },
-            "funding": {
-                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+                "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
             }
         },
-        "node_modules/tap/node_modules/treport/node_modules/chalk": {
-            "version": "3.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/walk-up-path": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz",
+            "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==",
+            "dev": true
+        },
+        "node_modules/which": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
             "dependencies": {
-                "ansi-styles": "^4.1.0",
-                "supports-color": "^7.1.0"
+                "isexe": "^2.0.0"
+            },
+            "bin": {
+                "node-which": "bin/node-which"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">= 8"
             }
         },
-        "node_modules/tap/node_modules/treport/node_modules/color-convert": {
-            "version": "2.0.1",
+        "node_modules/wide-align": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+            "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-name": "~1.1.4"
-            },
-            "engines": {
-                "node": ">=7.0.0"
+                "string-width": "^1.0.2 || 2 || 3 || 4"
             }
         },
-        "node_modules/tap/node_modules/treport/node_modules/color-name": {
-            "version": "1.1.4",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
+        "node_modules/wide-align/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "dev": true
         },
-        "node_modules/tap/node_modules/treport/node_modules/has-flag": {
-            "version": "4.0.0",
+        "node_modules/wide-align/node_modules/is-fullwidth-code-point": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/treport/node_modules/supports-color": {
-            "version": "7.2.0",
+        "node_modules/wide-align/node_modules/string-width": {
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "has-flag": "^4.0.0"
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/type-fest": {
-            "version": "0.12.0",
+        "node_modules/widest-line": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
+            "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
             "dev": true,
-            "inBundle": true,
-            "license": "(MIT OR CC0-1.0)",
+            "dependencies": {
+                "string-width": "^5.0.1"
+            },
             "engines": {
-                "node": ">=10"
+                "node": ">=12"
             },
             "funding": {
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/tap/node_modules/unicode-length": {
-            "version": "2.0.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "punycode": "^2.0.0",
-                "strip-ansi": "^3.0.1"
-            }
-        },
-        "node_modules/tap/node_modules/unicode-length/node_modules/ansi-regex": {
-            "version": "2.1.1",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
+        "node_modules/word-wrap": {
+            "version": "1.2.5",
+            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+            "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
             "engines": {
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/tap/node_modules/unicode-length/node_modules/strip-ansi": {
-            "version": "3.0.1",
+        "node_modules/wrap-ansi": {
+            "version": "8.1.0",
+            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+            "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "ansi-regex": "^2.0.0"
+                "ansi-styles": "^6.1.0",
+                "string-width": "^5.0.1",
+                "strip-ansi": "^7.0.1"
             },
             "engines": {
-                "node": ">=0.10.0"
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/widest-line": {
-            "version": "3.1.0",
+        "node_modules/wrap-ansi-cjs": {
+            "name": "wrap-ansi",
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+            "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "string-width": "^4.0.0"
+                "ansi-styles": "^4.0.0",
+                "string-width": "^4.1.0",
+                "strip-ansi": "^6.0.0"
             },
             "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+            }
+        },
+        "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "dev": true
+        },
+        "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+            "dev": true,
+            "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/wrap-ansi": {
-            "version": "6.2.0",
+        "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "ansi-styles": "^4.0.0",
-                "string-width": "^4.1.0",
-                "strip-ansi": "^6.0.0"
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
             },
             "engines": {
                 "node": ">=8"
             }
         },
-        "node_modules/tap/node_modules/wrap-ansi/node_modules/ansi-styles": {
-            "version": "4.3.0",
+        "node_modules/wrap-ansi/node_modules/ansi-regex": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+            "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "color-convert": "^2.0.1"
+            "engines": {
+                "node": ">=12"
             },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+            }
+        },
+        "node_modules/wrap-ansi/node_modules/ansi-styles": {
+            "version": "6.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+            "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+            "dev": true,
             "engines": {
-                "node": ">=8"
+                "node": ">=12"
             },
             "funding": {
                 "url": "https://github.com/chalk/ansi-styles?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/wrap-ansi/node_modules/color-convert": {
-            "version": "2.0.1",
+        "node_modules/wrap-ansi/node_modules/strip-ansi": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+            "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "dependencies": {
-                "color-name": "~1.1.4"
+                "ansi-regex": "^6.0.1"
             },
             "engines": {
-                "node": ">=7.0.0"
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/strip-ansi?sponsor=1"
             }
         },
-        "node_modules/tap/node_modules/wrap-ansi/node_modules/color-name": {
-            "version": "1.1.4",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT"
-        },
-        "node_modules/tap/node_modules/wrappy": {
+        "node_modules/wrappy": {
             "version": "1.0.2",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC"
+            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+            "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
         },
-        "node_modules/tap/node_modules/ws": {
-            "version": "7.5.7",
+        "node_modules/ws": {
+            "version": "8.14.2",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
+            "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
             "dev": true,
-            "inBundle": true,
-            "license": "MIT",
             "engines": {
-                "node": ">=8.3.0"
+                "node": ">=10.0.0"
             },
             "peerDependencies": {
                 "bufferutil": "^4.0.1",
-                "utf-8-validate": "^5.0.2"
+                "utf-8-validate": ">=5.0.2"
             },
             "peerDependenciesMeta": {
                 "bufferutil": {
@@ -4651,118 +5605,103 @@
                 }
             }
         },
-        "node_modules/tap/node_modules/yallist": {
-            "version": "4.0.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "ISC"
-        },
-        "node_modules/tap/node_modules/yaml": {
-            "version": "1.10.2",
+        "node_modules/y18n": {
+            "version": "5.0.8",
+            "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+            "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
             "dev": true,
-            "inBundle": true,
-            "license": "ISC",
             "engines": {
-                "node": ">= 6"
+                "node": ">=10"
             }
         },
-        "node_modules/tap/node_modules/yoga-layout-prebuilt": {
-            "version": "1.10.0",
-            "dev": true,
-            "inBundle": true,
-            "license": "MIT",
-            "dependencies": {
-                "@types/yoga-layout": "1.9.2"
-            },
-            "engines": {
-                "node": ">=8"
-            }
+        "node_modules/yallist": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+            "dev": true
         },
-        "node_modules/tcompare": {
-            "version": "6.0.0",
-            "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-6.0.0.tgz",
-            "integrity": "sha512-JeX89lSVkxTzYND0LxzFCGrXm/TqGEQ0heu1JTwplnpaYQNky6hIaO4lQBOrs+/P787i3CoK9T/O3/oEcnJXvA==",
+        "node_modules/yaml": {
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",
+            "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==",
             "dev": true,
-            "dependencies": {
-                "diff": "^5.1.0"
-            },
             "engines": {
-                "node": ">=16"
+                "node": ">= 14"
             }
         },
-        "node_modules/tcompare/node_modules/diff": {
-            "version": "5.1.0",
-            "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz",
-            "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
+        "node_modules/yaml-types": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/yaml-types/-/yaml-types-0.3.0.tgz",
+            "integrity": "sha512-i9RxAO/LZBiE0NJUy9pbN5jFz5EasYDImzRkj8Y81kkInTi1laia3P3K/wlMKzOxFQutZip8TejvQP/DwgbU7A==",
             "dev": true,
             "engines": {
-                "node": ">=0.3.1"
+                "node": ">= 16",
+                "npm": ">= 7"
+            },
+            "peerDependencies": {
+                "yaml": "^2.3.0"
             }
         },
-        "node_modules/test-exclude": {
-            "version": "6.0.0",
-            "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
-            "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+        "node_modules/yargs": {
+            "version": "17.7.2",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+            "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
             "dev": true,
             "dependencies": {
-                "@istanbuljs/schema": "^0.1.2",
-                "glob": "^7.1.4",
-                "minimatch": "^3.0.4"
+                "cliui": "^8.0.1",
+                "escalade": "^3.1.1",
+                "get-caller-file": "^2.0.5",
+                "require-directory": "^2.1.1",
+                "string-width": "^4.2.3",
+                "y18n": "^5.0.5",
+                "yargs-parser": "^21.1.1"
             },
             "engines": {
-                "node": ">=8"
+                "node": ">=12"
             }
         },
-        "node_modules/text-table": {
-            "version": "0.2.0",
-            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-            "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
-        },
-        "node_modules/to-fast-properties": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-            "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+        "node_modules/yargs-parser": {
+            "version": "21.1.1",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+            "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
             "dev": true,
             "engines": {
-                "node": ">=4"
+                "node": ">=12"
             }
         },
-        "node_modules/to-regex-range": {
-            "version": "5.0.1",
-            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-            "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-            "dev": true,
-            "dependencies": {
-                "is-number": "^7.0.0"
-            },
-            "engines": {
-                "node": ">=8.0"
-            }
+        "node_modules/yargs/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "dev": true
         },
-        "node_modules/trivial-deferred": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.1.2.tgz",
-            "integrity": "sha512-vDPiDBC3hyP6O4JrJYMImW3nl3c03Tsj9fEXc7Qc/XKa1O7gf5ZtFfIR/E0dun9SnDHdwjna1Z2rSzYgqpxh/g==",
+        "node_modules/yargs/node_modules/is-fullwidth-code-point": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
             "dev": true,
             "engines": {
-                "node": ">= 8"
+                "node": ">=8"
             }
         },
-        "node_modules/type-check": {
-            "version": "0.4.0",
-            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
-            "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+        "node_modules/yargs/node_modules/string-width": {
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+            "dev": true,
             "dependencies": {
-                "prelude-ls": "^1.2.1"
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
             },
             "engines": {
-                "node": ">= 0.8.0"
+                "node": ">=8"
             }
         },
-        "node_modules/type-fest": {
-            "version": "0.20.2",
-            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
-            "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+        "node_modules/yocto-queue": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+            "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
             "engines": {
                 "node": ">=10"
             },
@@ -4770,689 +5709,956 @@
                 "url": "https://github.com/sponsors/sindresorhus"
             }
         },
-        "node_modules/typedarray-to-buffer": {
-            "version": "3.1.5",
-            "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
-            "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
+        "node_modules/yoga-wasm-web": {
+            "version": "0.3.3",
+            "resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz",
+            "integrity": "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==",
+            "dev": true
+        }
+    },
+    "dependencies": {
+        "@alcalzone/ansi-tokenize": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.1.3.tgz",
+            "integrity": "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==",
             "dev": true,
+            "requires": {
+                "ansi-styles": "^6.2.1",
+                "is-fullwidth-code-point": "^4.0.0"
+            },
             "dependencies": {
-                "is-typedarray": "^1.0.0"
+                "ansi-styles": {
+                    "version": "6.2.1",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+                    "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+                    "dev": true
+                }
             }
         },
-        "node_modules/unicode-length": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.1.0.tgz",
-            "integrity": "sha512-4bV582zTV9Q02RXBxSUMiuN/KHo5w4aTojuKTNT96DIKps/SIawFp7cS5Mu25VuY1AioGXrmYyzKZUzh8OqoUw==",
-            "dev": true,
-            "dependencies": {
-                "punycode": "^2.0.0"
-            }
+        "@base2/pretty-print-object": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz",
+            "integrity": "sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==",
+            "dev": true
+        },
+        "@bcoe/v8-coverage": {
+            "version": "0.2.3",
+            "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+            "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+            "dev": true
         },
-        "node_modules/update-browserslist-db": {
-            "version": "1.0.10",
-            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
-            "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+        "@cspotcode/source-map-support": {
+            "version": "0.8.1",
+            "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+            "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
             "dev": true,
-            "funding": [
-                {
-                    "type": "opencollective",
-                    "url": "https://opencollective.com/browserslist"
-                },
-                {
-                    "type": "tidelift",
-                    "url": "https://tidelift.com/funding/github/npm/browserslist"
-                }
-            ],
-            "dependencies": {
-                "escalade": "^3.1.1",
-                "picocolors": "^1.0.0"
-            },
-            "bin": {
-                "browserslist-lint": "cli.js"
-            },
-            "peerDependencies": {
-                "browserslist": ">= 4.21.0"
+            "requires": {
+                "@jridgewell/trace-mapping": "0.3.9"
             }
         },
-        "node_modules/uri-js": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
-            "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
-            "dependencies": {
-                "punycode": "^2.1.0"
+        "@eslint-community/eslint-utils": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+            "requires": {
+                "eslint-visitor-keys": "^3.3.0"
             }
         },
-        "node_modules/uuid": {
-            "version": "8.3.2",
-            "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
-            "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
-            "dev": true,
-            "bin": {
-                "uuid": "dist/bin/uuid"
-            }
+        "@eslint-community/regexpp": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
+            "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ=="
         },
-        "node_modules/which": {
+        "@eslint/eslintrc": {
             "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-            "dependencies": {
-                "isexe": "^2.0.0"
-            },
-            "bin": {
-                "node-which": "bin/node-which"
-            },
-            "engines": {
-                "node": ">= 8"
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
+            "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+            "requires": {
+                "ajv": "^6.12.4",
+                "debug": "^4.3.2",
+                "espree": "^9.5.1",
+                "globals": "^13.19.0",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.2.1",
+                "js-yaml": "^4.1.0",
+                "minimatch": "^3.1.2",
+                "strip-json-comments": "^3.1.1"
             }
         },
-        "node_modules/which-module": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-            "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
-            "dev": true
+        "@eslint/js": {
+            "version": "8.37.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz",
+            "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A=="
         },
-        "node_modules/word-wrap": {
-            "version": "1.2.3",
-            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
-            "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
-            "engines": {
-                "node": ">=0.10.0"
+        "@humanwhocodes/config-array": {
+            "version": "0.11.8",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
+            "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+            "requires": {
+                "@humanwhocodes/object-schema": "^1.2.1",
+                "debug": "^4.1.1",
+                "minimatch": "^3.0.5"
             }
         },
-        "node_modules/wrap-ansi": {
-            "version": "7.0.0",
-            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
-            "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-            "dev": true,
-            "dependencies": {
-                "ansi-styles": "^4.0.0",
-                "string-width": "^4.1.0",
-                "strip-ansi": "^6.0.0"
-            },
-            "engines": {
-                "node": ">=10"
-            },
-            "funding": {
-                "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
-            }
+        "@humanwhocodes/module-importer": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+            "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="
         },
-        "node_modules/wrappy": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-            "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+        "@humanwhocodes/object-schema": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
         },
-        "node_modules/write-file-atomic": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
-            "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+        "@isaacs/cliui": {
+            "version": "8.0.2",
+            "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+            "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
             "dev": true,
+            "requires": {
+                "string-width": "^5.1.2",
+                "string-width-cjs": "npm:string-width@^4.2.0",
+                "strip-ansi": "^7.0.1",
+                "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+                "wrap-ansi": "^8.1.0",
+                "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+            },
             "dependencies": {
-                "imurmurhash": "^0.1.4",
-                "is-typedarray": "^1.0.0",
-                "signal-exit": "^3.0.2",
-                "typedarray-to-buffer": "^3.1.5"
+                "ansi-regex": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+                    "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "7.1.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+                    "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^6.0.1"
+                    }
+                }
             }
         },
-        "node_modules/y18n": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
-            "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+        "@istanbuljs/schema": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+            "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
             "dev": true
         },
-        "node_modules/yallist": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+        "@jridgewell/resolve-uri": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+            "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
             "dev": true
         },
-        "node_modules/yaml": {
-            "version": "1.10.2",
-            "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-            "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+        "@jridgewell/sourcemap-codec": {
+            "version": "1.4.15",
+            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+            "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+            "dev": true
+        },
+        "@jridgewell/trace-mapping": {
+            "version": "0.3.9",
+            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+            "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
             "dev": true,
-            "engines": {
-                "node": ">= 6"
+            "requires": {
+                "@jridgewell/resolve-uri": "^3.0.3",
+                "@jridgewell/sourcemap-codec": "^1.4.10"
             }
         },
-        "node_modules/yargs": {
-            "version": "15.4.1",
-            "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
-            "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
-            "dev": true,
-            "dependencies": {
-                "cliui": "^6.0.0",
-                "decamelize": "^1.2.0",
-                "find-up": "^4.1.0",
-                "get-caller-file": "^2.0.1",
-                "require-directory": "^2.1.1",
-                "require-main-filename": "^2.0.0",
-                "set-blocking": "^2.0.0",
-                "string-width": "^4.2.0",
-                "which-module": "^2.0.0",
-                "y18n": "^4.0.0",
-                "yargs-parser": "^18.1.2"
-            },
-            "engines": {
-                "node": ">=8"
+        "@nodelib/fs.scandir": {
+            "version": "2.1.5",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+            "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+            "requires": {
+                "@nodelib/fs.stat": "2.0.5",
+                "run-parallel": "^1.1.9"
             }
         },
-        "node_modules/yargs-parser": {
-            "version": "18.1.3",
-            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
-            "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
-            "dev": true,
-            "dependencies": {
-                "camelcase": "^5.0.0",
-                "decamelize": "^1.2.0"
-            },
-            "engines": {
-                "node": ">=6"
+        "@nodelib/fs.stat": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+            "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
+        },
+        "@nodelib/fs.walk": {
+            "version": "1.2.8",
+            "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+            "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+            "requires": {
+                "@nodelib/fs.scandir": "2.1.5",
+                "fastq": "^1.6.0"
             }
         },
-        "node_modules/yargs/node_modules/cliui": {
-            "version": "6.0.0",
-            "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
-            "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+        "@npmcli/agent": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.1.1.tgz",
+            "integrity": "sha512-6RlbiOAi6L6uUYF4/CDEkDZQnKw0XDsFJVrEpnib8rAx2WRMOsUyAdgnvDpX/fdkDWxtqE+NHwF465llI2wR0g==",
             "dev": true,
+            "requires": {
+                "http-proxy-agent": "^7.0.0",
+                "https-proxy-agent": "^7.0.1",
+                "lru-cache": "^10.0.1",
+                "socks-proxy-agent": "^8.0.1"
+            },
             "dependencies": {
-                "string-width": "^4.2.0",
-                "strip-ansi": "^6.0.0",
-                "wrap-ansi": "^6.2.0"
+                "agent-base": {
+                    "version": "7.1.0",
+                    "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
+                    "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
+                    "dev": true,
+                    "requires": {
+                        "debug": "^4.3.4"
+                    }
+                },
+                "http-proxy-agent": {
+                    "version": "7.0.0",
+                    "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz",
+                    "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==",
+                    "dev": true,
+                    "requires": {
+                        "agent-base": "^7.1.0",
+                        "debug": "^4.3.4"
+                    }
+                },
+                "https-proxy-agent": {
+                    "version": "7.0.2",
+                    "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
+                    "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
+                    "dev": true,
+                    "requires": {
+                        "agent-base": "^7.0.2",
+                        "debug": "4"
+                    }
+                },
+                "socks-proxy-agent": {
+                    "version": "8.0.2",
+                    "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz",
+                    "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==",
+                    "dev": true,
+                    "requires": {
+                        "agent-base": "^7.0.2",
+                        "debug": "^4.3.4",
+                        "socks": "^2.7.1"
+                    }
+                }
             }
         },
-        "node_modules/yargs/node_modules/wrap-ansi": {
-            "version": "6.2.0",
-            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
-            "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+        "@npmcli/fs": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz",
+            "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==",
             "dev": true,
-            "dependencies": {
-                "ansi-styles": "^4.0.0",
-                "string-width": "^4.1.0",
-                "strip-ansi": "^6.0.0"
-            },
-            "engines": {
-                "node": ">=8"
+            "requires": {
+                "semver": "^7.3.5"
             }
         },
-        "node_modules/yocto-queue": {
-            "version": "0.1.0",
-            "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
-            "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
-            "engines": {
-                "node": ">=10"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/sindresorhus"
-            }
-        }
-    },
-    "dependencies": {
-        "@ampproject/remapping": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
-            "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+        "@npmcli/git": {
+            "version": "5.0.3",
+            "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.3.tgz",
+            "integrity": "sha512-UZp9NwK+AynTrKvHn5k3KviW/hA5eENmFsu3iAPe7sWRt0lFUdsY/wXIYjpDFe7cdSNwOIzbObfwgt6eL5/2zw==",
             "dev": true,
             "requires": {
-                "@jridgewell/gen-mapping": "^0.1.0",
-                "@jridgewell/trace-mapping": "^0.3.9"
+                "@npmcli/promise-spawn": "^7.0.0",
+                "lru-cache": "^10.0.1",
+                "npm-pick-manifest": "^9.0.0",
+                "proc-log": "^3.0.0",
+                "promise-inflight": "^1.0.1",
+                "promise-retry": "^2.0.1",
+                "semver": "^7.3.5",
+                "which": "^4.0.0"
+            },
+            "dependencies": {
+                "isexe": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+                    "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+                    "dev": true
+                },
+                "which": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+                    "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+                    "dev": true,
+                    "requires": {
+                        "isexe": "^3.1.1"
+                    }
+                }
             }
         },
-        "@babel/code-frame": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
-            "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+        "@npmcli/installed-package-contents": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz",
+            "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==",
             "dev": true,
             "requires": {
-                "@babel/highlight": "^7.18.6"
+                "npm-bundled": "^3.0.0",
+                "npm-normalize-package-bin": "^3.0.0"
             }
         },
-        "@babel/compat-data": {
-            "version": "7.21.0",
-            "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz",
-            "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==",
+        "@npmcli/node-gyp": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz",
+            "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==",
             "dev": true
         },
-        "@babel/core": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz",
-            "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==",
+        "@npmcli/promise-spawn": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.0.tgz",
+            "integrity": "sha512-wBqcGsMELZna0jDblGd7UXgOby45TQaMWmbFwWX+SEotk4HV6zG2t6rT9siyLhPk4P6YYqgfL1UO8nMWDBVJXQ==",
             "dev": true,
             "requires": {
-                "@ampproject/remapping": "^2.2.0",
-                "@babel/code-frame": "^7.18.6",
-                "@babel/generator": "^7.21.3",
-                "@babel/helper-compilation-targets": "^7.20.7",
-                "@babel/helper-module-transforms": "^7.21.2",
-                "@babel/helpers": "^7.21.0",
-                "@babel/parser": "^7.21.3",
-                "@babel/template": "^7.20.7",
-                "@babel/traverse": "^7.21.3",
-                "@babel/types": "^7.21.3",
-                "convert-source-map": "^1.7.0",
-                "debug": "^4.1.0",
-                "gensync": "^1.0.0-beta.2",
-                "json5": "^2.2.2",
-                "semver": "^6.3.0"
+                "which": "^4.0.0"
+            },
+            "dependencies": {
+                "isexe": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+                    "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+                    "dev": true
+                },
+                "which": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+                    "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+                    "dev": true,
+                    "requires": {
+                        "isexe": "^3.1.1"
+                    }
+                }
             }
         },
-        "@babel/generator": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz",
-            "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==",
+        "@npmcli/run-script": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.1.tgz",
+            "integrity": "sha512-Od/JMrgkjZ8alyBE0IzeqZDiF1jgMez9Gkc/OYrCkHHiXNwM0wc6s7+h+xM7kYDZkS0tAoOLr9VvygyE5+2F7g==",
             "dev": true,
             "requires": {
-                "@babel/types": "^7.21.3",
-                "@jridgewell/gen-mapping": "^0.3.2",
-                "@jridgewell/trace-mapping": "^0.3.17",
-                "jsesc": "^2.5.1"
+                "@npmcli/node-gyp": "^3.0.0",
+                "@npmcli/promise-spawn": "^7.0.0",
+                "node-gyp": "^9.0.0",
+                "read-package-json-fast": "^3.0.0",
+                "which": "^4.0.0"
             },
             "dependencies": {
-                "@jridgewell/gen-mapping": {
-                    "version": "0.3.2",
-                    "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
-                    "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+                "isexe": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+                    "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+                    "dev": true
+                },
+                "which": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+                    "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
                     "dev": true,
                     "requires": {
-                        "@jridgewell/set-array": "^1.0.1",
-                        "@jridgewell/sourcemap-codec": "^1.4.10",
-                        "@jridgewell/trace-mapping": "^0.3.9"
+                        "isexe": "^3.1.1"
                     }
                 }
             }
         },
-        "@babel/helper-compilation-targets": {
-            "version": "7.20.7",
-            "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
-            "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
+        "@pkgjs/parseargs": {
+            "version": "0.11.0",
+            "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+            "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+            "dev": true,
+            "optional": true
+        },
+        "@sigstore/bundle": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.1.0.tgz",
+            "integrity": "sha512-89uOo6yh/oxaU8AeOUnVrTdVMcGk9Q1hJa7Hkvalc6G3Z3CupWk4Xe9djSgJm9fMkH69s0P0cVHUoKSOemLdng==",
             "dev": true,
             "requires": {
-                "@babel/compat-data": "^7.20.5",
-                "@babel/helper-validator-option": "^7.18.6",
-                "browserslist": "^4.21.3",
-                "lru-cache": "^5.1.1",
-                "semver": "^6.3.0"
+                "@sigstore/protobuf-specs": "^0.2.1"
             }
         },
-        "@babel/helper-environment-visitor": {
-            "version": "7.18.9",
-            "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
-            "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+        "@sigstore/protobuf-specs": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz",
+            "integrity": "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==",
             "dev": true
         },
-        "@babel/helper-function-name": {
-            "version": "7.21.0",
-            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
-            "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+        "@sigstore/sign": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.1.0.tgz",
+            "integrity": "sha512-4VRpfJxs+8eLqzLVrZngVNExVA/zAhVbi4UT4zmtLi4xRd7vz5qie834OgkrGsLlLB1B2nz/3wUxT1XAUBe8gw==",
             "dev": true,
             "requires": {
-                "@babel/template": "^7.20.7",
-                "@babel/types": "^7.21.0"
+                "@sigstore/bundle": "^2.1.0",
+                "@sigstore/protobuf-specs": "^0.2.1",
+                "make-fetch-happen": "^13.0.0"
+            },
+            "dependencies": {
+                "make-fetch-happen": {
+                    "version": "13.0.0",
+                    "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz",
+                    "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==",
+                    "dev": true,
+                    "requires": {
+                        "@npmcli/agent": "^2.0.0",
+                        "cacache": "^18.0.0",
+                        "http-cache-semantics": "^4.1.1",
+                        "is-lambda": "^1.0.1",
+                        "minipass": "^7.0.2",
+                        "minipass-fetch": "^3.0.0",
+                        "minipass-flush": "^1.0.5",
+                        "minipass-pipeline": "^1.2.4",
+                        "negotiator": "^0.6.3",
+                        "promise-retry": "^2.0.1",
+                        "ssri": "^10.0.0"
+                    }
+                }
             }
         },
-        "@babel/helper-hoist-variables": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
-            "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+        "@sigstore/tuf": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.2.0.tgz",
+            "integrity": "sha512-KKATZ5orWfqd9ZG6MN8PtCIx4eevWSuGRKQvofnWXRpyMyUEpmrzg5M5BrCpjM+NfZ0RbNGOh5tCz/P2uoRqOA==",
             "dev": true,
             "requires": {
-                "@babel/types": "^7.18.6"
+                "@sigstore/protobuf-specs": "^0.2.1",
+                "tuf-js": "^2.1.0"
             }
         },
-        "@babel/helper-module-imports": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
-            "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+        "@tapjs/after": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/after/-/after-1.1.4.tgz",
+            "integrity": "sha512-TVjrOwpPZt/VfdYc+X4gF/TY06gDHfzP9lfSv7hcxSaUGtvlU0xLH1xsTZS1BKM+EX1qXrCA8RYaLblAniKmaQ==",
             "dev": true,
             "requires": {
-                "@babel/types": "^7.18.6"
+                "is-actual-promise": "^1.0.0"
             }
         },
-        "@babel/helper-module-transforms": {
-            "version": "7.21.2",
-            "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz",
-            "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==",
+        "@tapjs/after-each": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/after-each/-/after-each-1.1.4.tgz",
+            "integrity": "sha512-vcmPQi2wXi2obK2j1nXTDo6EV8uqXONGiaPAPsj+iELr7OB3vBR1FFOQ6GWAFw0Xh8EIIUs8CWyNHn40/kmyUg==",
             "dev": true,
             "requires": {
-                "@babel/helper-environment-visitor": "^7.18.9",
-                "@babel/helper-module-imports": "^7.18.6",
-                "@babel/helper-simple-access": "^7.20.2",
-                "@babel/helper-split-export-declaration": "^7.18.6",
-                "@babel/helper-validator-identifier": "^7.19.1",
-                "@babel/template": "^7.20.7",
-                "@babel/traverse": "^7.21.2",
-                "@babel/types": "^7.21.2"
+                "function-loop": "^4.0.0"
             }
         },
-        "@babel/helper-simple-access": {
-            "version": "7.20.2",
-            "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
-            "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+        "@tapjs/asserts": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/asserts/-/asserts-1.1.4.tgz",
+            "integrity": "sha512-5jhbvqJ88agvGEW27l/ucNK7WqQAsCCt6gTBJKdVIL8jOZz5jOVaN/UI6gqUHLO7SYxIl4SOh8N11OYizRSKfA==",
             "dev": true,
             "requires": {
-                "@babel/types": "^7.20.2"
+                "is-actual-promise": "^1.0.0",
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
             }
         },
-        "@babel/helper-split-export-declaration": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
-            "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+        "@tapjs/before": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/before/-/before-1.1.4.tgz",
+            "integrity": "sha512-JnCg39toYCBMZKECL6dqXkpi5p9efxvug/vqMoW7XDpYSJRnRz25EUvTPFd1IE6SwVpJF2xRFL7EKUnxLN3JiQ==",
             "dev": true,
             "requires": {
-                "@babel/types": "^7.18.6"
+                "is-actual-promise": "^1.0.0"
             }
         },
-        "@babel/helper-string-parser": {
-            "version": "7.19.4",
-            "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
-            "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
-            "dev": true
+        "@tapjs/before-each": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/before-each/-/before-each-1.1.4.tgz",
+            "integrity": "sha512-DnwLTOmeifh571kvL3Ef94Ui0OpGzM/oIbjOaL9onHnLTR+cOO8yZALJp6zVg/pq/OzScDY3DQuazunolEVCQQ==",
+            "dev": true,
+            "requires": {
+                "function-loop": "^4.0.0"
+            }
         },
-        "@babel/helper-validator-identifier": {
-            "version": "7.19.1",
-            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
-            "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
-            "dev": true
+        "@tapjs/config": {
+            "version": "2.4.0",
+            "resolved": "https://registry.npmjs.org/@tapjs/config/-/config-2.4.0.tgz",
+            "integrity": "sha512-iz8n4GFY8FM1kKro4W6kZ3mQvzjddL4j8ta1B08q9ix8K5ysfHnbamjh2syORVRGo/dZNMnKvfXTxFzZ+WIbDg==",
+            "dev": true,
+            "requires": {
+                "chalk": "^5.2.0",
+                "jackspeak": "^2.3.6",
+                "polite-json": "^4.0.1",
+                "walk-up-path": "^3.0.1"
+            },
+            "dependencies": {
+                "chalk": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+                    "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+                    "dev": true
+                }
+            }
         },
-        "@babel/helper-validator-option": {
-            "version": "7.21.0",
-            "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
-            "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
-            "dev": true
+        "@tapjs/core": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/core/-/core-1.3.4.tgz",
+            "integrity": "sha512-EcINYx86gDzLeZAsHMckv4Fjd4TdYJ7KduvdhD0Qy4EhROjQnaY9lPQTQxT2uwaEjpWB2Pio3ahtLzNUT2lY1g==",
+            "dev": true,
+            "requires": {
+                "@tapjs/processinfo": "^3.1.2",
+                "@tapjs/stack": "1.2.3",
+                "@tapjs/test": "1.3.4",
+                "async-hook-domain": "^4.0.1",
+                "is-actual-promise": "^1.0.0",
+                "jackspeak": "^2.3.6",
+                "minipass": "^7.0.3",
+                "signal-exit": "4.1",
+                "tap-parser": "15.2.0",
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
+            }
+        },
+        "@tapjs/error-serdes": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/@tapjs/error-serdes/-/error-serdes-1.1.0.tgz",
+            "integrity": "sha512-RAdsafCQ9fyudLY4EQPhfWQvRNddvSoXKEsZQWZC6G5QfdB/BYnSqaXggK5TD0XZ79Ja0ex3uB+5kBaaeLKtQA==",
+            "dev": true,
+            "requires": {
+                "minipass": "^7.0.3"
+            }
         },
-        "@babel/helpers": {
-            "version": "7.21.0",
-            "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz",
-            "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==",
+        "@tapjs/filter": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/filter/-/filter-1.2.4.tgz",
+            "integrity": "sha512-YHIjat67MuuO2SzSg2Hcwwm1Y1UJ1yvD20hyy6MYGrKG8vkaU1hSu4bBheRhJ2IyqJQVgSIM+raNctlN5Bpa/A==",
             "dev": true,
             "requires": {
-                "@babel/template": "^7.20.7",
-                "@babel/traverse": "^7.21.0",
-                "@babel/types": "^7.21.0"
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
             }
         },
-        "@babel/highlight": {
-            "version": "7.18.6",
-            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
-            "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+        "@tapjs/fixture": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/fixture/-/fixture-1.2.4.tgz",
+            "integrity": "sha512-7PHkg7fbKRWThU017qkw92dovreQct3LCArUJ9OdZWFoPYRwYND7CKB3/x7qtnNftBFZbRzf562miH0+TLDDTQ==",
             "dev": true,
             "requires": {
-                "@babel/helper-validator-identifier": "^7.18.6",
-                "chalk": "^2.0.0",
-                "js-tokens": "^4.0.0"
+                "mkdirp": "^3.0.0",
+                "rimraf": "^5.0.5"
             },
             "dependencies": {
-                "ansi-styles": {
-                    "version": "3.2.1",
-                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-                    "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
                     "dev": true,
                     "requires": {
-                        "color-convert": "^1.9.0"
+                        "balanced-match": "^1.0.0"
                     }
                 },
-                "chalk": {
-                    "version": "2.4.2",
-                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-                    "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+                "glob": {
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "^3.2.1",
-                        "escape-string-regexp": "^1.0.5",
-                        "supports-color": "^5.3.0"
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
                     }
                 },
-                "color-convert": {
-                    "version": "1.9.3",
-                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-                    "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
                     "dev": true,
                     "requires": {
-                        "color-name": "1.1.3"
+                        "brace-expansion": "^2.0.1"
                     }
                 },
-                "color-name": {
-                    "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-                    "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-                    "dev": true
-                },
-                "escape-string-regexp": {
-                    "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-                    "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-                    "dev": true
-                },
-                "has-flag": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-                    "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-                    "dev": true
-                },
-                "supports-color": {
-                    "version": "5.5.0",
-                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-                    "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+                "rimraf": {
+                    "version": "5.0.5",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+                    "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
                     "dev": true,
                     "requires": {
-                        "has-flag": "^3.0.0"
+                        "glob": "^10.3.7"
                     }
                 }
             }
         },
-        "@babel/parser": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz",
-            "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==",
-            "dev": true
+        "@tapjs/intercept": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/intercept/-/intercept-1.2.4.tgz",
+            "integrity": "sha512-aEPwa40DqJPmgnZRbED+hI1x3dSUn4o5rePW6I2ludRle3o1bHSSnucYsjhwNPz0LCpOH9q/UAivJPO66xyTBA==",
+            "dev": true,
+            "requires": {
+                "@tapjs/after": "1.1.4",
+                "@tapjs/stack": "1.2.3"
+            }
+        },
+        "@tapjs/mock": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/@tapjs/mock/-/mock-1.2.2.tgz",
+            "integrity": "sha512-5SgMRNaHgxjuna5YfVrT/l9bCTV4qePbqxNhwLWiL/l4fHMcF8CB7jMQ2IXsB8/0q9dKSuuxysOeiYSScNQcsA==",
+            "dev": true,
+            "requires": {
+                "@tapjs/after": "1.1.4",
+                "@tapjs/stack": "1.2.3",
+                "resolve-import": "^1.4.2",
+                "walk-up-path": "^3.0.1"
+            }
         },
-        "@babel/template": {
-            "version": "7.20.7",
-            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
-            "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+        "@tapjs/node-serialize": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/node-serialize/-/node-serialize-1.1.4.tgz",
+            "integrity": "sha512-t0x4jC15jae4DviixIqb0v53eXkWdE3KkmKcf/eMGCqN7EL3lRyQRTOtjC3fJRWmdXYCGK/311DpoUfpgzL3sA==",
             "dev": true,
             "requires": {
-                "@babel/code-frame": "^7.18.6",
-                "@babel/parser": "^7.20.7",
-                "@babel/types": "^7.20.7"
+                "@tapjs/error-serdes": "1.1.0"
             }
         },
-        "@babel/traverse": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz",
-            "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==",
+        "@tapjs/processinfo": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/@tapjs/processinfo/-/processinfo-3.1.2.tgz",
+            "integrity": "sha512-O3lg1X7zy4sQs+jDYHu+njFQCC5hYJWRmmbLy9UVhgqQKZifS4DYqkoAedK3ixj5NQ1stMNmJGJxbEvJLw/NWA==",
             "dev": true,
             "requires": {
-                "@babel/code-frame": "^7.18.6",
-                "@babel/generator": "^7.21.3",
-                "@babel/helper-environment-visitor": "^7.18.9",
-                "@babel/helper-function-name": "^7.21.0",
-                "@babel/helper-hoist-variables": "^7.18.6",
-                "@babel/helper-split-export-declaration": "^7.18.6",
-                "@babel/parser": "^7.21.3",
-                "@babel/types": "^7.21.3",
-                "debug": "^4.1.0",
-                "globals": "^11.1.0"
+                "pirates": "^4.0.5",
+                "process-on-spawn": "^1.0.0",
+                "signal-exit": "^4.0.2",
+                "uuid": "^8.3.2"
+            }
+        },
+        "@tapjs/reporter": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/@tapjs/reporter/-/reporter-1.3.0.tgz",
+            "integrity": "sha512-zjXwsZh895zUPM00w9q0W2u/y2ncTz4q/FYu3Jl8Ph0KcSTiGBob01Rj4+Uhhx0N5YwJxb4HOujRtAqhyqs7Gg==",
+            "dev": true,
+            "requires": {
+                "@tapjs/config": "2.4.0",
+                "@tapjs/test": "1.3.4",
+                "chalk": "^5.2.0",
+                "ink": "^4.4.1",
+                "minipass": "^7.0.3",
+                "ms": "^2.1.3",
+                "patch-console": "^2.0.0",
+                "prismjs": "^1.29.0",
+                "prismjs-terminal": "^1.2.3",
+                "react": "^18.2.0",
+                "string-length": "^6.0.0",
+                "tcompare": "6.4.0"
             },
             "dependencies": {
-                "globals": {
-                    "version": "11.12.0",
-                    "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
-                    "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+                "chalk": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+                    "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+                    "dev": true
+                },
+                "ms": {
+                    "version": "2.1.3",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+                    "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
                     "dev": true
                 }
             }
         },
-        "@babel/types": {
-            "version": "7.21.3",
-            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz",
-            "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==",
+        "@tapjs/run": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/@tapjs/run/-/run-1.4.0.tgz",
+            "integrity": "sha512-3LNRejFAos8iND30CiQV+RIdaiHBKjsLNq1BZ/nena7lcshKoQCFtiVpKMlqGAStMQgLygjgSo2uHbuSDD0Qww==",
             "dev": true,
             "requires": {
-                "@babel/helper-string-parser": "^7.19.4",
-                "@babel/helper-validator-identifier": "^7.19.1",
-                "to-fast-properties": "^2.0.0"
+                "@tapjs/after": "1.1.4",
+                "@tapjs/before": "1.1.4",
+                "@tapjs/config": "2.4.0",
+                "@tapjs/processinfo": "^3.1.2",
+                "@tapjs/reporter": "1.3.0",
+                "@tapjs/spawn": "1.1.4",
+                "@tapjs/stdin": "1.1.4",
+                "@tapjs/test": "1.3.4",
+                "c8": "^8.0.1",
+                "chokidar": "^3.5.3",
+                "foreground-child": "^3.1.1",
+                "glob": "^10.3.10",
+                "minipass": "^7.0.3",
+                "mkdirp": "^3.0.1",
+                "opener": "^1.5.2",
+                "pacote": "^17.0.3",
+                "path-scurry": "^1.9.2",
+                "resolve-import": "^1.4.2",
+                "rimraf": "^5.0.5",
+                "semver": "^7.5.4",
+                "signal-exit": "^4.1.0",
+                "tap-yaml": "2.2.0",
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0",
+                "ts-node": "npm:@isaacs/ts-node-temp-fork-for-pr-2009@^10.9.1",
+                "which": "^4.0.0"
+            },
+            "dependencies": {
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+                    "dev": true,
+                    "requires": {
+                        "balanced-match": "^1.0.0"
+                    }
+                },
+                "glob": {
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+                    "dev": true,
+                    "requires": {
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
+                    }
+                },
+                "isexe": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+                    "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+                    "dev": true
+                },
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                },
+                "rimraf": {
+                    "version": "5.0.5",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+                    "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
+                    "dev": true,
+                    "requires": {
+                        "glob": "^10.3.7"
+                    }
+                },
+                "which": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz",
+                    "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==",
+                    "dev": true,
+                    "requires": {
+                        "isexe": "^3.1.1"
+                    }
+                }
             }
         },
-        "@eslint-community/eslint-utils": {
-            "version": "4.4.0",
-            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
-            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+        "@tapjs/snapshot": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/snapshot/-/snapshot-1.2.4.tgz",
+            "integrity": "sha512-8pStZczbArIC6+s8TblHTs/Mr5RGApWZA91Eey5UuU5MX3IPUw77MPQpPOoh2zrefa8VZRmHM7IgQq8SKyYjyQ==",
+            "dev": true,
             "requires": {
-                "eslint-visitor-keys": "^3.3.0"
+                "is-actual-promise": "^1.0.0",
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
             }
         },
-        "@eslint-community/regexpp": {
-            "version": "4.5.0",
-            "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
-            "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ=="
+        "@tapjs/spawn": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/spawn/-/spawn-1.1.4.tgz",
+            "integrity": "sha512-H3/VBi/Zfnb53PbpNmT/OYhIdqk8k6pGnM+WNLB8KBzwLa23q75P0jSYAEhzX3sZO+JIiaHACj/SxvttFapDtg==",
+            "dev": true,
+            "requires": {}
         },
-        "@eslint/eslintrc": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
-            "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+        "@tapjs/stack": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/@tapjs/stack/-/stack-1.2.3.tgz",
+            "integrity": "sha512-LY7Rxse2QY+DczTCoqOA4rxjqhnCgXYZeynrhzOsiut6IVnDWnqjUvZMq1XYnk5G69lhgG5lTDHmZrKP33BKgg==",
+            "dev": true,
             "requires": {
-                "ajv": "^6.12.4",
-                "debug": "^4.3.2",
-                "espree": "^9.5.1",
-                "globals": "^13.19.0",
-                "ignore": "^5.2.0",
-                "import-fresh": "^3.2.1",
-                "js-yaml": "^4.1.0",
-                "minimatch": "^3.1.2",
-                "strip-json-comments": "^3.1.1"
+                "tcompare": "6.4.0",
+                "trivial-deferred": "^2.0.0"
             }
         },
-        "@eslint/js": {
-            "version": "8.37.0",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz",
-            "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A=="
-        },
-        "@humanwhocodes/config-array": {
-            "version": "0.11.8",
-            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
-            "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
-            "requires": {
-                "@humanwhocodes/object-schema": "^1.2.1",
-                "debug": "^4.1.1",
-                "minimatch": "^3.0.5"
-            }
-        },
-        "@humanwhocodes/module-importer": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
-            "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="
-        },
-        "@humanwhocodes/object-schema": {
-            "version": "1.2.1",
-            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
-            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
+        "@tapjs/stdin": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/stdin/-/stdin-1.1.4.tgz",
+            "integrity": "sha512-yQzeiWaWRFd5jXVy3F0Q4inQqVmEGynFfWz2cbQYJFm/CNCcKFM1t4uIRRqtNdfJwSrr19m8Lq0qqfT7pHV/yg==",
+            "dev": true,
+            "requires": {}
         },
-        "@istanbuljs/load-nyc-config": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
-            "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+        "@tapjs/test": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/test/-/test-1.3.4.tgz",
+            "integrity": "sha512-ud2T10OhxdQw4f7Wo4G+5/Vyw5JYgfb5bDmKo0B3xmMgVvIFpUS/4V2Zq+59DZGXmEgjO0KPhb8NvOpOHAy/fg==",
             "dev": true,
             "requires": {
-                "camelcase": "^5.3.1",
-                "find-up": "^4.1.0",
-                "get-package-type": "^0.1.0",
-                "js-yaml": "^3.13.1",
-                "resolve-from": "^5.0.0"
+                "@tapjs/after": "1.1.4",
+                "@tapjs/after-each": "1.1.4",
+                "@tapjs/asserts": "1.1.4",
+                "@tapjs/before": "1.1.4",
+                "@tapjs/before-each": "1.1.4",
+                "@tapjs/filter": "1.2.4",
+                "@tapjs/fixture": "1.2.4",
+                "@tapjs/intercept": "1.2.4",
+                "@tapjs/mock": "1.2.2",
+                "@tapjs/node-serialize": "1.1.4",
+                "@tapjs/snapshot": "1.2.4",
+                "@tapjs/spawn": "1.1.4",
+                "@tapjs/stdin": "1.1.4",
+                "@tapjs/typescript": "1.2.4",
+                "@tapjs/worker": "1.1.4",
+                "glob": "^10.3.10",
+                "jackspeak": "^2.3.6",
+                "mkdirp": "^3.0.0",
+                "resolve-import": "^1.4.1",
+                "rimraf": "^5.0.5",
+                "sync-content": "^1.0.1",
+                "tap-parser": "15.2.0",
+                "ts-node": "npm:@isaacs/ts-node-temp-fork-for-pr-2009@^10.9.1",
+                "tshy": "^1.2.2",
+                "typescript": "5.2"
             },
             "dependencies": {
-                "argparse": {
-                    "version": "1.0.10",
-                    "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-                    "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
                     "dev": true,
                     "requires": {
-                        "sprintf-js": "~1.0.2"
+                        "balanced-match": "^1.0.0"
                     }
                 },
-                "js-yaml": {
-                    "version": "3.14.1",
-                    "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-                    "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+                "glob": {
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
                     "dev": true,
                     "requires": {
-                        "argparse": "^1.0.7",
-                        "esprima": "^4.0.0"
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
                     }
                 },
-                "resolve-from": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
-                    "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
-                    "dev": true
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                },
+                "rimraf": {
+                    "version": "5.0.5",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+                    "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
+                    "dev": true,
+                    "requires": {
+                        "glob": "^10.3.7"
+                    }
                 }
             }
         },
-        "@istanbuljs/schema": {
-            "version": "0.1.3",
-            "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
-            "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
-            "dev": true
-        },
-        "@jridgewell/gen-mapping": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
-            "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+        "@tapjs/typescript": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/typescript/-/typescript-1.2.4.tgz",
+            "integrity": "sha512-exhSckFlKLr0RFHKYBJb3N6CftoafH5GwNeAWN0yua+FmzwDleGvgKThW3l/xeOF7BeCq/m4zu9HWrwjkPaDhQ==",
             "dev": true,
             "requires": {
-                "@jridgewell/set-array": "^1.0.0",
-                "@jridgewell/sourcemap-codec": "^1.4.10"
+                "ts-node": "npm:@isaacs/ts-node-temp-fork-for-pr-2009@^10.9.1"
             }
         },
-        "@jridgewell/resolve-uri": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
-            "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+        "@tapjs/worker": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/@tapjs/worker/-/worker-1.1.4.tgz",
+            "integrity": "sha512-HcaafOWghXpMtLaCk8BOIMQcphZU2Gi0OSUb6vzgxKQ4iQxTsBkJSnZ1+4F8Qed9EWZ9n6zaggjy7/fDLVdJRg==",
+            "dev": true,
+            "requires": {}
+        },
+        "@tootallnate/once": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+            "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
             "dev": true
         },
-        "@jridgewell/set-array": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
-            "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+        "@tsconfig/node14": {
+            "version": "14.1.0",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-14.1.0.tgz",
+            "integrity": "sha512-VmsCG04YR58ciHBeJKBDNMWWfYbyP8FekWVuTlpstaUPlat1D0x/tXzkWP7yCMU0eSz9V4OZU0LBWTFJ3xZf6w==",
             "dev": true
         },
-        "@jridgewell/sourcemap-codec": {
-            "version": "1.4.14",
-            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
-            "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+        "@tsconfig/node16": {
+            "version": "16.1.1",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-16.1.1.tgz",
+            "integrity": "sha512-+pio93ejHN4nINX4pXqfnR/fPLRtJBaT4ORaa5RH0Oc1zoYmo2B2koG+M328CQhHKn1Wj6FcOxCDFXAot9NhvA==",
             "dev": true
         },
-        "@jridgewell/trace-mapping": {
-            "version": "0.3.17",
-            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
-            "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+        "@tsconfig/node18": {
+            "version": "18.2.2",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.2.tgz",
+            "integrity": "sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==",
+            "dev": true
+        },
+        "@tsconfig/node20": {
+            "version": "20.1.2",
+            "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.2.tgz",
+            "integrity": "sha512-madaWq2k+LYMEhmcp0fs+OGaLFk0OenpHa4gmI4VEmCKX4PJntQ6fnnGADVFrVkBj0wIdAlQnK/MrlYTHsa1gQ==",
+            "dev": true
+        },
+        "@tufjs/canonical-json": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
+            "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==",
+            "dev": true
+        },
+        "@tufjs/models": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.0.tgz",
+            "integrity": "sha512-c8nj8BaOExmZKO2DXhDfegyhSGcG9E/mPN3U13L+/PsoWm1uaGiHHjxqSHQiasDBQwDA3aHuw9+9spYAP1qvvg==",
             "dev": true,
             "requires": {
-                "@jridgewell/resolve-uri": "3.1.0",
-                "@jridgewell/sourcemap-codec": "1.4.14"
+                "@tufjs/canonical-json": "2.0.0",
+                "minimatch": "^9.0.3"
+            },
+            "dependencies": {
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+                    "dev": true,
+                    "requires": {
+                        "balanced-match": "^1.0.0"
+                    }
+                },
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                }
             }
         },
-        "@nodelib/fs.scandir": {
-            "version": "2.1.5",
-            "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
-            "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
-            "requires": {
-                "@nodelib/fs.stat": "2.0.5",
-                "run-parallel": "^1.1.9"
-            }
+        "@types/istanbul-lib-coverage": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
+            "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
+            "dev": true
         },
-        "@nodelib/fs.stat": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
-            "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
+        "@types/node": {
+            "version": "20.8.0",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.0.tgz",
+            "integrity": "sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==",
+            "dev": true,
+            "peer": true
         },
-        "@nodelib/fs.walk": {
-            "version": "1.2.8",
-            "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
-            "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
-            "requires": {
-                "@nodelib/fs.scandir": "2.1.5",
-                "fastq": "^1.6.0"
-            }
+        "abbrev": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+            "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+            "dev": true
         },
         "acorn": {
             "version": "8.8.2",
@@ -5465,6 +6671,30 @@
             "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
             "requires": {}
         },
+        "acorn-walk": {
+            "version": "8.2.0",
+            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+            "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+            "dev": true
+        },
+        "agent-base": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+            "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+            "dev": true,
+            "requires": {
+                "debug": "4"
+            }
+        },
+        "agentkeepalive": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
+            "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
+            "dev": true,
+            "requires": {
+                "humanize-ms": "^1.2.1"
+            }
+        },
         "aggregate-error": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@@ -5473,6 +6703,14 @@
             "requires": {
                 "clean-stack": "^2.0.0",
                 "indent-string": "^4.0.0"
+            },
+            "dependencies": {
+                "indent-string": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+                    "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+                    "dev": true
+                }
             }
         },
         "ajv": {
@@ -5486,6 +6724,23 @@
                 "uri-js": "^4.2.2"
             }
         },
+        "ansi-escapes": {
+            "version": "6.2.0",
+            "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz",
+            "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==",
+            "dev": true,
+            "requires": {
+                "type-fest": "^3.0.0"
+            },
+            "dependencies": {
+                "type-fest": {
+                    "version": "3.13.1",
+                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz",
+                    "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
+                    "dev": true
+                }
+            }
+        },
         "ansi-regex": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -5509,19 +6764,26 @@
                 "picomatch": "^2.0.4"
             }
         },
-        "append-transform": {
+        "aproba": {
             "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz",
-            "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==",
+            "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+            "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+            "dev": true
+        },
+        "are-we-there-yet": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
+            "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
             "dev": true,
             "requires": {
-                "default-require-extensions": "^3.0.0"
+                "delegates": "^1.0.0",
+                "readable-stream": "^3.6.0"
             }
         },
-        "archy": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
-            "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==",
+        "arg": {
+            "version": "4.1.3",
+            "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+            "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
             "dev": true
         },
         "argparse": {
@@ -5530,9 +6792,15 @@
             "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
         },
         "async-hook-domain": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.4.tgz",
-            "integrity": "sha512-14LjCmlK1PK8eDtTezR6WX8TMaYNIzBIsd2D1sGoGjgx0BuNMMoSdk7i/drlbtamy0AWv9yv2tkB+ASdmeqFIw==",
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-4.0.1.tgz",
+            "integrity": "sha512-bSktexGodAjfHWIrSrrqxqWzf1hWBZBpmPNZv+TYUMyWa2eoefFc6q6H1+KtdHYSz35lrhWdmXt/XK9wNEZvww==",
+            "dev": true
+        },
+        "auto-bind": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz",
+            "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==",
             "dev": true
         },
         "balanced-match": {
@@ -5546,12 +6814,6 @@
             "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
             "dev": true
         },
-        "bind-obj-methods": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz",
-            "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==",
-            "dev": true
-        },
         "brace-expansion": {
             "version": "1.1.11",
             "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -5570,34 +6832,104 @@
                 "fill-range": "^7.0.1"
             }
         },
-        "browserslist": {
-            "version": "4.21.5",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
-            "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+        "builtins": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
+            "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
             "dev": true,
             "requires": {
-                "caniuse-lite": "^1.0.30001449",
-                "electron-to-chromium": "^1.4.284",
-                "node-releases": "^2.0.8",
-                "update-browserslist-db": "^1.0.10"
+                "semver": "^7.0.0"
             }
         },
-        "buffer-from": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
-            "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
-            "dev": true
+        "c8": {
+            "version": "8.0.1",
+            "resolved": "https://registry.npmjs.org/c8/-/c8-8.0.1.tgz",
+            "integrity": "sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==",
+            "dev": true,
+            "requires": {
+                "@bcoe/v8-coverage": "^0.2.3",
+                "@istanbuljs/schema": "^0.1.3",
+                "find-up": "^5.0.0",
+                "foreground-child": "^2.0.0",
+                "istanbul-lib-coverage": "^3.2.0",
+                "istanbul-lib-report": "^3.0.1",
+                "istanbul-reports": "^3.1.6",
+                "rimraf": "^3.0.2",
+                "test-exclude": "^6.0.0",
+                "v8-to-istanbul": "^9.0.0",
+                "yargs": "^17.7.2",
+                "yargs-parser": "^21.1.1"
+            },
+            "dependencies": {
+                "foreground-child": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
+                    "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+                    "dev": true,
+                    "requires": {
+                        "cross-spawn": "^7.0.0",
+                        "signal-exit": "^3.0.2"
+                    }
+                },
+                "signal-exit": {
+                    "version": "3.0.7",
+                    "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+                    "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+                    "dev": true
+                }
+            }
         },
-        "caching-transform": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
-            "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==",
+        "cacache": {
+            "version": "18.0.0",
+            "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.0.tgz",
+            "integrity": "sha512-I7mVOPl3PUCeRub1U8YoGz2Lqv9WOBpobZ8RyWFXmReuILz+3OAyTa5oH3QPdtKZD7N0Yk00aLfzn0qvp8dZ1w==",
             "dev": true,
             "requires": {
-                "hasha": "^5.0.0",
-                "make-dir": "^3.0.0",
-                "package-hash": "^4.0.0",
-                "write-file-atomic": "^3.0.0"
+                "@npmcli/fs": "^3.1.0",
+                "fs-minipass": "^3.0.0",
+                "glob": "^10.2.2",
+                "lru-cache": "^10.0.1",
+                "minipass": "^7.0.3",
+                "minipass-collect": "^1.0.2",
+                "minipass-flush": "^1.0.5",
+                "minipass-pipeline": "^1.2.4",
+                "p-map": "^4.0.0",
+                "ssri": "^10.0.0",
+                "tar": "^6.1.11",
+                "unique-filename": "^3.0.0"
+            },
+            "dependencies": {
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+                    "dev": true,
+                    "requires": {
+                        "balanced-match": "^1.0.0"
+                    }
+                },
+                "glob": {
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+                    "dev": true,
+                    "requires": {
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
+                    }
+                },
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                }
             }
         },
         "callsites": {
@@ -5605,18 +6937,6 @@
             "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
             "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
         },
-        "camelcase": {
-            "version": "5.3.1",
-            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-            "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-            "dev": true
-        },
-        "caniuse-lite": {
-            "version": "1.0.30001469",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz",
-            "integrity": "sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g==",
-            "dev": true
-        },
         "chalk": {
             "version": "4.1.2",
             "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -5653,26 +6973,126 @@
                 }
             }
         },
+        "chownr": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+            "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+            "dev": true
+        },
         "chroma-js": {
             "version": "2.4.2",
             "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz",
             "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A=="
         },
+        "ci-info": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+            "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
+            "dev": true
+        },
         "clean-stack": {
             "version": "2.2.0",
             "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
             "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
             "dev": true
         },
+        "cli-boxes": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+            "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
+            "dev": true
+        },
+        "cli-cursor": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
+            "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+            "dev": true,
+            "requires": {
+                "restore-cursor": "^4.0.0"
+            }
+        },
+        "cli-truncate": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz",
+            "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==",
+            "dev": true,
+            "requires": {
+                "slice-ansi": "^5.0.0",
+                "string-width": "^5.0.0"
+            },
+            "dependencies": {
+                "ansi-styles": {
+                    "version": "6.2.1",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+                    "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+                    "dev": true
+                },
+                "slice-ansi": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+                    "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^6.0.0",
+                        "is-fullwidth-code-point": "^4.0.0"
+                    }
+                }
+            }
+        },
         "cliui": {
-            "version": "7.0.4",
-            "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
-            "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+            "version": "8.0.1",
+            "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+            "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
             "dev": true,
             "requires": {
                 "string-width": "^4.2.0",
-                "strip-ansi": "^6.0.0",
+                "strip-ansi": "^6.0.1",
                 "wrap-ansi": "^7.0.0"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
+                    }
+                },
+                "wrap-ansi": {
+                    "version": "7.0.0",
+                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+                    "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^4.0.0",
+                        "string-width": "^4.1.0",
+                        "strip-ansi": "^6.0.0"
+                    }
+                }
+            }
+        },
+        "code-excerpt": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz",
+            "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==",
+            "dev": true,
+            "requires": {
+                "convert-to-spaces": "^2.0.1"
             }
         },
         "color-convert": {
@@ -5699,23 +7119,29 @@
             "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
             "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="
         },
-        "commondir": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-            "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
-            "dev": true
-        },
         "concat-map": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
             "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
         },
+        "console-control-strings": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+            "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+            "dev": true
+        },
         "convert-source-map": {
             "version": "1.9.0",
             "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
             "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
             "dev": true
         },
+        "convert-to-spaces": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz",
+            "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==",
+            "dev": true
+        },
         "cross-spawn": {
             "version": "7.0.3",
             "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -5734,25 +7160,16 @@
                 "ms": "2.1.2"
             }
         },
-        "decamelize": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-            "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
-            "dev": true
-        },
         "deep-is": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
             "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
         },
-        "default-require-extensions": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz",
-            "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==",
-            "dev": true,
-            "requires": {
-                "strip-bom": "^4.0.0"
-            }
+        "delegates": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+            "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+            "dev": true
         },
         "diff": {
             "version": "4.0.2",
@@ -5768,22 +7185,38 @@
                 "esutils": "^2.0.2"
             }
         },
-        "electron-to-chromium": {
-            "version": "1.4.340",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.340.tgz",
-            "integrity": "sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg==",
+        "eastasianwidth": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+            "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
             "dev": true
         },
         "emoji-regex": {
-            "version": "8.0.0",
-            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "version": "9.2.2",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+            "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
             "dev": true
         },
-        "es6-error": {
-            "version": "4.1.1",
-            "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
-            "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+        "encoding": {
+            "version": "0.1.13",
+            "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+            "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+            "dev": true,
+            "optional": true,
+            "requires": {
+                "iconv-lite": "^0.6.2"
+            }
+        },
+        "env-paths": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+            "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+            "dev": true
+        },
+        "err-code": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+            "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
             "dev": true
         },
         "escalade": {
@@ -5842,41 +7275,6 @@
                 "strip-ansi": "^6.0.1",
                 "strip-json-comments": "^3.1.0",
                 "text-table": "^0.2.0"
-            },
-            "dependencies": {
-                "find-up": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
-                    "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
-                    "requires": {
-                        "locate-path": "^6.0.0",
-                        "path-exists": "^4.0.0"
-                    }
-                },
-                "locate-path": {
-                    "version": "6.0.0",
-                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
-                    "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
-                    "requires": {
-                        "p-locate": "^5.0.0"
-                    }
-                },
-                "p-limit": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
-                    "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
-                    "requires": {
-                        "yocto-queue": "^0.1.0"
-                    }
-                },
-                "p-locate": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
-                    "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
-                    "requires": {
-                        "p-limit": "^3.0.2"
-                    }
-                }
             }
         },
         "eslint-scope": {
@@ -5903,12 +7301,6 @@
                 "eslint-visitor-keys": "^3.4.0"
             }
         },
-        "esprima": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-            "dev": true
-        },
         "esquery": {
             "version": "1.5.0",
             "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
@@ -5936,9 +7328,15 @@
             "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
         },
         "events-to-array": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz",
-            "integrity": "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==",
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-2.0.3.tgz",
+            "integrity": "sha512-f/qE2gImHRa4Cp2y1stEOSgw8wTFyUdVJX7G//bMwbaV9JqISFxg99NbmVQeP7YLnDUZ2un851jlaDrlpmGehQ==",
+            "dev": true
+        },
+        "exponential-backoff": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz",
+            "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==",
             "dev": true
         },
         "fast-deep-equal": {
@@ -5981,33 +7379,15 @@
                 "to-regex-range": "^5.0.1"
             }
         },
-        "find-cache-dir": {
-            "version": "3.3.2",
-            "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
-            "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
-            "dev": true,
-            "requires": {
-                "commondir": "^1.0.1",
-                "make-dir": "^3.0.2",
-                "pkg-dir": "^4.1.0"
-            }
-        },
         "find-up": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-            "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-            "dev": true,
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+            "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
             "requires": {
-                "locate-path": "^5.0.0",
+                "locate-path": "^6.0.0",
                 "path-exists": "^4.0.0"
             }
         },
-        "findit": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz",
-            "integrity": "sha512-ENZS237/Hr8bjczn5eKuBohLgaD0JyUd0arxretR1f9RO46vZHA1b2y0VorgGV3WaOT3c+78P8h7v4JGJ1i/rg==",
-            "dev": true
-        },
         "flat-cache": {
             "version": "3.0.4",
             "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -6023,13 +7403,13 @@
             "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg=="
         },
         "foreground-child": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
-            "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
+            "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
             "dev": true,
             "requires": {
                 "cross-spawn": "^7.0.0",
-                "signal-exit": "^3.0.2"
+                "signal-exit": "^4.0.1"
             }
         },
         "fromentries": {
@@ -6038,11 +7418,14 @@
             "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
             "dev": true
         },
-        "fs-exists-cached": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz",
-            "integrity": "sha512-kSxoARUDn4F2RPXX48UXnaFKwVU7Ivd/6qpzZL29MCDmr9sTvybv4gFCp+qaI4fM9m0z9fgz/yJvi56GAz+BZg==",
-            "dev": true
+        "fs-minipass": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
+            "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
+            "dev": true,
+            "requires": {
+                "minipass": "^7.0.3"
+            }
         },
         "fs.realpath": {
             "version": "1.0.0",
@@ -6056,30 +7439,71 @@
             "dev": true,
             "optional": true
         },
-        "function-loop": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz",
-            "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==",
+        "function-bind": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
             "dev": true
         },
-        "gensync": {
-            "version": "1.0.0-beta.2",
-            "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
-            "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+        "function-loop": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-4.0.0.tgz",
+            "integrity": "sha512-f34iQBedYF3XcI93uewZZOnyscDragxgTK/eTvVB74k3fCD0ZorOi5BV9GS4M8rz/JoNi0Kl3qX5Y9MH3S/CLQ==",
             "dev": true
         },
+        "gauge": {
+            "version": "4.0.4",
+            "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
+            "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+            "dev": true,
+            "requires": {
+                "aproba": "^1.0.3 || ^2.0.0",
+                "color-support": "^1.1.3",
+                "console-control-strings": "^1.1.0",
+                "has-unicode": "^2.0.1",
+                "signal-exit": "^3.0.7",
+                "string-width": "^4.2.3",
+                "strip-ansi": "^6.0.1",
+                "wide-align": "^1.1.5"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                },
+                "signal-exit": {
+                    "version": "3.0.7",
+                    "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+                    "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
+                    }
+                }
+            }
+        },
         "get-caller-file": {
             "version": "2.0.5",
             "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
             "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
             "dev": true
         },
-        "get-package-type": {
-            "version": "0.1.0",
-            "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
-            "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
-            "dev": true
-        },
         "glob": {
             "version": "7.2.3",
             "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -6120,45 +7544,126 @@
             "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
             "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
         },
+        "has": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+            "dev": true,
+            "requires": {
+                "function-bind": "^1.1.1"
+            }
+        },
         "has-flag": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
             "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
         },
-        "hasha": {
-            "version": "5.2.2",
-            "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
-            "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
-            "dev": true,
-            "requires": {
-                "is-stream": "^2.0.0",
-                "type-fest": "^0.8.0"
-            },
-            "dependencies": {
-                "type-fest": {
-                    "version": "0.8.1",
-                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
-                    "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
-                    "dev": true
-                }
-            }
+        "has-unicode": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+            "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+            "dev": true
         },
         "he": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
             "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
         },
+        "hosted-git-info": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz",
+            "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==",
+            "dev": true,
+            "requires": {
+                "lru-cache": "^10.0.1"
+            }
+        },
         "html-escaper": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
             "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
             "dev": true
         },
+        "http-cache-semantics": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+            "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
+            "dev": true
+        },
+        "http-proxy-agent": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+            "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+            "dev": true,
+            "requires": {
+                "@tootallnate/once": "2",
+                "agent-base": "6",
+                "debug": "4"
+            }
+        },
+        "https-proxy-agent": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+            "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+            "dev": true,
+            "requires": {
+                "agent-base": "6",
+                "debug": "4"
+            }
+        },
+        "humanize-ms": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+            "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+            "dev": true,
+            "requires": {
+                "ms": "^2.0.0"
+            }
+        },
+        "iconv-lite": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+            "dev": true,
+            "optional": true,
+            "requires": {
+                "safer-buffer": ">= 2.1.2 < 3.0.0"
+            }
+        },
         "ignore": {
             "version": "5.2.4",
             "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
             "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ=="
         },
+        "ignore-walk": {
+            "version": "6.0.3",
+            "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.3.tgz",
+            "integrity": "sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==",
+            "dev": true,
+            "requires": {
+                "minimatch": "^9.0.0"
+            },
+            "dependencies": {
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+                    "dev": true,
+                    "requires": {
+                        "balanced-match": "^1.0.0"
+                    }
+                },
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                }
+            }
+        },
         "image-size": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz",
@@ -6182,9 +7687,9 @@
             "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
         },
         "indent-string": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
-            "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+            "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
             "dev": true
         },
         "inflight": {
@@ -6201,6 +7706,71 @@
             "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
             "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
         },
+        "ink": {
+            "version": "4.4.1",
+            "resolved": "https://registry.npmjs.org/ink/-/ink-4.4.1.tgz",
+            "integrity": "sha512-rXckvqPBB0Krifk5rn/5LvQGmyXwCUpBfmTwbkQNBY9JY8RSl3b8OftBNEYxg4+SWUhEKcPifgope28uL9inlA==",
+            "dev": true,
+            "requires": {
+                "@alcalzone/ansi-tokenize": "^0.1.3",
+                "ansi-escapes": "^6.0.0",
+                "auto-bind": "^5.0.1",
+                "chalk": "^5.2.0",
+                "cli-boxes": "^3.0.0",
+                "cli-cursor": "^4.0.0",
+                "cli-truncate": "^3.1.0",
+                "code-excerpt": "^4.0.0",
+                "indent-string": "^5.0.0",
+                "is-ci": "^3.0.1",
+                "is-lower-case": "^2.0.2",
+                "is-upper-case": "^2.0.2",
+                "lodash": "^4.17.21",
+                "patch-console": "^2.0.0",
+                "react-reconciler": "^0.29.0",
+                "scheduler": "^0.23.0",
+                "signal-exit": "^3.0.7",
+                "slice-ansi": "^6.0.0",
+                "stack-utils": "^2.0.6",
+                "string-width": "^5.1.2",
+                "type-fest": "^0.12.0",
+                "widest-line": "^4.0.1",
+                "wrap-ansi": "^8.1.0",
+                "ws": "^8.12.0",
+                "yoga-wasm-web": "~0.3.3"
+            },
+            "dependencies": {
+                "chalk": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+                    "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+                    "dev": true
+                },
+                "signal-exit": {
+                    "version": "3.0.7",
+                    "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+                    "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+                    "dev": true
+                },
+                "type-fest": {
+                    "version": "0.12.0",
+                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz",
+                    "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==",
+                    "dev": true
+                }
+            }
+        },
+        "ip": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
+            "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
+            "dev": true
+        },
+        "is-actual-promise": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-actual-promise/-/is-actual-promise-1.0.0.tgz",
+            "integrity": "sha512-DWSmKTiEoY3Y9LGHG9TVnFgydCCu+3fLJi4rv3fpi0gL/lKoILekh/oF/nO3/Lq1l5Rqo+tQt5TWzxMmYIhWyg==",
+            "dev": true
+        },
         "is-binary-path": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -6210,15 +7780,33 @@
                 "binary-extensions": "^2.0.0"
             }
         },
+        "is-ci": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
+            "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+            "dev": true,
+            "requires": {
+                "ci-info": "^3.2.0"
+            }
+        },
+        "is-core-module": {
+            "version": "2.13.0",
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz",
+            "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==",
+            "dev": true,
+            "requires": {
+                "has": "^1.0.3"
+            }
+        },
         "is-extglob": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
             "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
         },
         "is-fullwidth-code-point": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+            "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
             "dev": true
         },
         "is-glob": {
@@ -6229,6 +7817,21 @@
                 "is-extglob": "^2.1.1"
             }
         },
+        "is-lambda": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
+            "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+            "dev": true
+        },
+        "is-lower-case": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz",
+            "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==",
+            "dev": true,
+            "requires": {
+                "tslib": "^2.0.3"
+            }
+        },
         "is-number": {
             "version": "7.0.0",
             "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -6240,23 +7843,20 @@
             "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
             "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="
         },
-        "is-stream": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-            "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
-            "dev": true
-        },
-        "is-typedarray": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-            "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+        "is-plain-object": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+            "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
             "dev": true
         },
-        "is-windows": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-            "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
-            "dev": true
+        "is-upper-case": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz",
+            "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==",
+            "dev": true,
+            "requires": {
+                "tslib": "^2.0.3"
+            }
         },
         "isexe": {
             "version": "2.0.0",
@@ -6269,67 +7869,21 @@
             "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
             "dev": true
         },
-        "istanbul-lib-hook": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz",
-            "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==",
-            "dev": true,
-            "requires": {
-                "append-transform": "^2.0.0"
-            }
-        },
-        "istanbul-lib-instrument": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
-            "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
-            "dev": true,
-            "requires": {
-                "@babel/core": "^7.7.5",
-                "@istanbuljs/schema": "^0.1.2",
-                "istanbul-lib-coverage": "^3.0.0",
-                "semver": "^6.3.0"
-            }
-        },
-        "istanbul-lib-processinfo": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz",
-            "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==",
-            "dev": true,
-            "requires": {
-                "archy": "^1.0.0",
-                "cross-spawn": "^7.0.3",
-                "istanbul-lib-coverage": "^3.2.0",
-                "p-map": "^3.0.0",
-                "rimraf": "^3.0.0",
-                "uuid": "^8.3.2"
-            }
-        },
         "istanbul-lib-report": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
-            "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+            "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
             "dev": true,
             "requires": {
                 "istanbul-lib-coverage": "^3.0.0",
-                "make-dir": "^3.0.0",
+                "make-dir": "^4.0.0",
                 "supports-color": "^7.1.0"
             }
         },
-        "istanbul-lib-source-maps": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
-            "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
-            "dev": true,
-            "requires": {
-                "debug": "^4.1.1",
-                "istanbul-lib-coverage": "^3.0.0",
-                "source-map": "^0.6.1"
-            }
-        },
         "istanbul-reports": {
-            "version": "3.1.5",
-            "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
-            "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
+            "version": "3.1.6",
+            "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz",
+            "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==",
             "dev": true,
             "requires": {
                 "html-escaper": "^2.0.0",
@@ -6337,12 +7891,13 @@
             }
         },
         "jackspeak": {
-            "version": "1.4.2",
-            "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.2.tgz",
-            "integrity": "sha512-GHeGTmnuaHnvS+ZctRB01bfxARuu9wW83ENbuiweu07SFcVlZrJpcshSre/keGT7YGBhLHg/+rXCNSrsEHKU4Q==",
+            "version": "2.3.6",
+            "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
+            "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
             "dev": true,
             "requires": {
-                "cliui": "^7.0.4"
+                "@isaacs/cliui": "^8.0.2",
+                "@pkgjs/parseargs": "^0.11.0"
             }
         },
         "js-sdsl": {
@@ -6364,10 +7919,10 @@
                 "argparse": "^2.0.1"
             }
         },
-        "jsesc": {
-            "version": "2.5.2",
-            "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
-            "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+        "json-parse-even-better-errors": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz",
+            "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==",
             "dev": true
         },
         "json-schema-traverse": {
@@ -6380,10 +7935,10 @@
             "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
             "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
         },
-        "json5": {
-            "version": "2.2.3",
-            "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
-            "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+        "jsonparse": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+            "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
             "dev": true
         },
         "levn": {
@@ -6395,51 +7950,18 @@
                 "type-check": "~0.4.0"
             }
         },
-        "libtap": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.4.0.tgz",
-            "integrity": "sha512-STLFynswQ2A6W14JkabgGetBNk6INL1REgJ9UeNKw5llXroC2cGLgKTqavv0sl8OLVztLLipVKMcQ7yeUcqpmg==",
-            "dev": true,
-            "requires": {
-                "async-hook-domain": "^2.0.4",
-                "bind-obj-methods": "^3.0.0",
-                "diff": "^4.0.2",
-                "function-loop": "^2.0.1",
-                "minipass": "^3.1.5",
-                "own-or": "^1.0.0",
-                "own-or-env": "^1.0.2",
-                "signal-exit": "^3.0.4",
-                "stack-utils": "^2.0.4",
-                "tap-parser": "^11.0.0",
-                "tap-yaml": "^1.0.0",
-                "tcompare": "^5.0.6",
-                "trivial-deferred": "^1.0.1"
-            },
-            "dependencies": {
-                "tcompare": {
-                    "version": "5.0.7",
-                    "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz",
-                    "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==",
-                    "dev": true,
-                    "requires": {
-                        "diff": "^4.0.2"
-                    }
-                }
-            }
-        },
         "locate-path": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-            "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-            "dev": true,
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+            "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
             "requires": {
-                "p-locate": "^4.1.0"
+                "p-locate": "^5.0.0"
             }
         },
-        "lodash.flattendeep": {
-            "version": "4.4.0",
-            "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
-            "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
+        "lodash": {
+            "version": "4.17.21",
+            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+            "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
             "dev": true
         },
         "lodash.merge": {
@@ -6447,30 +7969,130 @@
             "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
             "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
         },
-        "lru-cache": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-            "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+        "loose-envify": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+            "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
             "dev": true,
             "requires": {
-                "yallist": "^3.0.2"
-            },
-            "dependencies": {
-                "yallist": {
-                    "version": "3.1.1",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-                    "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-                    "dev": true
-                }
+                "js-tokens": "^3.0.0 || ^4.0.0"
             }
         },
+        "lru-cache": {
+            "version": "10.0.1",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz",
+            "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==",
+            "dev": true
+        },
         "make-dir": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
-            "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+            "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
             "dev": true,
             "requires": {
-                "semver": "^6.0.0"
+                "semver": "^7.5.3"
+            }
+        },
+        "make-error": {
+            "version": "1.3.6",
+            "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+            "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+            "dev": true
+        },
+        "make-fetch-happen": {
+            "version": "11.1.1",
+            "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz",
+            "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==",
+            "dev": true,
+            "requires": {
+                "agentkeepalive": "^4.2.1",
+                "cacache": "^17.0.0",
+                "http-cache-semantics": "^4.1.1",
+                "http-proxy-agent": "^5.0.0",
+                "https-proxy-agent": "^5.0.0",
+                "is-lambda": "^1.0.1",
+                "lru-cache": "^7.7.1",
+                "minipass": "^5.0.0",
+                "minipass-fetch": "^3.0.0",
+                "minipass-flush": "^1.0.5",
+                "minipass-pipeline": "^1.2.4",
+                "negotiator": "^0.6.3",
+                "promise-retry": "^2.0.1",
+                "socks-proxy-agent": "^7.0.0",
+                "ssri": "^10.0.0"
+            },
+            "dependencies": {
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+                    "dev": true,
+                    "requires": {
+                        "balanced-match": "^1.0.0"
+                    }
+                },
+                "cacache": {
+                    "version": "17.1.4",
+                    "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz",
+                    "integrity": "sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==",
+                    "dev": true,
+                    "requires": {
+                        "@npmcli/fs": "^3.1.0",
+                        "fs-minipass": "^3.0.0",
+                        "glob": "^10.2.2",
+                        "lru-cache": "^7.7.1",
+                        "minipass": "^7.0.3",
+                        "minipass-collect": "^1.0.2",
+                        "minipass-flush": "^1.0.5",
+                        "minipass-pipeline": "^1.2.4",
+                        "p-map": "^4.0.0",
+                        "ssri": "^10.0.0",
+                        "tar": "^6.1.11",
+                        "unique-filename": "^3.0.0"
+                    },
+                    "dependencies": {
+                        "minipass": {
+                            "version": "7.0.4",
+                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+                            "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
+                            "dev": true
+                        }
+                    }
+                },
+                "glob": {
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+                    "dev": true,
+                    "requires": {
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
+                    }
+                },
+                "lru-cache": {
+                    "version": "7.18.3",
+                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+                    "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+                    "dev": true
+                },
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                },
+                "minipass": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+                    "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+                    "dev": true
+                }
             }
         },
         "marked": {
@@ -6478,6 +8100,12 @@
             "resolved": "https://registry.npmjs.org/marked/-/marked-5.0.2.tgz",
             "integrity": "sha512-TXksm9GwqXCRNbFUZmMtqNLvy3K2cQHuWmyBDLOrY1e6i9UvZpOTJXoz7fBjYkJkaUFzV9hBFxMuZSyQt8R6KQ=="
         },
+        "mimic-fn": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+            "dev": true
+        },
         "minimatch": {
             "version": "3.1.2",
             "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -6487,18 +8115,149 @@
             }
         },
         "minipass": {
-            "version": "3.3.6",
-            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
-            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+            "version": "7.0.4",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+            "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
+            "dev": true
+        },
+        "minipass-collect": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+            "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
             "dev": true,
             "requires": {
+                "minipass": "^3.0.0"
+            },
+            "dependencies": {
+                "minipass": {
+                    "version": "3.3.6",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+                    "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+                    "dev": true,
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                }
+            }
+        },
+        "minipass-fetch": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz",
+            "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==",
+            "dev": true,
+            "requires": {
+                "encoding": "^0.1.13",
+                "minipass": "^7.0.3",
+                "minipass-sized": "^1.0.3",
+                "minizlib": "^2.1.2"
+            }
+        },
+        "minipass-flush": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+            "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+            "dev": true,
+            "requires": {
+                "minipass": "^3.0.0"
+            },
+            "dependencies": {
+                "minipass": {
+                    "version": "3.3.6",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+                    "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+                    "dev": true,
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                }
+            }
+        },
+        "minipass-json-stream": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz",
+            "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==",
+            "dev": true,
+            "requires": {
+                "jsonparse": "^1.3.1",
+                "minipass": "^3.0.0"
+            },
+            "dependencies": {
+                "minipass": {
+                    "version": "3.3.6",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+                    "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+                    "dev": true,
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                }
+            }
+        },
+        "minipass-pipeline": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+            "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+            "dev": true,
+            "requires": {
+                "minipass": "^3.0.0"
+            },
+            "dependencies": {
+                "minipass": {
+                    "version": "3.3.6",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+                    "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+                    "dev": true,
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                }
+            }
+        },
+        "minipass-sized": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
+            "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+            "dev": true,
+            "requires": {
+                "minipass": "^3.0.0"
+            },
+            "dependencies": {
+                "minipass": {
+                    "version": "3.3.6",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+                    "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+                    "dev": true,
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                }
+            }
+        },
+        "minizlib": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+            "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+            "dev": true,
+            "requires": {
+                "minipass": "^3.0.0",
                 "yallist": "^4.0.0"
+            },
+            "dependencies": {
+                "minipass": {
+                    "version": "3.3.6",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+                    "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+                    "dev": true,
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                }
             }
         },
         "mkdirp": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-            "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+            "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
             "dev": true
         },
         "ms": {
@@ -6511,20 +8270,51 @@
             "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
             "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
         },
-        "node-preload": {
-            "version": "0.2.1",
-            "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
-            "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==",
+        "negotiator": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+            "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+            "dev": true
+        },
+        "node-gyp": {
+            "version": "9.4.0",
+            "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz",
+            "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==",
             "dev": true,
             "requires": {
-                "process-on-spawn": "^1.0.0"
+                "env-paths": "^2.2.0",
+                "exponential-backoff": "^3.1.1",
+                "glob": "^7.1.4",
+                "graceful-fs": "^4.2.6",
+                "make-fetch-happen": "^11.0.3",
+                "nopt": "^6.0.0",
+                "npmlog": "^6.0.0",
+                "rimraf": "^3.0.2",
+                "semver": "^7.3.5",
+                "tar": "^6.1.2",
+                "which": "^2.0.2"
             }
         },
-        "node-releases": {
-            "version": "2.0.10",
-            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
-            "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
-            "dev": true
+        "nopt": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz",
+            "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==",
+            "dev": true,
+            "requires": {
+                "abbrev": "^1.0.0"
+            }
+        },
+        "normalize-package-data": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz",
+            "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==",
+            "dev": true,
+            "requires": {
+                "hosted-git-info": "^7.0.0",
+                "is-core-module": "^2.8.1",
+                "semver": "^7.3.5",
+                "validate-npm-package-license": "^3.0.4"
+            }
         },
         "normalize-path": {
             "version": "3.0.0",
@@ -6532,49 +8322,111 @@
             "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
             "dev": true
         },
-        "nyc": {
-            "version": "15.1.0",
-            "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
-            "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==",
+        "npm-bundled": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz",
+            "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==",
             "dev": true,
             "requires": {
-                "@istanbuljs/load-nyc-config": "^1.0.0",
-                "@istanbuljs/schema": "^0.1.2",
-                "caching-transform": "^4.0.0",
-                "convert-source-map": "^1.7.0",
-                "decamelize": "^1.2.0",
-                "find-cache-dir": "^3.2.0",
-                "find-up": "^4.1.0",
-                "foreground-child": "^2.0.0",
-                "get-package-type": "^0.1.0",
-                "glob": "^7.1.6",
-                "istanbul-lib-coverage": "^3.0.0",
-                "istanbul-lib-hook": "^3.0.0",
-                "istanbul-lib-instrument": "^4.0.0",
-                "istanbul-lib-processinfo": "^2.0.2",
-                "istanbul-lib-report": "^3.0.0",
-                "istanbul-lib-source-maps": "^4.0.0",
-                "istanbul-reports": "^3.0.2",
-                "make-dir": "^3.0.0",
-                "node-preload": "^0.2.1",
-                "p-map": "^3.0.0",
-                "process-on-spawn": "^1.0.0",
-                "resolve-from": "^5.0.0",
-                "rimraf": "^3.0.0",
-                "signal-exit": "^3.0.2",
-                "spawn-wrap": "^2.0.0",
-                "test-exclude": "^6.0.0",
-                "yargs": "^15.0.2"
-            },
-            "dependencies": {
-                "resolve-from": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
-                    "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
-                    "dev": true
+                "npm-normalize-package-bin": "^3.0.0"
+            }
+        },
+        "npm-install-checks": {
+            "version": "6.2.0",
+            "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.2.0.tgz",
+            "integrity": "sha512-744wat5wAAHsxa4590mWO0tJ8PKxR8ORZsH9wGpQc3nWTzozMAgBN/XyqYw7mg3yqLM8dLwEnwSfKMmXAjF69g==",
+            "dev": true,
+            "requires": {
+                "semver": "^7.1.1"
+            }
+        },
+        "npm-normalize-package-bin": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz",
+            "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==",
+            "dev": true
+        },
+        "npm-package-arg": {
+            "version": "11.0.1",
+            "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz",
+            "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==",
+            "dev": true,
+            "requires": {
+                "hosted-git-info": "^7.0.0",
+                "proc-log": "^3.0.0",
+                "semver": "^7.3.5",
+                "validate-npm-package-name": "^5.0.0"
+            }
+        },
+        "npm-packlist": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.0.tgz",
+            "integrity": "sha512-ErAGFB5kJUciPy1mmx/C2YFbvxoJ0QJ9uwkCZOeR6CqLLISPZBOiFModAbSXnjjlwW5lOhuhXva+fURsSGJqyw==",
+            "dev": true,
+            "requires": {
+                "ignore-walk": "^6.0.0"
+            }
+        },
+        "npm-pick-manifest": {
+            "version": "9.0.0",
+            "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz",
+            "integrity": "sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==",
+            "dev": true,
+            "requires": {
+                "npm-install-checks": "^6.0.0",
+                "npm-normalize-package-bin": "^3.0.0",
+                "npm-package-arg": "^11.0.0",
+                "semver": "^7.3.5"
+            }
+        },
+        "npm-registry-fetch": {
+            "version": "16.0.0",
+            "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.0.0.tgz",
+            "integrity": "sha512-JFCpAPUpvpwfSydv99u85yhP68rNIxSFmDpNbNnRWKSe3gpjHnWL8v320gATwRzjtgmZ9Jfe37+ZPOLZPwz6BQ==",
+            "dev": true,
+            "requires": {
+                "make-fetch-happen": "^13.0.0",
+                "minipass": "^7.0.2",
+                "minipass-fetch": "^3.0.0",
+                "minipass-json-stream": "^1.0.1",
+                "minizlib": "^2.1.2",
+                "npm-package-arg": "^11.0.0",
+                "proc-log": "^3.0.0"
+            },
+            "dependencies": {
+                "make-fetch-happen": {
+                    "version": "13.0.0",
+                    "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz",
+                    "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==",
+                    "dev": true,
+                    "requires": {
+                        "@npmcli/agent": "^2.0.0",
+                        "cacache": "^18.0.0",
+                        "http-cache-semantics": "^4.1.1",
+                        "is-lambda": "^1.0.1",
+                        "minipass": "^7.0.2",
+                        "minipass-fetch": "^3.0.0",
+                        "minipass-flush": "^1.0.5",
+                        "minipass-pipeline": "^1.2.4",
+                        "negotiator": "^0.6.3",
+                        "promise-retry": "^2.0.1",
+                        "ssri": "^10.0.0"
+                    }
                 }
             }
         },
+        "npmlog": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
+            "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
+            "dev": true,
+            "requires": {
+                "are-we-there-yet": "^3.0.0",
+                "console-control-strings": "^1.1.0",
+                "gauge": "^4.0.3",
+                "set-blocking": "^2.0.0"
+            }
+        },
         "once": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -6583,6 +8435,15 @@
                 "wrappy": "1"
             }
         },
+        "onetime": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+            "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+            "dev": true,
+            "requires": {
+                "mimic-fn": "^2.1.0"
+            }
+        },
         "opener": {
             "version": "1.5.2",
             "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
@@ -6602,64 +8463,55 @@
                 "word-wrap": "^1.2.3"
             }
         },
-        "own-or": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz",
-            "integrity": "sha512-NfZr5+Tdf6MB8UI9GLvKRs4cXY8/yB0w3xtt84xFdWy8hkGjn+JFc60VhzS/hFRfbyxFcGYMTjnF4Me+RbbqrA==",
-            "dev": true
-        },
-        "own-or-env": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.2.tgz",
-            "integrity": "sha512-NQ7v0fliWtK7Lkb+WdFqe6ky9XAzYmlkXthQrBbzlYbmFKoAYbDDcwmOm6q8kOuwSRXW8bdL5ORksploUJmWgw==",
-            "dev": true,
-            "requires": {
-                "own-or": "^1.0.0"
-            }
-        },
         "p-limit": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-            "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-            "dev": true,
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+            "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
             "requires": {
-                "p-try": "^2.0.0"
+                "yocto-queue": "^0.1.0"
             }
         },
         "p-locate": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-            "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-            "dev": true,
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+            "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
             "requires": {
-                "p-limit": "^2.2.0"
+                "p-limit": "^3.0.2"
             }
         },
         "p-map": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
-            "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+            "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
             "dev": true,
             "requires": {
                 "aggregate-error": "^3.0.0"
             }
         },
-        "p-try": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-            "dev": true
-        },
-        "package-hash": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz",
-            "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==",
+        "pacote": {
+            "version": "17.0.4",
+            "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.4.tgz",
+            "integrity": "sha512-eGdLHrV/g5b5MtD5cTPyss+JxOlaOloSMG3UwPMAvL8ywaLJ6beONPF40K4KKl/UI6q5hTKCJq5rCu8tkF+7Dg==",
             "dev": true,
             "requires": {
-                "graceful-fs": "^4.1.15",
-                "hasha": "^5.0.0",
-                "lodash.flattendeep": "^4.4.0",
-                "release-zalgo": "^1.0.0"
+                "@npmcli/git": "^5.0.0",
+                "@npmcli/installed-package-contents": "^2.0.1",
+                "@npmcli/promise-spawn": "^7.0.0",
+                "@npmcli/run-script": "^7.0.0",
+                "cacache": "^18.0.0",
+                "fs-minipass": "^3.0.0",
+                "minipass": "^7.0.2",
+                "npm-package-arg": "^11.0.0",
+                "npm-packlist": "^8.0.0",
+                "npm-pick-manifest": "^9.0.0",
+                "npm-registry-fetch": "^16.0.0",
+                "proc-log": "^3.0.0",
+                "promise-retry": "^2.0.1",
+                "read-package-json": "^7.0.0",
+                "read-package-json-fast": "^3.0.0",
+                "sigstore": "^2.0.0",
+                "ssri": "^10.0.0",
+                "tar": "^6.1.11"
             }
         },
         "parent-module": {
@@ -6670,6 +8522,12 @@
                 "callsites": "^3.0.0"
             }
         },
+        "patch-console": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz",
+            "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==",
+            "dev": true
+        },
         "path-exists": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -6685,11 +8543,15 @@
             "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
             "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
         },
-        "picocolors": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-            "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
-            "dev": true
+        "path-scurry": {
+            "version": "1.10.1",
+            "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
+            "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
+            "dev": true,
+            "requires": {
+                "lru-cache": "^9.1.1 || ^10.0.0",
+                "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+            }
         },
         "picomatch": {
             "version": "2.3.1",
@@ -6697,20 +8559,54 @@
             "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
             "dev": true
         },
-        "pkg-dir": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
-            "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
-            "dev": true,
-            "requires": {
-                "find-up": "^4.0.0"
-            }
+        "pirates": {
+            "version": "4.0.6",
+            "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+            "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+            "dev": true
+        },
+        "polite-json": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/polite-json/-/polite-json-4.0.1.tgz",
+            "integrity": "sha512-8LI5ZeCPBEb4uBbcYKNVwk4jgqNx1yHReWoW4H4uUihWlSqZsUDfSITrRhjliuPgxsNPFhNSudGO2Zu4cbWinQ==",
+            "dev": true
         },
         "prelude-ls": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
             "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
         },
+        "prismjs": {
+            "version": "1.29.0",
+            "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
+            "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
+            "dev": true
+        },
+        "prismjs-terminal": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/prismjs-terminal/-/prismjs-terminal-1.2.3.tgz",
+            "integrity": "sha512-xc0zuJ5FMqvW+DpiRkvxURlz98DdfDsZcFHdO699+oL+ykbFfgI7O4VDEgUyc07BSL2NHl3zdb8m/tZ/aaqUrw==",
+            "dev": true,
+            "requires": {
+                "chalk": "^5.2.0",
+                "prismjs": "^1.29.0",
+                "string-length": "^6.0.0"
+            },
+            "dependencies": {
+                "chalk": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+                    "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+                    "dev": true
+                }
+            }
+        },
+        "proc-log": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
+            "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==",
+            "dev": true
+        },
         "process-on-spawn": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz",
@@ -6720,6 +8616,22 @@
                 "fromentries": "^1.2.0"
             }
         },
+        "promise-inflight": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+            "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+            "dev": true
+        },
+        "promise-retry": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+            "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+            "dev": true,
+            "requires": {
+                "err-code": "^2.0.2",
+                "retry": "^0.12.0"
+            }
+        },
         "punycode": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -6738,6 +8650,119 @@
             "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
             "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
         },
+        "react": {
+            "version": "18.2.0",
+            "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+            "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+            "dev": true,
+            "requires": {
+                "loose-envify": "^1.1.0"
+            }
+        },
+        "react-dom": {
+            "version": "18.2.0",
+            "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+            "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
+            "dev": true,
+            "peer": true,
+            "requires": {
+                "loose-envify": "^1.1.0",
+                "scheduler": "^0.23.0"
+            }
+        },
+        "react-element-to-jsx-string": {
+            "version": "15.0.0",
+            "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz",
+            "integrity": "sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==",
+            "dev": true,
+            "requires": {
+                "@base2/pretty-print-object": "1.0.1",
+                "is-plain-object": "5.0.0",
+                "react-is": "18.1.0"
+            }
+        },
+        "react-is": {
+            "version": "18.1.0",
+            "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz",
+            "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==",
+            "dev": true
+        },
+        "react-reconciler": {
+            "version": "0.29.0",
+            "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.0.tgz",
+            "integrity": "sha512-wa0fGj7Zht1EYMRhKWwoo1H9GApxYLBuhoAuXN0TlltESAjDssB+Apf0T/DngVqaMyPypDmabL37vw/2aRM98Q==",
+            "dev": true,
+            "requires": {
+                "loose-envify": "^1.1.0",
+                "scheduler": "^0.23.0"
+            }
+        },
+        "read-package-json": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.0.tgz",
+            "integrity": "sha512-uL4Z10OKV4p6vbdvIXB+OzhInYtIozl/VxUBPgNkBuUi2DeRonnuspmaVAMcrkmfjKGNmRndyQAbE7/AmzGwFg==",
+            "dev": true,
+            "requires": {
+                "glob": "^10.2.2",
+                "json-parse-even-better-errors": "^3.0.0",
+                "normalize-package-data": "^6.0.0",
+                "npm-normalize-package-bin": "^3.0.0"
+            },
+            "dependencies": {
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+                    "dev": true,
+                    "requires": {
+                        "balanced-match": "^1.0.0"
+                    }
+                },
+                "glob": {
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+                    "dev": true,
+                    "requires": {
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
+                    }
+                },
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                }
+            }
+        },
+        "read-package-json-fast": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz",
+            "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==",
+            "dev": true,
+            "requires": {
+                "json-parse-even-better-errors": "^3.0.0",
+                "npm-normalize-package-bin": "^3.0.0"
+            }
+        },
+        "readable-stream": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+            "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+            "dev": true,
+            "requires": {
+                "inherits": "^2.0.3",
+                "string_decoder": "^1.1.1",
+                "util-deprecate": "^1.0.1"
+            }
+        },
         "readdirp": {
             "version": "3.6.0",
             "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -6747,32 +8772,84 @@
                 "picomatch": "^2.2.1"
             }
         },
-        "release-zalgo": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
-            "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==",
-            "dev": true,
-            "requires": {
-                "es6-error": "^4.0.1"
-            }
-        },
         "require-directory": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
             "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
             "dev": true
         },
-        "require-main-filename": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-            "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-            "dev": true
-        },
         "resolve-from": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
             "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
         },
+        "resolve-import": {
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/resolve-import/-/resolve-import-1.4.2.tgz",
+            "integrity": "sha512-ayUU3E2yeFu8ZewNEHbGorcPmHjOmCY8b50wloum8eQUuNExSyddRoWYaX0X6lj3XSufi2WUlXY3mkMcF5ISmw==",
+            "dev": true,
+            "requires": {
+                "glob": "^10.3.3",
+                "walk-up-path": "^3.0.1"
+            },
+            "dependencies": {
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+                    "dev": true,
+                    "requires": {
+                        "balanced-match": "^1.0.0"
+                    }
+                },
+                "glob": {
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+                    "dev": true,
+                    "requires": {
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
+                    }
+                },
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                }
+            }
+        },
+        "restore-cursor": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
+            "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
+            "dev": true,
+            "requires": {
+                "onetime": "^5.1.0",
+                "signal-exit": "^3.0.2"
+            },
+            "dependencies": {
+                "signal-exit": {
+                    "version": "3.0.7",
+                    "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+                    "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+                    "dev": true
+                }
+            }
+        },
+        "retry": {
+            "version": "0.12.0",
+            "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+            "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+            "dev": true
+        },
         "reusify": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -6794,12 +8871,48 @@
                 "queue-microtask": "^1.2.2"
             }
         },
-        "semver": {
-            "version": "6.3.0",
-            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+        "safe-buffer": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
             "dev": true
         },
+        "safer-buffer": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+            "dev": true,
+            "optional": true
+        },
+        "scheduler": {
+            "version": "0.23.0",
+            "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
+            "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+            "dev": true,
+            "requires": {
+                "loose-envify": "^1.1.0"
+            }
+        },
+        "semver": {
+            "version": "7.5.4",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+            "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+            "dev": true,
+            "requires": {
+                "lru-cache": "^6.0.0"
+            },
+            "dependencies": {
+                "lru-cache": {
+                    "version": "6.0.0",
+                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+                    "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+                    "dev": true,
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                }
+            }
+        },
         "set-blocking": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@@ -6820,47 +8933,109 @@
             "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
         },
         "signal-exit": {
-            "version": "3.0.7",
-            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+            "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
             "dev": true
         },
-        "source-map": {
-            "version": "0.6.1",
-            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+        "sigstore": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.1.0.tgz",
+            "integrity": "sha512-kPIj+ZLkyI3QaM0qX8V/nSsweYND3W448pwkDgS6CQ74MfhEkIR8ToK5Iyx46KJYRjseVcD3Rp9zAmUAj6ZjPw==",
+            "dev": true,
+            "requires": {
+                "@sigstore/bundle": "^2.1.0",
+                "@sigstore/protobuf-specs": "^0.2.1",
+                "@sigstore/sign": "^2.1.0",
+                "@sigstore/tuf": "^2.1.0"
+            }
+        },
+        "slice-ansi": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-6.0.0.tgz",
+            "integrity": "sha512-6bn4hRfkTvDfUoEQYkERg0BVF1D0vrX9HEkMl08uDiNWvVvjylLHvZFZWkDo6wjT8tUctbYl1nCOuE66ZTaUtA==",
+            "dev": true,
+            "requires": {
+                "ansi-styles": "^6.2.1",
+                "is-fullwidth-code-point": "^4.0.0"
+            },
+            "dependencies": {
+                "ansi-styles": {
+                    "version": "6.2.1",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+                    "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+                    "dev": true
+                }
+            }
+        },
+        "smart-buffer": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+            "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
             "dev": true
         },
-        "source-map-support": {
-            "version": "0.5.21",
-            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
-            "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+        "socks": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+            "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
             "dev": true,
             "requires": {
-                "buffer-from": "^1.0.0",
-                "source-map": "^0.6.0"
+                "ip": "^2.0.0",
+                "smart-buffer": "^4.2.0"
             }
         },
-        "spawn-wrap": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz",
-            "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==",
+        "socks-proxy-agent": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
+            "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
             "dev": true,
             "requires": {
-                "foreground-child": "^2.0.0",
-                "is-windows": "^1.0.2",
-                "make-dir": "^3.0.0",
-                "rimraf": "^3.0.0",
-                "signal-exit": "^3.0.2",
-                "which": "^2.0.1"
+                "agent-base": "^6.0.2",
+                "debug": "^4.3.3",
+                "socks": "^2.6.2"
             }
         },
-        "sprintf-js": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-            "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+        "spdx-correct": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+            "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+            "dev": true,
+            "requires": {
+                "spdx-expression-parse": "^3.0.0",
+                "spdx-license-ids": "^3.0.0"
+            }
+        },
+        "spdx-exceptions": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+            "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+            "dev": true
+        },
+        "spdx-expression-parse": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+            "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+            "dev": true,
+            "requires": {
+                "spdx-exceptions": "^2.1.0",
+                "spdx-license-ids": "^3.0.0"
+            }
+        },
+        "spdx-license-ids": {
+            "version": "3.0.15",
+            "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.15.tgz",
+            "integrity": "sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==",
             "dev": true
         },
+        "ssri": {
+            "version": "10.0.5",
+            "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz",
+            "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==",
+            "dev": true,
+            "requires": {
+                "minipass": "^7.0.3"
+            }
+        },
         "stack-utils": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -6878,8 +9053,71 @@
                 }
             }
         },
+        "string_decoder": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+            "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+            "dev": true,
+            "requires": {
+                "safe-buffer": "~5.2.0"
+            }
+        },
+        "string-length": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/string-length/-/string-length-6.0.0.tgz",
+            "integrity": "sha512-1U361pxZHEQ+FeSjzqRpV+cu2vTzYeWeafXFLykiFlv4Vc0n3njgU8HrMbyik5uwm77naWMuVG8fhEF+Ovb1Kg==",
+            "dev": true,
+            "requires": {
+                "strip-ansi": "^7.1.0"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+                    "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "7.1.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+                    "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^6.0.1"
+                    }
+                }
+            }
+        },
         "string-width": {
-            "version": "4.2.3",
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+            "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+            "dev": true,
+            "requires": {
+                "eastasianwidth": "^0.2.0",
+                "emoji-regex": "^9.2.2",
+                "strip-ansi": "^7.0.1"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+                    "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "7.1.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+                    "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^6.0.1"
+                    }
+                }
+            }
+        },
+        "string-width-cjs": {
+            "version": "npm:string-width@4.2.3",
             "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
             "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
             "dev": true,
@@ -6887,6 +9125,20 @@
                 "emoji-regex": "^8.0.0",
                 "is-fullwidth-code-point": "^3.0.0",
                 "strip-ansi": "^6.0.1"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                }
             }
         },
         "strip-ansi": {
@@ -6897,11 +9149,14 @@
                 "ansi-regex": "^5.0.1"
             }
         },
-        "strip-bom": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
-            "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
-            "dev": true
+        "strip-ansi-cjs": {
+            "version": "npm:strip-ansi@6.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+            "dev": true,
+            "requires": {
+                "ansi-regex": "^5.0.1"
+            }
         },
         "strip-json-comments": {
             "version": "3.1.1",
@@ -6921,1324 +9176,162 @@
                 "has-flag": "^4.0.0"
             }
         },
-        "tap": {
-            "version": "16.3.4",
-            "resolved": "https://registry.npmjs.org/tap/-/tap-16.3.4.tgz",
-            "integrity": "sha512-SAexdt2ZF4XBgye6TPucFI2y7VE0qeFXlXucJIV1XDPCs+iJodk0MYacr1zR6Ycltzz7PYg8zrblDXKbAZM2LQ==",
+        "sync-content": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/sync-content/-/sync-content-1.0.2.tgz",
+            "integrity": "sha512-znd3rYiiSxU3WteWyS9a6FXkTA/Wjk8WQsOyzHbineeL837dLn3DA4MRhsIX3qGcxDMH6+uuFV4axztssk7wEQ==",
             "dev": true,
             "requires": {
-                "@isaacs/import-jsx": "^4.0.1",
-                "@types/react": "^17.0.52",
-                "chokidar": "^3.3.0",
-                "findit": "^2.0.0",
-                "foreground-child": "^2.0.0",
-                "fs-exists-cached": "^1.0.0",
-                "glob": "^7.2.3",
-                "ink": "^3.2.0",
-                "isexe": "^2.0.0",
-                "istanbul-lib-processinfo": "^2.0.3",
-                "jackspeak": "^1.4.2",
-                "libtap": "^1.4.0",
-                "minipass": "^3.3.4",
-                "mkdirp": "^1.0.4",
-                "nyc": "^15.1.0",
-                "opener": "^1.5.1",
-                "react": "^17.0.2",
-                "rimraf": "^3.0.0",
-                "signal-exit": "^3.0.6",
-                "source-map-support": "^0.5.16",
-                "tap-mocha-reporter": "^5.0.3",
-                "tap-parser": "^11.0.2",
-                "tap-yaml": "^1.0.2",
-                "tcompare": "^5.0.7",
-                "treport": "^3.0.4",
-                "which": "^2.0.2"
+                "glob": "^10.2.6",
+                "mkdirp": "^3.0.1",
+                "path-scurry": "^1.9.2",
+                "rimraf": "^5.0.1"
             },
             "dependencies": {
-                "@ampproject/remapping": {
-                    "version": "2.1.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@jridgewell/trace-mapping": "^0.3.0"
-                    }
-                },
-                "@babel/code-frame": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/highlight": "^7.16.7"
-                    }
-                },
-                "@babel/compat-data": {
-                    "version": "7.17.7",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@babel/core": {
-                    "version": "7.17.8",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@ampproject/remapping": "^2.1.0",
-                        "@babel/code-frame": "^7.16.7",
-                        "@babel/generator": "^7.17.7",
-                        "@babel/helper-compilation-targets": "^7.17.7",
-                        "@babel/helper-module-transforms": "^7.17.7",
-                        "@babel/helpers": "^7.17.8",
-                        "@babel/parser": "^7.17.8",
-                        "@babel/template": "^7.16.7",
-                        "@babel/traverse": "^7.17.3",
-                        "@babel/types": "^7.17.0",
-                        "convert-source-map": "^1.7.0",
-                        "debug": "^4.1.0",
-                        "gensync": "^1.0.0-beta.2",
-                        "json5": "^2.1.2",
-                        "semver": "^6.3.0"
-                    }
-                },
-                "@babel/generator": {
-                    "version": "7.17.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.17.0",
-                        "jsesc": "^2.5.1",
-                        "source-map": "^0.5.0"
-                    }
-                },
-                "@babel/helper-annotate-as-pure": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/helper-compilation-targets": {
-                    "version": "7.17.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/compat-data": "^7.17.7",
-                        "@babel/helper-validator-option": "^7.16.7",
-                        "browserslist": "^4.17.5",
-                        "semver": "^6.3.0"
-                    }
-                },
-                "@babel/helper-environment-visitor": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/helper-function-name": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-get-function-arity": "^7.16.7",
-                        "@babel/template": "^7.16.7",
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/helper-get-function-arity": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/helper-hoist-variables": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/helper-module-imports": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/helper-module-transforms": {
-                    "version": "7.17.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-environment-visitor": "^7.16.7",
-                        "@babel/helper-module-imports": "^7.16.7",
-                        "@babel/helper-simple-access": "^7.17.7",
-                        "@babel/helper-split-export-declaration": "^7.16.7",
-                        "@babel/helper-validator-identifier": "^7.16.7",
-                        "@babel/template": "^7.16.7",
-                        "@babel/traverse": "^7.17.3",
-                        "@babel/types": "^7.17.0"
-                    }
-                },
-                "@babel/helper-plugin-utils": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@babel/helper-simple-access": {
-                    "version": "7.17.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.17.0"
-                    }
-                },
-                "@babel/helper-split-export-declaration": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/helper-validator-identifier": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@babel/helper-validator-option": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@babel/helpers": {
-                    "version": "7.17.8",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/template": "^7.16.7",
-                        "@babel/traverse": "^7.17.3",
-                        "@babel/types": "^7.17.0"
-                    }
-                },
-                "@babel/highlight": {
-                    "version": "7.16.10",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-validator-identifier": "^7.16.7",
-                        "chalk": "^2.0.0",
-                        "js-tokens": "^4.0.0"
-                    }
-                },
-                "@babel/parser": {
-                    "version": "7.17.8",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@babel/plugin-proposal-object-rest-spread": {
-                    "version": "7.17.3",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/compat-data": "^7.17.0",
-                        "@babel/helper-compilation-targets": "^7.16.7",
-                        "@babel/helper-plugin-utils": "^7.16.7",
-                        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
-                        "@babel/plugin-transform-parameters": "^7.16.7"
-                    }
-                },
-                "@babel/plugin-syntax-jsx": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-plugin-utils": "^7.16.7"
-                    }
-                },
-                "@babel/plugin-syntax-object-rest-spread": {
-                    "version": "7.8.3",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-plugin-utils": "^7.8.0"
-                    }
-                },
-                "@babel/plugin-transform-destructuring": {
-                    "version": "7.17.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-plugin-utils": "^7.16.7"
-                    }
-                },
-                "@babel/plugin-transform-parameters": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-plugin-utils": "^7.16.7"
-                    }
-                },
-                "@babel/plugin-transform-react-jsx": {
-                    "version": "7.17.3",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-annotate-as-pure": "^7.16.7",
-                        "@babel/helper-module-imports": "^7.16.7",
-                        "@babel/helper-plugin-utils": "^7.16.7",
-                        "@babel/plugin-syntax-jsx": "^7.16.7",
-                        "@babel/types": "^7.17.0"
-                    }
-                },
-                "@babel/template": {
-                    "version": "7.16.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/code-frame": "^7.16.7",
-                        "@babel/parser": "^7.16.7",
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/traverse": {
-                    "version": "7.17.3",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/code-frame": "^7.16.7",
-                        "@babel/generator": "^7.17.3",
-                        "@babel/helper-environment-visitor": "^7.16.7",
-                        "@babel/helper-function-name": "^7.16.7",
-                        "@babel/helper-hoist-variables": "^7.16.7",
-                        "@babel/helper-split-export-declaration": "^7.16.7",
-                        "@babel/parser": "^7.17.3",
-                        "@babel/types": "^7.17.0",
-                        "debug": "^4.1.0",
-                        "globals": "^11.1.0"
-                    }
-                },
-                "@babel/types": {
-                    "version": "7.17.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-validator-identifier": "^7.16.7",
-                        "to-fast-properties": "^2.0.0"
-                    }
-                },
-                "@isaacs/import-jsx": {
-                    "version": "4.0.1",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@babel/core": "^7.5.5",
-                        "@babel/plugin-proposal-object-rest-spread": "^7.5.5",
-                        "@babel/plugin-transform-destructuring": "^7.5.0",
-                        "@babel/plugin-transform-react-jsx": "^7.3.0",
-                        "caller-path": "^3.0.1",
-                        "find-cache-dir": "^3.2.0",
-                        "make-dir": "^3.0.2",
-                        "resolve-from": "^3.0.0",
-                        "rimraf": "^3.0.0"
-                    }
-                },
-                "@jridgewell/resolve-uri": {
-                    "version": "3.0.5",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@jridgewell/sourcemap-codec": {
-                    "version": "1.4.11",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@jridgewell/trace-mapping": {
-                    "version": "0.3.4",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@jridgewell/resolve-uri": "^3.0.3",
-                        "@jridgewell/sourcemap-codec": "^1.4.10"
-                    }
-                },
-                "@types/prop-types": {
-                    "version": "15.7.4",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@types/react": {
-                    "version": "17.0.52",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "@types/scheduler": {
-                    "version": "0.16.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "@types/yoga-layout": {
-                    "version": "1.9.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "ansi-escapes": {
-                    "version": "4.3.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "type-fest": "^0.21.3"
-                    },
-                    "dependencies": {
-                        "type-fest": {
-                            "version": "0.21.3",
-                            "bundled": true,
-                            "dev": true
-                        }
-                    }
-                },
-                "ansi-regex": {
-                    "version": "5.0.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "ansi-styles": {
-                    "version": "3.2.1",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "color-convert": "^1.9.0"
-                    }
-                },
-                "ansicolors": {
-                    "version": "0.3.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "astral-regex": {
-                    "version": "2.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "auto-bind": {
-                    "version": "4.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "balanced-match": {
-                    "version": "1.0.2",
-                    "bundled": true,
-                    "dev": true
-                },
                 "brace-expansion": {
-                    "version": "1.1.11",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "balanced-match": "^1.0.0",
-                        "concat-map": "0.0.1"
-                    }
-                },
-                "browserslist": {
-                    "version": "4.20.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "caniuse-lite": "^1.0.30001317",
-                        "electron-to-chromium": "^1.4.84",
-                        "escalade": "^3.1.1",
-                        "node-releases": "^2.0.2",
-                        "picocolors": "^1.0.0"
-                    }
-                },
-                "caller-callsite": {
-                    "version": "4.1.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "callsites": "^3.1.0"
-                    }
-                },
-                "caller-path": {
-                    "version": "3.0.1",
-                    "bundled": true,
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
                     "dev": true,
                     "requires": {
-                        "caller-callsite": "^4.1.0"
+                        "balanced-match": "^1.0.0"
                     }
                 },
-                "callsites": {
-                    "version": "3.1.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "caniuse-lite": {
-                    "version": "1.0.30001319",
-                    "bundled": true,
-                    "dev": true
-                },
-                "cardinal": {
-                    "version": "2.1.1",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "ansicolors": "~0.3.2",
-                        "redeyed": "~2.1.0"
-                    }
-                },
-                "chalk": {
-                    "version": "2.4.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "ansi-styles": "^3.2.1",
-                        "escape-string-regexp": "^1.0.5",
-                        "supports-color": "^5.3.0"
-                    }
-                },
-                "ci-info": {
-                    "version": "2.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "cli-boxes": {
-                    "version": "2.2.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "cli-cursor": {
-                    "version": "3.1.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "restore-cursor": "^3.1.0"
-                    }
-                },
-                "cli-truncate": {
-                    "version": "2.1.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "slice-ansi": "^3.0.0",
-                        "string-width": "^4.2.0"
-                    }
-                },
-                "code-excerpt": {
-                    "version": "3.0.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "convert-to-spaces": "^1.0.1"
-                    }
-                },
-                "color-convert": {
-                    "version": "1.9.3",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "color-name": "1.1.3"
-                    }
-                },
-                "color-name": {
-                    "version": "1.1.3",
-                    "bundled": true,
-                    "dev": true
-                },
-                "commondir": {
-                    "version": "1.0.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "concat-map": {
-                    "version": "0.0.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "convert-source-map": {
-                    "version": "1.8.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "safe-buffer": "~5.1.1"
-                    }
-                },
-                "convert-to-spaces": {
-                    "version": "1.0.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "csstype": {
-                    "version": "3.0.11",
-                    "bundled": true,
-                    "dev": true
-                },
-                "debug": {
-                    "version": "4.3.4",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "ms": "2.1.2"
-                    }
-                },
-                "electron-to-chromium": {
-                    "version": "1.4.89",
-                    "bundled": true,
-                    "dev": true
-                },
-                "emoji-regex": {
-                    "version": "8.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "escalade": {
-                    "version": "3.1.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "escape-string-regexp": {
-                    "version": "1.0.5",
-                    "bundled": true,
-                    "dev": true
-                },
-                "esprima": {
-                    "version": "4.0.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "events-to-array": {
-                    "version": "1.1.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "find-cache-dir": {
-                    "version": "3.3.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "commondir": "^1.0.1",
-                        "make-dir": "^3.0.2",
-                        "pkg-dir": "^4.1.0"
-                    }
-                },
-                "find-up": {
-                    "version": "4.1.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "locate-path": "^5.0.0",
-                        "path-exists": "^4.0.0"
-                    }
-                },
-                "fs.realpath": {
-                    "version": "1.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "gensync": {
-                    "version": "1.0.0-beta.2",
-                    "bundled": true,
-                    "dev": true
-                },
                 "glob": {
-                    "version": "7.2.3",
-                    "bundled": true,
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
                     "dev": true,
                     "requires": {
-                        "fs.realpath": "^1.0.0",
-                        "inflight": "^1.0.4",
-                        "inherits": "2",
-                        "minimatch": "^3.1.1",
-                        "once": "^1.3.0",
-                        "path-is-absolute": "^1.0.0"
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
                     }
                 },
-                "globals": {
-                    "version": "11.12.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "has-flag": {
-                    "version": "3.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "indent-string": {
-                    "version": "4.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "inflight": {
-                    "version": "1.0.6",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "once": "^1.3.0",
-                        "wrappy": "1"
-                    }
-                },
-                "inherits": {
-                    "version": "2.0.4",
-                    "bundled": true,
-                    "dev": true
-                },
-                "ink": {
-                    "version": "3.2.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "ansi-escapes": "^4.2.1",
-                        "auto-bind": "4.0.0",
-                        "chalk": "^4.1.0",
-                        "cli-boxes": "^2.2.0",
-                        "cli-cursor": "^3.1.0",
-                        "cli-truncate": "^2.1.0",
-                        "code-excerpt": "^3.0.0",
-                        "indent-string": "^4.0.0",
-                        "is-ci": "^2.0.0",
-                        "lodash": "^4.17.20",
-                        "patch-console": "^1.0.0",
-                        "react-devtools-core": "^4.19.1",
-                        "react-reconciler": "^0.26.2",
-                        "scheduler": "^0.20.2",
-                        "signal-exit": "^3.0.2",
-                        "slice-ansi": "^3.0.0",
-                        "stack-utils": "^2.0.2",
-                        "string-width": "^4.2.2",
-                        "type-fest": "^0.12.0",
-                        "widest-line": "^3.1.0",
-                        "wrap-ansi": "^6.2.0",
-                        "ws": "^7.5.5",
-                        "yoga-layout-prebuilt": "^1.9.6"
-                    },
-                    "dependencies": {
-                        "ansi-styles": {
-                            "version": "4.3.0",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "color-convert": "^2.0.1"
-                            }
-                        },
-                        "chalk": {
-                            "version": "4.1.2",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "ansi-styles": "^4.1.0",
-                                "supports-color": "^7.1.0"
-                            }
-                        },
-                        "color-convert": {
-                            "version": "2.0.1",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "color-name": "~1.1.4"
-                            }
-                        },
-                        "color-name": {
-                            "version": "1.1.4",
-                            "bundled": true,
-                            "dev": true
-                        },
-                        "has-flag": {
-                            "version": "4.0.0",
-                            "bundled": true,
-                            "dev": true
-                        },
-                        "supports-color": {
-                            "version": "7.2.0",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "has-flag": "^4.0.0"
-                            }
-                        }
-                    }
-                },
-                "is-ci": {
-                    "version": "2.0.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "ci-info": "^2.0.0"
-                    }
-                },
-                "is-fullwidth-code-point": {
-                    "version": "3.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "js-tokens": {
-                    "version": "4.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "jsesc": {
-                    "version": "2.5.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "json5": {
-                    "version": "2.2.3",
-                    "bundled": true,
-                    "dev": true
-                },
-                "locate-path": {
-                    "version": "5.0.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "p-locate": "^4.1.0"
-                    }
-                },
-                "lodash": {
-                    "version": "4.17.21",
-                    "bundled": true,
-                    "dev": true
-                },
-                "loose-envify": {
-                    "version": "1.4.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "js-tokens": "^3.0.0 || ^4.0.0"
-                    }
-                },
-                "make-dir": {
-                    "version": "3.1.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "semver": "^6.0.0"
-                    }
-                },
-                "mimic-fn": {
-                    "version": "2.1.0",
-                    "bundled": true,
-                    "dev": true
-                },
                 "minimatch": {
-                    "version": "3.1.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "brace-expansion": "^1.1.7"
-                    }
-                },
-                "minipass": {
-                    "version": "3.3.4",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "yallist": "^4.0.0"
-                    }
-                },
-                "ms": {
-                    "version": "2.1.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "node-releases": {
-                    "version": "2.0.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "object-assign": {
-                    "version": "4.1.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "once": {
-                    "version": "1.4.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "wrappy": "1"
-                    }
-                },
-                "onetime": {
-                    "version": "5.1.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "mimic-fn": "^2.1.0"
-                    }
-                },
-                "p-limit": {
-                    "version": "2.3.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "p-try": "^2.0.0"
-                    }
-                },
-                "p-locate": {
-                    "version": "4.1.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "p-limit": "^2.2.0"
-                    }
-                },
-                "p-try": {
-                    "version": "2.2.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "patch-console": {
-                    "version": "1.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "path-exists": {
-                    "version": "4.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "path-is-absolute": {
-                    "version": "1.0.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "picocolors": {
-                    "version": "1.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "pkg-dir": {
-                    "version": "4.2.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "find-up": "^4.0.0"
-                    }
-                },
-                "punycode": {
-                    "version": "2.1.1",
-                    "bundled": true,
-                    "dev": true
-                },
-                "react": {
-                    "version": "17.0.2",
-                    "bundled": true,
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
                     "dev": true,
                     "requires": {
-                        "loose-envify": "^1.1.0",
-                        "object-assign": "^4.1.1"
-                    }
-                },
-                "react-devtools-core": {
-                    "version": "4.24.1",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "shell-quote": "^1.6.1",
-                        "ws": "^7"
-                    }
-                },
-                "react-reconciler": {
-                    "version": "0.26.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "loose-envify": "^1.1.0",
-                        "object-assign": "^4.1.1",
-                        "scheduler": "^0.20.2"
-                    }
-                },
-                "redeyed": {
-                    "version": "2.1.1",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "esprima": "~4.0.0"
-                    }
-                },
-                "resolve-from": {
-                    "version": "3.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "restore-cursor": {
-                    "version": "3.1.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "onetime": "^5.1.0",
-                        "signal-exit": "^3.0.2"
+                        "brace-expansion": "^2.0.1"
                     }
                 },
                 "rimraf": {
-                    "version": "3.0.2",
-                    "bundled": true,
+                    "version": "5.0.5",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+                    "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
                     "dev": true,
                     "requires": {
-                        "glob": "^7.1.3"
-                    }
-                },
-                "safe-buffer": {
-                    "version": "5.1.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "scheduler": {
-                    "version": "0.20.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "loose-envify": "^1.1.0",
-                        "object-assign": "^4.1.1"
-                    }
-                },
-                "semver": {
-                    "version": "6.3.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "shell-quote": {
-                    "version": "1.7.3",
-                    "bundled": true,
-                    "dev": true
-                },
-                "signal-exit": {
-                    "version": "3.0.7",
-                    "bundled": true,
-                    "dev": true
-                },
-                "slice-ansi": {
-                    "version": "3.0.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "ansi-styles": "^4.0.0",
-                        "astral-regex": "^2.0.0",
-                        "is-fullwidth-code-point": "^3.0.0"
-                    },
-                    "dependencies": {
-                        "ansi-styles": {
-                            "version": "4.3.0",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "color-convert": "^2.0.1"
-                            }
-                        },
-                        "color-convert": {
-                            "version": "2.0.1",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "color-name": "~1.1.4"
-                            }
-                        },
-                        "color-name": {
-                            "version": "1.1.4",
-                            "bundled": true,
-                            "dev": true
-                        }
-                    }
-                },
-                "source-map": {
-                    "version": "0.5.7",
-                    "bundled": true,
-                    "dev": true
-                },
-                "stack-utils": {
-                    "version": "2.0.5",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "escape-string-regexp": "^2.0.0"
-                    },
-                    "dependencies": {
-                        "escape-string-regexp": {
-                            "version": "2.0.0",
-                            "bundled": true,
-                            "dev": true
-                        }
-                    }
-                },
-                "string-width": {
-                    "version": "4.2.3",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "emoji-regex": "^8.0.0",
-                        "is-fullwidth-code-point": "^3.0.0",
-                        "strip-ansi": "^6.0.1"
-                    }
-                },
-                "strip-ansi": {
-                    "version": "6.0.1",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "ansi-regex": "^5.0.1"
-                    }
-                },
-                "supports-color": {
-                    "version": "5.5.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "has-flag": "^3.0.0"
-                    }
-                },
-                "tap-parser": {
-                    "version": "11.0.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "events-to-array": "^1.0.1",
-                        "minipass": "^3.1.6",
-                        "tap-yaml": "^1.0.0"
-                    }
-                },
-                "tap-yaml": {
-                    "version": "1.0.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "yaml": "^1.10.2"
-                    }
-                },
-                "tcompare": {
-                    "version": "5.0.7",
-                    "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.7.tgz",
-                    "integrity": "sha512-d9iddt6YYGgyxJw5bjsN7UJUO1kGOtjSlNy/4PoGYAjQS5pAT/hzIoLf1bZCw+uUxRmZJh7Yy1aA7xKVRT9B4w==",
-                    "dev": true,
-                    "requires": {
-                        "diff": "^4.0.2"
-                    }
-                },
-                "to-fast-properties": {
-                    "version": "2.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "treport": {
-                    "version": "3.0.4",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@isaacs/import-jsx": "^4.0.1",
-                        "cardinal": "^2.1.1",
-                        "chalk": "^3.0.0",
-                        "ink": "^3.2.0",
-                        "ms": "^2.1.2",
-                        "tap-parser": "^11.0.0",
-                        "tap-yaml": "^1.0.0",
-                        "unicode-length": "^2.0.2"
-                    },
-                    "dependencies": {
-                        "ansi-styles": {
-                            "version": "4.3.0",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "color-convert": "^2.0.1"
-                            }
-                        },
-                        "chalk": {
-                            "version": "3.0.0",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "ansi-styles": "^4.1.0",
-                                "supports-color": "^7.1.0"
-                            }
-                        },
-                        "color-convert": {
-                            "version": "2.0.1",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "color-name": "~1.1.4"
-                            }
-                        },
-                        "color-name": {
-                            "version": "1.1.4",
-                            "bundled": true,
-                            "dev": true
-                        },
-                        "has-flag": {
-                            "version": "4.0.0",
-                            "bundled": true,
-                            "dev": true
-                        },
-                        "supports-color": {
-                            "version": "7.2.0",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "has-flag": "^4.0.0"
-                            }
-                        }
-                    }
-                },
-                "type-fest": {
-                    "version": "0.12.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "unicode-length": {
-                    "version": "2.0.2",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "punycode": "^2.0.0",
-                        "strip-ansi": "^3.0.1"
-                    },
-                    "dependencies": {
-                        "ansi-regex": {
-                            "version": "2.1.1",
-                            "bundled": true,
-                            "dev": true
-                        },
-                        "strip-ansi": {
-                            "version": "3.0.1",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "ansi-regex": "^2.0.0"
-                            }
-                        }
-                    }
-                },
-                "widest-line": {
-                    "version": "3.1.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "string-width": "^4.0.0"
-                    }
-                },
-                "wrap-ansi": {
-                    "version": "6.2.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "ansi-styles": "^4.0.0",
-                        "string-width": "^4.1.0",
-                        "strip-ansi": "^6.0.0"
-                    },
-                    "dependencies": {
-                        "ansi-styles": {
-                            "version": "4.3.0",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "color-convert": "^2.0.1"
-                            }
-                        },
-                        "color-convert": {
-                            "version": "2.0.1",
-                            "bundled": true,
-                            "dev": true,
-                            "requires": {
-                                "color-name": "~1.1.4"
-                            }
-                        },
-                        "color-name": {
-                            "version": "1.1.4",
-                            "bundled": true,
-                            "dev": true
-                        }
-                    }
-                },
-                "wrappy": {
-                    "version": "1.0.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "ws": {
-                    "version": "7.5.7",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {}
-                },
-                "yallist": {
-                    "version": "4.0.0",
-                    "bundled": true,
-                    "dev": true
-                },
-                "yaml": {
-                    "version": "1.10.2",
-                    "bundled": true,
-                    "dev": true
-                },
-                "yoga-layout-prebuilt": {
-                    "version": "1.10.0",
-                    "bundled": true,
-                    "dev": true,
-                    "requires": {
-                        "@types/yoga-layout": "1.9.2"
+                        "glob": "^10.3.7"
                     }
                 }
             }
         },
-        "tap-mocha-reporter": {
-            "version": "5.0.3",
-            "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.3.tgz",
-            "integrity": "sha512-6zlGkaV4J+XMRFkN0X+yuw6xHbE9jyCZ3WUKfw4KxMyRGOpYSRuuQTRJyWX88WWuLdVTuFbxzwXhXuS2XE6o0g==",
+        "tap": {
+            "version": "18.4.0",
+            "resolved": "https://registry.npmjs.org/tap/-/tap-18.4.0.tgz",
+            "integrity": "sha512-42bqz0KpoDg8F6Gs5zrTVOELq5ShaK86rCsRG6C6uJM7nUANCB3GW9Dmvy3BGHRll4wAwr+SA+iM0tvBQtrilg==",
             "dev": true,
             "requires": {
-                "color-support": "^1.1.0",
-                "debug": "^4.1.1",
-                "diff": "^4.0.1",
-                "escape-string-regexp": "^2.0.0",
-                "glob": "^7.0.5",
-                "tap-parser": "^11.0.0",
-                "tap-yaml": "^1.0.0",
-                "unicode-length": "^2.0.2"
-            },
-            "dependencies": {
-                "escape-string-regexp": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
-                    "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
-                    "dev": true
-                }
+                "@tapjs/after": "1.1.4",
+                "@tapjs/after-each": "1.1.4",
+                "@tapjs/asserts": "1.1.4",
+                "@tapjs/before": "1.1.4",
+                "@tapjs/before-each": "1.1.4",
+                "@tapjs/core": "1.3.4",
+                "@tapjs/filter": "1.2.4",
+                "@tapjs/fixture": "1.2.4",
+                "@tapjs/intercept": "1.2.4",
+                "@tapjs/mock": "1.2.2",
+                "@tapjs/node-serialize": "1.1.4",
+                "@tapjs/run": "1.4.0",
+                "@tapjs/snapshot": "1.2.4",
+                "@tapjs/spawn": "1.1.4",
+                "@tapjs/stdin": "1.1.4",
+                "@tapjs/test": "1.3.4",
+                "@tapjs/typescript": "1.2.4",
+                "@tapjs/worker": "1.1.4"
             }
         },
         "tap-parser": {
-            "version": "11.0.2",
-            "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-11.0.2.tgz",
-            "integrity": "sha512-6qGlC956rcORw+fg7Fv1iCRAY8/bU9UabUAhs3mXRH6eRmVZcNPLheSXCYaVaYeSwx5xa/1HXZb1537YSvwDZg==",
+            "version": "15.2.0",
+            "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-15.2.0.tgz",
+            "integrity": "sha512-bDBR7cuVLfsmmc7ruerZXVBlDtJwqqWzqlO9BFNgw6gprpzjnjyfdc+fsW6mNUYSoxdVEeY7NFgrgGa81EuQ5w==",
             "dev": true,
             "requires": {
-                "events-to-array": "^1.0.1",
-                "minipass": "^3.1.6",
-                "tap-yaml": "^1.0.0"
+                "events-to-array": "^2.0.3",
+                "tap-yaml": "2.2.0"
             }
         },
         "tap-yaml": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.2.tgz",
-            "integrity": "sha512-GegASpuqBnRNdT1U+yuUPZ8rEU64pL35WPBpCISWwff4dErS2/438barz7WFJl4Nzh3Y05tfPidZnH+GaV1wMg==",
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-2.2.0.tgz",
+            "integrity": "sha512-o8I7WDNiGpuF04tGAVaNYY5rX9waCtqw9A7Y0YVSQBGcFwNUJWUPLkr2lbhgLRTxc+Tpnw4xUXlIanZc+ZAGnw==",
             "dev": true,
             "requires": {
-                "yaml": "^1.10.2"
+                "yaml": "^2.3.0",
+                "yaml-types": "^0.3.0"
+            }
+        },
+        "tar": {
+            "version": "6.2.0",
+            "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
+            "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
+            "dev": true,
+            "requires": {
+                "chownr": "^2.0.0",
+                "fs-minipass": "^2.0.0",
+                "minipass": "^5.0.0",
+                "minizlib": "^2.1.1",
+                "mkdirp": "^1.0.3",
+                "yallist": "^4.0.0"
+            },
+            "dependencies": {
+                "fs-minipass": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+                    "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+                    "dev": true,
+                    "requires": {
+                        "minipass": "^3.0.0"
+                    },
+                    "dependencies": {
+                        "minipass": {
+                            "version": "3.3.6",
+                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+                            "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+                            "dev": true,
+                            "requires": {
+                                "yallist": "^4.0.0"
+                            }
+                        }
+                    }
+                },
+                "minipass": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+                    "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+                    "dev": true
+                },
+                "mkdirp": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+                    "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+                    "dev": true
+                }
             }
         },
         "tcompare": {
-            "version": "6.0.0",
-            "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-6.0.0.tgz",
-            "integrity": "sha512-JeX89lSVkxTzYND0LxzFCGrXm/TqGEQ0heu1JTwplnpaYQNky6hIaO4lQBOrs+/P787i3CoK9T/O3/oEcnJXvA==",
+            "version": "6.4.0",
+            "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-6.4.0.tgz",
+            "integrity": "sha512-MR0TPvFaEQ53jgMP43aHr3wKGKKPi6Th3nxHoIsBVL0AxjKdfyrIIWvYt7u30NNs57Vc6UP5ooq/sD69IhQPzw==",
             "dev": true,
             "requires": {
-                "diff": "^5.1.0"
+                "diff": "^5.1.0",
+                "react-element-to-jsx-string": "^15.0.0"
             },
             "dependencies": {
                 "diff": {
@@ -8265,12 +9358,6 @@
             "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
             "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
         },
-        "to-fast-properties": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
-            "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
-            "dev": true
-        },
         "to-regex-range": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -8281,11 +9368,132 @@
             }
         },
         "trivial-deferred": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.1.2.tgz",
-            "integrity": "sha512-vDPiDBC3hyP6O4JrJYMImW3nl3c03Tsj9fEXc7Qc/XKa1O7gf5ZtFfIR/E0dun9SnDHdwjna1Z2rSzYgqpxh/g==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-2.0.0.tgz",
+            "integrity": "sha512-iGbM7X2slv9ORDVj2y2FFUq3cP/ypbtu2nQ8S38ufjL0glBABvmR9pTdsib1XtS2LUhhLMbelaBUaf/s5J3dSw==",
+            "dev": true
+        },
+        "ts-node": {
+            "version": "npm:@isaacs/ts-node-temp-fork-for-pr-2009@10.9.1",
+            "resolved": "https://registry.npmjs.org/@isaacs/ts-node-temp-fork-for-pr-2009/-/ts-node-temp-fork-for-pr-2009-10.9.1.tgz",
+            "integrity": "sha512-MY4rUonz835NsTbd4dcgKZvZFYX9IkLnYFZV9M7GQV8t39fawafLin/Qw6VXD4yfMs4HcBq8P3ddeU0QHMH1YQ==",
+            "dev": true,
+            "requires": {
+                "@cspotcode/source-map-support": "^0.8.0",
+                "@tsconfig/node14": "*",
+                "@tsconfig/node16": "*",
+                "@tsconfig/node18": "*",
+                "@tsconfig/node20": "*",
+                "acorn": "^8.4.1",
+                "acorn-walk": "^8.1.1",
+                "arg": "^4.1.0",
+                "diff": "^4.0.1",
+                "make-error": "^1.1.1",
+                "v8-compile-cache-lib": "^3.0.1"
+            }
+        },
+        "tshy": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/tshy/-/tshy-1.2.2.tgz",
+            "integrity": "sha512-y5ItK4DKLYO+hba7h5sOaCYygNtF44qytZGyjZSE6CQSVfzUfZ2qn/GmXu737amwfCKG9EizPw3oPBWrisF1uw==",
+            "dev": true,
+            "requires": {
+                "chalk": "^5.3.0",
+                "foreground-child": "^3.1.1",
+                "mkdirp": "^3.0.1",
+                "resolve-import": "^1.4.1",
+                "rimraf": "^5.0.1",
+                "sync-content": "^1.0.2",
+                "typescript": "5.2",
+                "walk-up-path": "^3.0.1"
+            },
+            "dependencies": {
+                "brace-expansion": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+                    "dev": true,
+                    "requires": {
+                        "balanced-match": "^1.0.0"
+                    }
+                },
+                "chalk": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+                    "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+                    "dev": true
+                },
+                "glob": {
+                    "version": "10.3.10",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+                    "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+                    "dev": true,
+                    "requires": {
+                        "foreground-child": "^3.1.0",
+                        "jackspeak": "^2.3.5",
+                        "minimatch": "^9.0.1",
+                        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+                        "path-scurry": "^1.10.1"
+                    }
+                },
+                "minimatch": {
+                    "version": "9.0.3",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^2.0.1"
+                    }
+                },
+                "rimraf": {
+                    "version": "5.0.5",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+                    "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
+                    "dev": true,
+                    "requires": {
+                        "glob": "^10.3.7"
+                    }
+                }
+            }
+        },
+        "tslib": {
+            "version": "2.6.2",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+            "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
             "dev": true
         },
+        "tuf-js": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.1.0.tgz",
+            "integrity": "sha512-eD7YPPjVlMzdggrOeE8zwoegUaG/rt6Bt3jwoQPunRiNVzgcCE009UDFJKJjG+Gk9wFu6W/Vi+P5d/5QpdD9jA==",
+            "dev": true,
+            "requires": {
+                "@tufjs/models": "2.0.0",
+                "debug": "^4.3.4",
+                "make-fetch-happen": "^13.0.0"
+            },
+            "dependencies": {
+                "make-fetch-happen": {
+                    "version": "13.0.0",
+                    "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz",
+                    "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==",
+                    "dev": true,
+                    "requires": {
+                        "@npmcli/agent": "^2.0.0",
+                        "cacache": "^18.0.0",
+                        "http-cache-semantics": "^4.1.1",
+                        "is-lambda": "^1.0.1",
+                        "minipass": "^7.0.2",
+                        "minipass-fetch": "^3.0.0",
+                        "minipass-flush": "^1.0.5",
+                        "minipass-pipeline": "^1.2.4",
+                        "negotiator": "^0.6.3",
+                        "promise-retry": "^2.0.1",
+                        "ssri": "^10.0.0"
+                    }
+                }
+            }
+        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -8299,32 +9507,28 @@
             "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
             "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
         },
-        "typedarray-to-buffer": {
-            "version": "3.1.5",
-            "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
-            "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
-            "dev": true,
-            "requires": {
-                "is-typedarray": "^1.0.0"
-            }
+        "typescript": {
+            "version": "5.2.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+            "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+            "dev": true
         },
-        "unicode-length": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.1.0.tgz",
-            "integrity": "sha512-4bV582zTV9Q02RXBxSUMiuN/KHo5w4aTojuKTNT96DIKps/SIawFp7cS5Mu25VuY1AioGXrmYyzKZUzh8OqoUw==",
+        "unique-filename": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz",
+            "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==",
             "dev": true,
             "requires": {
-                "punycode": "^2.0.0"
+                "unique-slug": "^4.0.0"
             }
         },
-        "update-browserslist-db": {
-            "version": "1.0.10",
-            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
-            "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+        "unique-slug": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz",
+            "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==",
             "dev": true,
             "requires": {
-                "escalade": "^3.1.1",
-                "picocolors": "^1.0.0"
+                "imurmurhash": "^0.1.4"
             }
         },
         "uri-js": {
@@ -8335,12 +9539,72 @@
                 "punycode": "^2.1.0"
             }
         },
+        "util-deprecate": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+            "dev": true
+        },
         "uuid": {
             "version": "8.3.2",
             "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
             "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
             "dev": true
         },
+        "v8-compile-cache-lib": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+            "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+            "dev": true
+        },
+        "v8-to-istanbul": {
+            "version": "9.1.0",
+            "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
+            "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
+            "dev": true,
+            "requires": {
+                "@jridgewell/trace-mapping": "^0.3.12",
+                "@types/istanbul-lib-coverage": "^2.0.1",
+                "convert-source-map": "^1.6.0"
+            },
+            "dependencies": {
+                "@jridgewell/trace-mapping": {
+                    "version": "0.3.19",
+                    "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
+                    "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
+                    "dev": true,
+                    "requires": {
+                        "@jridgewell/resolve-uri": "^3.1.0",
+                        "@jridgewell/sourcemap-codec": "^1.4.14"
+                    }
+                }
+            }
+        },
+        "validate-npm-package-license": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+            "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+            "dev": true,
+            "requires": {
+                "spdx-correct": "^3.0.0",
+                "spdx-expression-parse": "^3.0.0"
+            }
+        },
+        "validate-npm-package-name": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz",
+            "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==",
+            "dev": true,
+            "requires": {
+                "builtins": "^5.0.0"
+            }
+        },
+        "walk-up-path": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz",
+            "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==",
+            "dev": true
+        },
         "which": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -8349,19 +9613,90 @@
                 "isexe": "^2.0.0"
             }
         },
-        "which-module": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-            "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
-            "dev": true
+        "wide-align": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+            "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+            "dev": true,
+            "requires": {
+                "string-width": "^1.0.2 || 2 || 3 || 4"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
+                    }
+                }
+            }
+        },
+        "widest-line": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
+            "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
+            "dev": true,
+            "requires": {
+                "string-width": "^5.0.1"
+            }
         },
         "word-wrap": {
-            "version": "1.2.3",
-            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
-            "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
+            "version": "1.2.5",
+            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+            "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
         },
         "wrap-ansi": {
-            "version": "7.0.0",
+            "version": "8.1.0",
+            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+            "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+            "dev": true,
+            "requires": {
+                "ansi-styles": "^6.1.0",
+                "string-width": "^5.0.1",
+                "strip-ansi": "^7.0.1"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+                    "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+                    "dev": true
+                },
+                "ansi-styles": {
+                    "version": "6.2.1",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+                    "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "7.1.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+                    "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^6.0.1"
+                    }
+                }
+            }
+        },
+        "wrap-ansi-cjs": {
+            "version": "npm:wrap-ansi@7.0.0",
             "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
             "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
             "dev": true,
@@ -8369,6 +9704,31 @@
                 "ansi-styles": "^4.0.0",
                 "string-width": "^4.1.0",
                 "strip-ansi": "^6.0.0"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
+                    }
+                }
             }
         },
         "wrappy": {
@@ -8376,22 +9736,17 @@
             "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
             "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
         },
-        "write-file-atomic": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
-            "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
+        "ws": {
+            "version": "8.14.2",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
+            "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
             "dev": true,
-            "requires": {
-                "imurmurhash": "^0.1.4",
-                "is-typedarray": "^1.0.0",
-                "signal-exit": "^3.0.2",
-                "typedarray-to-buffer": "^3.1.5"
-            }
+            "requires": {}
         },
         "y18n": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
-            "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+            "version": "5.0.8",
+            "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+            "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
             "dev": true
         },
         "yallist": {
@@ -8401,68 +9756,74 @@
             "dev": true
         },
         "yaml": {
-            "version": "1.10.2",
-            "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
-            "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",
+            "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==",
             "dev": true
         },
+        "yaml-types": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/yaml-types/-/yaml-types-0.3.0.tgz",
+            "integrity": "sha512-i9RxAO/LZBiE0NJUy9pbN5jFz5EasYDImzRkj8Y81kkInTi1laia3P3K/wlMKzOxFQutZip8TejvQP/DwgbU7A==",
+            "dev": true,
+            "requires": {}
+        },
         "yargs": {
-            "version": "15.4.1",
-            "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
-            "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+            "version": "17.7.2",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+            "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
             "dev": true,
             "requires": {
-                "cliui": "^6.0.0",
-                "decamelize": "^1.2.0",
-                "find-up": "^4.1.0",
-                "get-caller-file": "^2.0.1",
+                "cliui": "^8.0.1",
+                "escalade": "^3.1.1",
+                "get-caller-file": "^2.0.5",
                 "require-directory": "^2.1.1",
-                "require-main-filename": "^2.0.0",
-                "set-blocking": "^2.0.0",
-                "string-width": "^4.2.0",
-                "which-module": "^2.0.0",
-                "y18n": "^4.0.0",
-                "yargs-parser": "^18.1.2"
+                "string-width": "^4.2.3",
+                "y18n": "^5.0.5",
+                "yargs-parser": "^21.1.1"
             },
             "dependencies": {
-                "cliui": {
-                    "version": "6.0.0",
-                    "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
-                    "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
-                    "dev": true,
-                    "requires": {
-                        "string-width": "^4.2.0",
-                        "strip-ansi": "^6.0.0",
-                        "wrap-ansi": "^6.2.0"
-                    }
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+                    "dev": true
                 },
-                "wrap-ansi": {
-                    "version": "6.2.0",
-                    "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
-                    "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
                     "dev": true,
                     "requires": {
-                        "ansi-styles": "^4.0.0",
-                        "string-width": "^4.1.0",
-                        "strip-ansi": "^6.0.0"
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
                     }
                 }
             }
         },
         "yargs-parser": {
-            "version": "18.1.3",
-            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
-            "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
-            "dev": true,
-            "requires": {
-                "camelcase": "^5.0.0",
-                "decamelize": "^1.2.0"
-            }
+            "version": "21.1.1",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+            "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+            "dev": true
         },
         "yocto-queue": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
             "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
+        },
+        "yoga-wasm-web": {
+            "version": "0.3.3",
+            "resolved": "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz",
+            "integrity": "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==",
+            "dev": true
         }
     }
 }
diff --git a/package.json b/package.json
index 3b8e1771..16261f92 100644
--- a/package.json
+++ b/package.json
@@ -8,11 +8,18 @@
         "hsmusic": "./src/upd8.js"
     },
     "scripts": {
-        "test": "tap 'test/snapshot/*.js' 'test/unit/**/*.js'",
+        "test": "tap",
         "dev": "eslint src && node src/upd8.js"
     },
     "imports": {
         "#colors": "./src/util/colors.js",
+        "#composite": "./src/data/things/composite.js",
+        "#composite/control-flow": "./src/data/composite/control-flow/index.js",
+        "#composite/data": "./src/data/composite/data/index.js",
+        "#composite/wiki-data": "./src/data/composite/wiki-data/index.js",
+        "#composite/wiki-properties": "./src/data/composite/wiki-properties/index.js",
+        "#composite/things/album": "./src/data/composite/things/album/index.js",
+        "#composite/things/track": "./src/data/composite/things/track/index.js",
         "#content-dependencies": "./src/content/dependencies/index.js",
         "#content-function": "./src/content-function.js",
         "#cli": "./src/util/cli.js",
@@ -47,7 +54,7 @@
     "license": "GPL-3.0",
     "devDependencies": {
         "chokidar": "^3.5.3",
-        "tap": "^16.3.4",
+        "tap": "^18.4.0",
         "tcompare": "^6.0.0"
     },
     "tap": {
diff --git a/src/content/dependencies/generateAlbumInfoPage.js b/src/content/dependencies/generateAlbumInfoPage.js
index 5c4344b0..5fe27caf 100644
--- a/src/content/dependencies/generateAlbumInfoPage.js
+++ b/src/content/dependencies/generateAlbumInfoPage.js
@@ -44,7 +44,7 @@ export default {
 
     relations.coverArtistChronologyContributions =
       getChronologyRelations(album, {
-        contributions: album.coverArtistContribs,
+        contributions: album.coverArtistContribs ?? [],
 
         linkArtist: artist => relation('linkArtist', artist),
 
diff --git a/src/content/dependencies/generateAlbumStyleRules.js b/src/content/dependencies/generateAlbumStyleRules.js
index 9269ae83..c5acf374 100644
--- a/src/content/dependencies/generateAlbumStyleRules.js
+++ b/src/content/dependencies/generateAlbumStyleRules.js
@@ -64,8 +64,9 @@ export default {
       ]);
 
     return (
-      [...wallpaperRule, ...bannerRule, ...dataRule]
+      [wallpaperRule, bannerRule, dataRule]
         .filter(Boolean)
+        .flat()
         .join('\n'));
   },
 };
diff --git a/src/content/dependencies/generateAlbumTrackListItem.js b/src/content/dependencies/generateAlbumTrackListItem.js
index f65b47c9..f92712f9 100644
--- a/src/content/dependencies/generateAlbumTrackListItem.js
+++ b/src/content/dependencies/generateAlbumTrackListItem.js
@@ -1,4 +1,4 @@
-import {compareArrays} from '#sugar';
+import {compareArrays, empty} from '#sugar';
 
 export default {
   contentDependencies: [
@@ -11,9 +11,11 @@ export default {
   relations(relation, track) {
     const relations = {};
 
-    relations.contributionLinks =
-      track.artistContribs
-        .map(contrib => relation('linkContribution', contrib));
+    if (!empty(track.artistContribs)) {
+      relations.contributionLinks =
+        track.artistContribs
+          .map(contrib => relation('linkContribution', contrib));
+    }
 
     relations.trackLink =
       relation('linkTrack', track);
@@ -31,10 +33,12 @@ export default {
     }
 
     data.showArtists =
-      !compareArrays(
-        track.artistContribs.map(c => c.who),
-        album.artistContribs.map(c => c.who),
-        {checkOrder: false});
+      !empty(track.artistContribs) &&
+       (empty(album.artistContribs) ||
+        !compareArrays(
+          track.artistContribs.map(c => c.who),
+          album.artistContribs.map(c => c.who),
+          {checkOrder: false}));
 
     return data;
   },
diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js
index c20c0d08..1083d863 100644
--- a/src/content/dependencies/generateTrackInfoPage.js
+++ b/src/content/dependencies/generateTrackInfoPage.js
@@ -51,7 +51,10 @@ export default {
 
     relations.artistChronologyContributions =
       getChronologyRelations(track, {
-        contributions: [...track.artistContribs, ...track.contributorContribs],
+        contributions: [
+          ...track.artistContribs ?? [],
+          ...track.contributorContribs ?? [],
+        ],
 
         linkArtist: artist => relation('linkArtist', artist),
         linkThing: track => relation('linkTrack', track),
@@ -65,7 +68,7 @@ export default {
 
     relations.coverArtistChronologyContributions =
       getChronologyRelations(track, {
-        contributions: track.coverArtistContribs,
+        contributions: track.coverArtistContribs ?? [],
 
         linkArtist: artist => relation('linkArtist', artist),
 
diff --git a/src/content/dependencies/generateTrackList.js b/src/content/dependencies/generateTrackList.js
index f001c3b3..65f5552b 100644
--- a/src/content/dependencies/generateTrackList.js
+++ b/src/content/dependencies/generateTrackList.js
@@ -1,4 +1,4 @@
-import {empty} from '#sugar';
+import {empty, stitchArrays} from '#sugar';
 
 export default {
   contentDependencies: ['linkTrack', 'linkContribution'],
@@ -11,14 +11,17 @@ export default {
     }
 
     return {
-      items: tracks.map(track => ({
-        trackLink:
-          relation('linkTrack', track),
+      trackLinks:
+        tracks
+          .map(track => relation('linkTrack', track)),
 
-        contributionLinks:
-          track.artistContribs
-            .map(contrib => relation('linkContribution', contrib)),
-      })),
+      contributionLinks:
+        tracks
+          .map(track =>
+            (empty(track.artistContribs)
+              ? null
+              : track.artistContribs
+                  .map(contrib => relation('linkContribution', contrib)))),
     };
   },
 
@@ -28,22 +31,28 @@ export default {
   },
 
   generate(relations, slots, {html, language}) {
-    return html.tag('ul',
-      relations.items.map(({trackLink, contributionLinks}) =>
-        html.tag('li',
-          language.$('trackList.item.withArtists', {
-            track: trackLink,
-            by:
-              html.tag('span', {class: 'by'},
-                language.$('trackList.item.withArtists.by', {
-                  artists:
-                    language.formatConjunctionList(
-                      contributionLinks.map(link =>
-                        link.slots({
-                          showContribution: slots.showContribution,
-                          showIcons: slots.showIcons,
-                        }))),
-                })),
-          }))));
+    return (
+      html.tag('ul',
+        stitchArrays({
+          trackLink: relations.trackLinks,
+          contributionLinks: relations.contributionLinks,
+        }).map(({trackLink, contributionLinks}) =>
+            html.tag('li',
+              (empty(contributionLinks)
+                ? trackLink
+                : language.$('trackList.item.withArtists', {
+                    track: trackLink,
+                    by:
+                      html.tag('span', {class: 'by'},
+                        language.$('trackList.item.withArtists.by', {
+                          artists:
+                            language.formatConjunctionList(
+                              contributionLinks.map(link =>
+                                link.slots({
+                                  showContribution: slots.showContribution,
+                                  showIcons: slots.showIcons,
+                                }))),
+                        })),
+                  }))))));
   },
 };
diff --git a/src/content/dependencies/generateWikiHomeAlbumsRow.js b/src/content/dependencies/generateWikiHomeAlbumsRow.js
index 99c1be55..cb0860f5 100644
--- a/src/content/dependencies/generateWikiHomeAlbumsRow.js
+++ b/src/content/dependencies/generateWikiHomeAlbumsRow.js
@@ -16,7 +16,7 @@ export default {
   sprawl({albumData}, row) {
     const sprawl = {};
 
-    switch (row.sourceGroupByRef) {
+    switch (row.sourceGroup) {
       case 'new-releases':
         sprawl.albums = getNewReleases(row.countAlbumsFromGroup, {albumData});
         break;
diff --git a/src/content/dependencies/image.js b/src/content/dependencies/image.js
index 64fe8533..6c0aeecd 100644
--- a/src/content/dependencies/image.js
+++ b/src/content/dependencies/image.js
@@ -102,6 +102,7 @@ export default {
     const willReveal =
       slots.reveal &&
       originalSrc &&
+      !isMissingImageFile &&
       !empty(data.contentWarnings);
 
     const willSquare = slots.square;
diff --git a/src/content/dependencies/index.js b/src/content/dependencies/index.js
index 3bc34845..71802050 100644
--- a/src/content/dependencies/index.js
+++ b/src/content/dependencies/index.js
@@ -6,7 +6,7 @@ import {fileURLToPath} from 'node:url';
 import chokidar from 'chokidar';
 import {ESLint} from 'eslint';
 
-import {color, logWarn} from '#cli';
+import {colors, logWarn} from '#cli';
 import contentFunction, {ContentFunctionSpecError} from '#content-function';
 import {annotateFunction} from '#sugar';
 
@@ -30,7 +30,6 @@ export function watchContentDependencies({
   const contentDependencies = {};
 
   let emittedReady = false;
-  let allDependenciesFulfilled = false;
   let closed = false;
 
   let _close = () => {};
@@ -77,12 +76,12 @@ export function watchContentDependencies({
   // prematurely find out there aren't any nulls - before the nulls have
   // been entered at all!).
 
-  readdir(metaDirname).then(files => {
+  readdir(watchPath).then(files => {
     if (closed) {
       return;
     }
 
-    const filePaths = files.map(file => path.join(metaDirname, file));
+    const filePaths = files.map(file => path.join(watchPath, file));
     for (const filePath of filePaths) {
       if (filePath === metaPath) continue;
       const functionName = getFunctionName(filePath);
@@ -91,7 +90,7 @@ export function watchContentDependencies({
       }
     }
 
-    const watcher = chokidar.watch(metaDirname);
+    const watcher = chokidar.watch(watchPath);
 
     watcher.on('all', (event, filePath) => {
       if (!['add', 'change'].includes(event)) return;
@@ -192,7 +191,7 @@ export function watchContentDependencies({
 
       if (logging && emittedReady) {
         const timestamp = new Date().toLocaleString('en-US', {timeStyle: 'medium'});
-        console.log(color.green(`[${timestamp}] Updated ${functionName}`));
+        console.log(colors.green(`[${timestamp}] Updated ${functionName}`));
       }
 
       contentDependencies[functionName] = fn;
@@ -219,9 +218,9 @@ export function watchContentDependencies({
       }
 
       if (typeof error === 'string') {
-        console.error(color.yellow(error));
+        console.error(colors.yellow(error));
       } else if (error instanceof ContentFunctionSpecError) {
-        console.error(color.yellow(error.message));
+        console.error(colors.yellow(error.message));
       } else {
         console.error(error);
       }
diff --git a/src/data/composite/control-flow/exitWithoutDependency.js b/src/data/composite/control-flow/exitWithoutDependency.js
new file mode 100644
index 00000000..c660a7ef
--- /dev/null
+++ b/src/data/composite/control-flow/exitWithoutDependency.js
@@ -0,0 +1,35 @@
+// Early exits if a dependency isn't available.
+// See withResultOfAvailabilityCheck for {mode} options.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
+import withResultOfAvailabilityCheck from './withResultOfAvailabilityCheck.js';
+
+export default templateCompositeFrom({
+  annotation: `exitWithoutDependency`,
+
+  inputs: {
+    dependency: input({acceptsNull: true}),
+    mode: inputAvailabilityCheckMode(),
+    value: input({defaultValue: null}),
+  },
+
+  steps: () => [
+    withResultOfAvailabilityCheck({
+      from: input('dependency'),
+      mode: input('mode'),
+    }),
+
+    {
+      dependencies: ['#availability', input('value')],
+      compute: (continuation, {
+        ['#availability']: availability,
+        [input('value')]: value,
+      }) =>
+        (availability
+          ? continuation()
+          : continuation.exit(value)),
+    },
+  ],
+});
diff --git a/src/data/composite/control-flow/exitWithoutUpdateValue.js b/src/data/composite/control-flow/exitWithoutUpdateValue.js
new file mode 100644
index 00000000..244b3233
--- /dev/null
+++ b/src/data/composite/control-flow/exitWithoutUpdateValue.js
@@ -0,0 +1,24 @@
+// Early exits if this property's update value isn't available.
+// See withResultOfAvailabilityCheck for {mode} options.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import exitWithoutDependency from './exitWithoutDependency.js';
+import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
+
+export default templateCompositeFrom({
+  annotation: `exitWithoutUpdateValue`,
+
+  inputs: {
+    mode: inputAvailabilityCheckMode(),
+    value: input({defaultValue: null}),
+  },
+
+  steps: () => [
+    exitWithoutDependency({
+      dependency: input.updateValue(),
+      mode: input('mode'),
+      value: input('value'),
+    }),
+  ],
+});
diff --git a/src/data/composite/control-flow/exposeConstant.js b/src/data/composite/control-flow/exposeConstant.js
new file mode 100644
index 00000000..e0435478
--- /dev/null
+++ b/src/data/composite/control-flow/exposeConstant.js
@@ -0,0 +1,26 @@
+// Exposes a constant value exactly as it is; like exposeDependency, this
+// is typically the base of a composition serving as a particular property
+// descriptor. It generally follows steps which will conditionally early
+// exit with some other value, with the exposeConstant base serving as the
+// fallback default value.
+
+import {input, templateCompositeFrom} from '#composite';
+
+export default templateCompositeFrom({
+  annotation: `exposeConstant`,
+
+  compose: false,
+
+  inputs: {
+    value: input.staticValue(),
+  },
+
+  steps: () => [
+    {
+      dependencies: [input('value')],
+      compute: ({
+        [input('value')]: value,
+      }) => value,
+    },
+  ],
+});
diff --git a/src/data/composite/control-flow/exposeDependency.js b/src/data/composite/control-flow/exposeDependency.js
new file mode 100644
index 00000000..3aa3d03a
--- /dev/null
+++ b/src/data/composite/control-flow/exposeDependency.js
@@ -0,0 +1,28 @@
+// Exposes a dependency exactly as it is; this is typically the base of a
+// composition which was created to serve as one property's descriptor.
+//
+// Please note that this *doesn't* verify that the dependency exists, so
+// if you provide the wrong name or it hasn't been set by a previous
+// compositional step, the property will be exposed as undefined instead
+// of null.
+
+import {input, templateCompositeFrom} from '#composite';
+
+export default templateCompositeFrom({
+  annotation: `exposeDependency`,
+
+  compose: false,
+
+  inputs: {
+    dependency: input.staticDependency({acceptsNull: true}),
+  },
+
+  steps: () => [
+    {
+      dependencies: [input('dependency')],
+      compute: ({
+        [input('dependency')]: dependency
+      }) => dependency,
+    },
+  ],
+});
diff --git a/src/data/composite/control-flow/exposeDependencyOrContinue.js b/src/data/composite/control-flow/exposeDependencyOrContinue.js
new file mode 100644
index 00000000..0f7f223e
--- /dev/null
+++ b/src/data/composite/control-flow/exposeDependencyOrContinue.js
@@ -0,0 +1,34 @@
+// Exposes a dependency as it is, or continues if it's unavailable.
+// See withResultOfAvailabilityCheck for {mode} options.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
+import withResultOfAvailabilityCheck from './withResultOfAvailabilityCheck.js';
+
+export default templateCompositeFrom({
+  annotation: `exposeDependencyOrContinue`,
+
+  inputs: {
+    dependency: input({acceptsNull: true}),
+    mode: inputAvailabilityCheckMode(),
+  },
+
+  steps: () => [
+    withResultOfAvailabilityCheck({
+      from: input('dependency'),
+      mode: input('mode'),
+    }),
+
+    {
+      dependencies: ['#availability', input('dependency')],
+      compute: (continuation, {
+        ['#availability']: availability,
+        [input('dependency')]: dependency,
+      }) =>
+        (availability
+          ? continuation.exit(dependency)
+          : continuation()),
+    },
+  ],
+});
diff --git a/src/data/composite/control-flow/exposeUpdateValueOrContinue.js b/src/data/composite/control-flow/exposeUpdateValueOrContinue.js
new file mode 100644
index 00000000..1f94b332
--- /dev/null
+++ b/src/data/composite/control-flow/exposeUpdateValueOrContinue.js
@@ -0,0 +1,40 @@
+// Exposes the update value of an {update: true} property as it is,
+// or continues if it's unavailable.
+//
+// See withResultOfAvailabilityCheck for {mode} options.
+//
+// Provide {validate} here to conveniently set a custom validation check
+// for this property's update value.
+//
+
+import {input, templateCompositeFrom} from '#composite';
+
+import exposeDependencyOrContinue from './exposeDependencyOrContinue.js';
+import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
+
+export default templateCompositeFrom({
+  annotation: `exposeUpdateValueOrContinue`,
+
+  inputs: {
+    mode: inputAvailabilityCheckMode(),
+
+    validate: input({
+      type: 'function',
+      defaultValue: null,
+    }),
+  },
+
+  update: ({
+    [input.staticValue('validate')]: validate,
+  }) =>
+    (validate
+      ? {validate}
+      : {}),
+
+  steps: () => [
+    exposeDependencyOrContinue({
+      dependency: input.updateValue(),
+      mode: input('mode'),
+    }),
+  ],
+});
diff --git a/src/data/composite/control-flow/index.js b/src/data/composite/control-flow/index.js
new file mode 100644
index 00000000..dfc53db7
--- /dev/null
+++ b/src/data/composite/control-flow/index.js
@@ -0,0 +1,9 @@
+export {default as exitWithoutDependency} from './exitWithoutDependency.js';
+export {default as exitWithoutUpdateValue} from './exitWithoutUpdateValue.js';
+export {default as exposeConstant} from './exposeConstant.js';
+export {default as exposeDependency} from './exposeDependency.js';
+export {default as exposeDependencyOrContinue} from './exposeDependencyOrContinue.js';
+export {default as exposeUpdateValueOrContinue} from './exposeUpdateValueOrContinue.js';
+export {default as raiseOutputWithoutDependency} from './raiseOutputWithoutDependency.js';
+export {default as raiseOutputWithoutUpdateValue} from './raiseOutputWithoutUpdateValue.js';
+export {default as withResultOfAvailabilityCheck} from './withResultOfAvailabilityCheck.js';
diff --git a/src/data/composite/control-flow/inputAvailabilityCheckMode.js b/src/data/composite/control-flow/inputAvailabilityCheckMode.js
new file mode 100644
index 00000000..d74a1149
--- /dev/null
+++ b/src/data/composite/control-flow/inputAvailabilityCheckMode.js
@@ -0,0 +1,9 @@
+import {input} from '#composite';
+import {is} from '#validators';
+
+export default function inputAvailabilityCheckMode() {
+  return input({
+    validate: is('null', 'empty', 'falsy'),
+    defaultValue: 'null',
+  });
+}
diff --git a/src/data/composite/control-flow/raiseOutputWithoutDependency.js b/src/data/composite/control-flow/raiseOutputWithoutDependency.js
new file mode 100644
index 00000000..03d8036a
--- /dev/null
+++ b/src/data/composite/control-flow/raiseOutputWithoutDependency.js
@@ -0,0 +1,39 @@
+// Raises if a dependency isn't available.
+// See withResultOfAvailabilityCheck for {mode} options.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
+import withResultOfAvailabilityCheck from './withResultOfAvailabilityCheck.js';
+
+export default templateCompositeFrom({
+  annotation: `raiseOutputWithoutDependency`,
+
+  inputs: {
+    dependency: input({acceptsNull: true}),
+    mode: inputAvailabilityCheckMode(),
+    output: input.staticValue({defaultValue: {}}),
+  },
+
+  outputs: ({
+    [input.staticValue('output')]: output,
+  }) => Object.keys(output),
+
+  steps: () => [
+    withResultOfAvailabilityCheck({
+      from: input('dependency'),
+      mode: input('mode'),
+    }),
+
+    {
+      dependencies: ['#availability', input('output')],
+      compute: (continuation, {
+        ['#availability']: availability,
+        [input('output')]: output,
+      }) =>
+        (availability
+          ? continuation()
+          : continuation.raiseOutputAbove(output)),
+    },
+  ],
+});
diff --git a/src/data/composite/control-flow/raiseOutputWithoutUpdateValue.js b/src/data/composite/control-flow/raiseOutputWithoutUpdateValue.js
new file mode 100644
index 00000000..3c39f5ba
--- /dev/null
+++ b/src/data/composite/control-flow/raiseOutputWithoutUpdateValue.js
@@ -0,0 +1,47 @@
+// Raises if this property's update value isn't available.
+// See withResultOfAvailabilityCheck for {mode} options!
+
+import {input, templateCompositeFrom} from '#composite';
+
+import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
+import withResultOfAvailabilityCheck from './withResultOfAvailabilityCheck.js';
+
+export default templateCompositeFrom({
+  annotation: `raiseOutputWithoutUpdateValue`,
+
+  inputs: {
+    mode: inputAvailabilityCheckMode(),
+    output: input.staticValue({defaultValue: {}}),
+  },
+
+  outputs: ({
+    [input.staticValue('output')]: output,
+  }) => Object.keys(output),
+
+  steps: () => [
+    withResultOfAvailabilityCheck({
+      from: input.updateValue(),
+      mode: input('mode'),
+    }),
+
+    // TODO: A bit of a kludge, below. Other "do something with the update
+    // value" type functions can get by pretty much just passing that value
+    // as an input (input.updateValue()) into the corresponding "do something
+    // with a dependency/arbitrary value" function. But we can't do that here,
+    // because the special behavior, raiseOutputAbove(), only works to raise
+    // output above the composition it's *directly* nested in. Other languages
+    // have a throw/catch system that might serve as inspiration for something
+    // better here.
+
+    {
+      dependencies: ['#availability', input('output')],
+      compute: (continuation, {
+        ['#availability']: availability,
+        [input('output')]: output,
+      }) =>
+        (availability
+          ? continuation()
+          : continuation.raiseOutputAbove(output)),
+    },
+  ],
+});
diff --git a/src/data/composite/control-flow/withResultOfAvailabilityCheck.js b/src/data/composite/control-flow/withResultOfAvailabilityCheck.js
new file mode 100644
index 00000000..bcbd0b37
--- /dev/null
+++ b/src/data/composite/control-flow/withResultOfAvailabilityCheck.js
@@ -0,0 +1,66 @@
+// Checks the availability of a dependency and provides the result to later
+// steps under '#availability' (by default). This is mainly intended for use
+// by the more specific utilities, which you should consider using instead.
+//
+// Customize {mode} to select one of these modes, or default to 'null':
+//
+// * 'null':  Check that the value isn't null (and not undefined either).
+// * 'empty': Check that the value is neither null, undefined, nor an empty
+//            array.
+// * 'falsy': Check that the value isn't false when treated as a boolean
+//            (nor an empty array). Keep in mind this will also be false
+//            for values like zero and the empty string!
+//
+// See also:
+//  - exitWithoutDependency
+//  - exitWithoutUpdateValue
+//  - exposeDependencyOrContinue
+//  - exposeUpdateValueOrContinue
+//  - raiseOutputWithoutDependency
+//  - raiseOutputWithoutUpdateValue
+//
+
+import {input, templateCompositeFrom} from '#composite';
+import {empty} from '#sugar';
+
+import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
+
+export default templateCompositeFrom({
+  annotation: `withResultOfAvailabilityCheck`,
+
+  inputs: {
+    from: input({acceptsNull: true}),
+    mode: inputAvailabilityCheckMode(),
+  },
+
+  outputs: ['#availability'],
+
+  steps: () => [
+    {
+      dependencies: [input('from'), input('mode')],
+
+      compute: (continuation, {
+        [input('from')]: value,
+        [input('mode')]: mode,
+      }) => {
+        let availability;
+
+        switch (mode) {
+          case 'null':
+            availability = value !== undefined && value !== null;
+            break;
+
+          case 'empty':
+            availability = value !== undefined && !empty(value);
+            break;
+
+          case 'falsy':
+            availability = !!value && (!Array.isArray(value) || !empty(value));
+            break;
+        }
+
+        return continuation({'#availability': availability});
+      },
+    },
+  ],
+});
diff --git a/src/data/composite/data/excludeFromList.js b/src/data/composite/data/excludeFromList.js
new file mode 100644
index 00000000..718f2294
--- /dev/null
+++ b/src/data/composite/data/excludeFromList.js
@@ -0,0 +1,56 @@
+// Filters particular values out of a list. Note that this will always
+// completely skip over null, but can be used to filter out any other
+// primitive or object value.
+//
+// See also:
+//  - fillMissingListItems
+//
+// More list utilities:
+//  - withFlattenedList
+//  - withPropertyFromList
+//  - withPropertiesFromList
+//  - withUnflattenedList
+//
+
+import {input, templateCompositeFrom} from '#composite';
+import {empty} from '#sugar';
+
+export default templateCompositeFrom({
+  annotation: `excludeFromList`,
+
+  inputs: {
+    list: input(),
+
+    item: input({defaultValue: null}),
+    items: input({type: 'array', defaultValue: null}),
+  },
+
+  outputs: ({
+    [input.staticDependency('list')]: list,
+  }) => [list ?? '#list'],
+
+  steps: () => [
+    {
+      dependencies: [
+        input.staticDependency('list'),
+        input('list'),
+        input('item'),
+        input('items'),
+      ],
+
+      compute: (continuation, {
+        [input.staticDependency('list')]: listName,
+        [input('list')]: listContents,
+        [input('item')]: excludeItem,
+        [input('items')]: excludeItems,
+      }) => continuation({
+        [listName ?? '#list']:
+          listContents.filter(item => {
+            if (excludeItem !== null && item === excludeItem) return false;
+            if (!empty(excludeItems) && excludeItems.includes(item)) return false;
+            return true;
+          }),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/data/fillMissingListItems.js b/src/data/composite/data/fillMissingListItems.js
new file mode 100644
index 00000000..c06eceda
--- /dev/null
+++ b/src/data/composite/data/fillMissingListItems.js
@@ -0,0 +1,51 @@
+// Replaces items of a list, which are null or undefined, with some fallback
+// value. By default, this replaces the passed dependency.
+//
+// See also:
+//  - excludeFromList
+//
+// More list utilities:
+//  - withFlattenedList
+//  - withPropertyFromList
+//  - withPropertiesFromList
+//  - withUnflattenedList
+//
+
+import {input, templateCompositeFrom} from '#composite';
+
+export default templateCompositeFrom({
+  annotation: `fillMissingListItems`,
+
+  inputs: {
+    list: input({type: 'array'}),
+    fill: input({acceptsNull: true}),
+  },
+
+  outputs: ({
+    [input.staticDependency('list')]: list,
+  }) => [list ?? '#list'],
+
+  steps: () => [
+    {
+      dependencies: [input('list'), input('fill')],
+      compute: (continuation, {
+        [input('list')]: list,
+        [input('fill')]: fill,
+      }) => continuation({
+        ['#filled']:
+          list.map(item => item ?? fill),
+      }),
+    },
+
+    {
+      dependencies: [input.staticDependency('list'), '#filled'],
+      compute: (continuation, {
+        [input.staticDependency('list')]: list,
+        ['#filled']: filled,
+      }) => continuation({
+        [list ?? '#list']:
+          filled,
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/data/index.js b/src/data/composite/data/index.js
new file mode 100644
index 00000000..ecd05129
--- /dev/null
+++ b/src/data/composite/data/index.js
@@ -0,0 +1,8 @@
+export {default as excludeFromList} from './excludeFromList.js';
+export {default as fillMissingListItems} from './fillMissingListItems.js';
+export {default as withFlattenedList} from './withFlattenedList.js';
+export {default as withPropertiesFromList} from './withPropertiesFromList.js';
+export {default as withPropertiesFromObject} from './withPropertiesFromObject.js';
+export {default as withPropertyFromList} from './withPropertyFromList.js';
+export {default as withPropertyFromObject} from './withPropertyFromObject.js';
+export {default as withUnflattenedList} from './withUnflattenedList.js';
diff --git a/src/data/composite/data/withFlattenedList.js b/src/data/composite/data/withFlattenedList.js
new file mode 100644
index 00000000..b08edb4e
--- /dev/null
+++ b/src/data/composite/data/withFlattenedList.js
@@ -0,0 +1,47 @@
+// Flattens an array with one level of nested arrays, providing as dependencies
+// both the flattened array as well as the original starting indices of each
+// successive source array.
+//
+// See also:
+//  - withFlattenedList
+//
+// More list utilities:
+//  - excludeFromList
+//  - fillMissingListItems
+//  - withPropertyFromList
+//  - withPropertiesFromList
+//
+
+import {input, templateCompositeFrom} from '#composite';
+
+export default templateCompositeFrom({
+  annotation: `withFlattenedList`,
+
+  inputs: {
+    list: input({type: 'array'}),
+  },
+
+  outputs: ['#flattenedList', '#flattenedIndices'],
+
+  steps: () => [
+    {
+      dependencies: [input('list')],
+      compute(continuation, {
+        [input('list')]: sourceList,
+      }) {
+        const flattenedList = sourceList.flat();
+        const indices = [];
+        let lastEndIndex = 0;
+        for (const {length} of sourceList) {
+          indices.push(lastEndIndex);
+          lastEndIndex += length;
+        }
+
+        return continuation({
+          ['#flattenedList']: flattenedList,
+          ['#flattenedIndices']: indices,
+        });
+      },
+    },
+  ],
+});
diff --git a/src/data/composite/data/withPropertiesFromList.js b/src/data/composite/data/withPropertiesFromList.js
new file mode 100644
index 00000000..76ba696c
--- /dev/null
+++ b/src/data/composite/data/withPropertiesFromList.js
@@ -0,0 +1,92 @@
+// Gets the listed properties from each of a list of objects, providing lists
+// of property values each into a dependency prefixed with the same name as the
+// list (by default).
+//
+// Like withPropertyFromList, this doesn't alter indices.
+//
+// See also:
+//  - withPropertiesFromObject
+//  - withPropertyFromList
+//
+// More list utilities:
+//  - excludeFromList
+//  - fillMissingListItems
+//  - withFlattenedList
+//  - withUnflattenedList
+//
+
+import {input, templateCompositeFrom} from '#composite';
+import {isString, validateArrayItems} from '#validators';
+
+export default templateCompositeFrom({
+  annotation: `withPropertiesFromList`,
+
+  inputs: {
+    list: input({type: 'array'}),
+
+    properties: input({
+      validate: validateArrayItems(isString),
+    }),
+
+    prefix: input.staticValue({type: 'string', defaultValue: null}),
+  },
+
+  outputs: ({
+    [input.staticDependency('list')]: list,
+    [input.staticValue('properties')]: properties,
+    [input.staticValue('prefix')]: prefix,
+  }) =>
+    (properties
+      ? properties.map(property =>
+          (prefix
+            ? `${prefix}.${property}`
+         : list
+            ? `${list}.${property}`
+            : `#list.${property}`))
+      : ['#lists']),
+
+  steps: () => [
+    {
+      dependencies: [input('list'), input('properties')],
+      compute: (continuation, {
+        [input('list')]: list,
+        [input('properties')]: properties,
+      }) => continuation({
+        ['#lists']:
+          Object.fromEntries(
+            properties.map(property => [
+              property,
+              list.map(item => item[property] ?? null),
+            ])),
+      }),
+    },
+
+    {
+      dependencies: [
+        input.staticDependency('list'),
+        input.staticValue('properties'),
+        input.staticValue('prefix'),
+        '#lists',
+      ],
+
+      compute: (continuation, {
+        [input.staticDependency('list')]: list,
+        [input.staticValue('properties')]: properties,
+        [input.staticValue('prefix')]: prefix,
+        ['#lists']: lists,
+      }) =>
+        (properties
+          ? continuation(
+              Object.fromEntries(
+                properties.map(property => [
+                  (prefix
+                    ? `${prefix}.${property}`
+                 : list
+                    ? `${list}.${property}`
+                    : `#list.${property}`),
+                  lists[property],
+                ])))
+          : continuation({'#lists': lists})),
+    },
+  ],
+});
diff --git a/src/data/composite/data/withPropertiesFromObject.js b/src/data/composite/data/withPropertiesFromObject.js
new file mode 100644
index 00000000..21726b58
--- /dev/null
+++ b/src/data/composite/data/withPropertiesFromObject.js
@@ -0,0 +1,87 @@
+// Gets the listed properties from some object, providing each property's value
+// as a dependency prefixed with the same name as the object (by default).
+// If the object itself is null, all provided dependencies will be null;
+// if it's missing only select properties, those will be provided as null.
+//
+// See also:
+//  - withPropertiesFromList
+//  - withPropertyFromObject
+//
+
+import {input, templateCompositeFrom} from '#composite';
+import {isString, validateArrayItems} from '#validators';
+
+export default templateCompositeFrom({
+  annotation: `withPropertiesFromObject`,
+
+  inputs: {
+    object: input({type: 'object', acceptsNull: true}),
+
+    properties: input({
+      type: 'array',
+      validate: validateArrayItems(isString),
+    }),
+
+    prefix: input.staticValue({type: 'string', defaultValue: null}),
+  },
+
+  outputs: ({
+    [input.staticDependency('object')]: object,
+    [input.staticValue('properties')]: properties,
+    [input.staticValue('prefix')]: prefix,
+  }) =>
+    (properties
+      ? properties.map(property =>
+          (prefix
+            ? `${prefix}.${property}`
+         : object
+            ? `${object}.${property}`
+            : `#object.${property}`))
+      : ['#object']),
+
+  steps: () => [
+    {
+      dependencies: [input('object'), input('properties')],
+      compute: (continuation, {
+        [input('object')]: object,
+        [input('properties')]: properties,
+      }) => continuation({
+        ['#entries']:
+          (object === null
+            ? properties.map(property => [property, null])
+            : properties.map(property => [property, object[property]])),
+      }),
+    },
+
+    {
+      dependencies: [
+        input.staticDependency('object'),
+        input.staticValue('properties'),
+        input.staticValue('prefix'),
+        '#entries',
+      ],
+
+      compute: (continuation, {
+        [input.staticDependency('object')]: object,
+        [input.staticValue('properties')]: properties,
+        [input.staticValue('prefix')]: prefix,
+        ['#entries']: entries,
+      }) =>
+        (properties
+          ? continuation(
+              Object.fromEntries(
+                entries.map(([property, value]) => [
+                  (prefix
+                    ? `${prefix}.${property}`
+                 : object
+                    ? `${object}.${property}`
+                    : `#object.${property}`),
+                  value ?? null,
+                ])))
+          : continuation({
+              ['#object']:
+                Object.fromEntries(entries),
+            })),
+    },
+  ],
+});
diff --git a/src/data/composite/data/withPropertyFromList.js b/src/data/composite/data/withPropertyFromList.js
new file mode 100644
index 00000000..3ce05fdf
--- /dev/null
+++ b/src/data/composite/data/withPropertyFromList.js
@@ -0,0 +1,56 @@
+// Gets a property from each of a list of objects (in a dependency) and
+// provides the results.
+//
+// This doesn't alter any list indices, so positions which were null in the
+// original list are kept null here. Objects which don't have the specified
+// property are retained in-place as null.
+//
+// See also:
+//  - withPropertiesFromList
+//  - withPropertyFromObject
+//
+// More list utilities:
+//  - excludeFromList
+//  - fillMissingListItems
+//  - withFlattenedList
+//  - withUnflattenedList
+//
+
+import {empty} from '#sugar';
+
+// todo: OUHHH THIS ONE'S NOT UPDATED YET LOL
+export default function({
+  list,
+  property,
+  into = null,
+}) {
+  into ??=
+    (list.startsWith('#')
+      ? `${list}.${property}`
+      : `#${list}.${property}`);
+
+  return {
+    annotation: `withPropertyFromList`,
+    flags: {expose: true, compose: true},
+
+    expose: {
+      mapDependencies: {list},
+      mapContinuation: {into},
+      options: {property},
+
+      compute(continuation, {list, '#options': {property}}) {
+        if (list === undefined || empty(list)) {
+          return continuation({into: []});
+        }
+
+        return continuation({
+          into:
+            list.map(item =>
+              (item === null || item === undefined
+                ? null
+                : item[property] ?? null)),
+        });
+      },
+    },
+  };
+}
diff --git a/src/data/composite/data/withPropertyFromObject.js b/src/data/composite/data/withPropertyFromObject.js
new file mode 100644
index 00000000..b31bab15
--- /dev/null
+++ b/src/data/composite/data/withPropertyFromObject.js
@@ -0,0 +1,69 @@
+// Gets a property of some object (in a dependency) and provides that value.
+// If the object itself is null, or the object doesn't have the listed property,
+// the provided dependency will also be null.
+//
+// See also:
+//  - withPropertiesFromObject
+//  - withPropertyFromList
+//
+
+import {input, templateCompositeFrom} from '#composite';
+
+export default templateCompositeFrom({
+  annotation: `withPropertyFromObject`,
+
+  inputs: {
+    object: input({type: 'object', acceptsNull: true}),
+    property: input({type: 'string'}),
+  },
+
+  outputs: ({
+    [input.staticDependency('object')]: object,
+    [input.staticValue('property')]: property,
+  }) =>
+    (object && property
+      ? (object.startsWith('#')
+          ? [`${object}.${property}`]
+          : [`#${object}.${property}`])
+      : ['#value']),
+
+  steps: () => [
+    {
+      dependencies: [
+        input.staticDependency('object'),
+        input.staticValue('property'),
+      ],
+
+      compute: (continuation, {
+        [input.staticDependency('object')]: object,
+        [input.staticValue('property')]: property,
+      }) => continuation({
+        '#output':
+          (object && property
+            ? (object.startsWith('#')
+                ? `${object}.${property}`
+                : `#${object}.${property}`)
+            : '#value'),
+      }),
+    },
+
+    {
+      dependencies: [
+        '#output',
+        input('object'),
+        input('property'),
+      ],
+
+      compute: (continuation, {
+        ['#output']: output,
+        [input('object')]: object,
+        [input('property')]: property,
+      }) => continuation({
+        [output]:
+          (object === null
+            ? null
+            : object[property] ?? null),
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/data/withUnflattenedList.js b/src/data/composite/data/withUnflattenedList.js
new file mode 100644
index 00000000..3cfc247b
--- /dev/null
+++ b/src/data/composite/data/withUnflattenedList.js
@@ -0,0 +1,62 @@
+// After mapping the contents of a flattened array in-place (being careful to
+// retain the original indices by replacing unmatched results with null instead
+// of filtering them out), this function allows for recombining them. It will
+// filter out null and undefined items by default (pass {filter: false} to
+// disable this).
+
+import {input, templateCompositeFrom} from '#composite';
+import {isWholeNumber, validateArrayItems} from '#validators';
+
+export default templateCompositeFrom({
+  annotation: `withUnflattenedList`,
+
+  inputs: {
+    list: input({
+      type: 'array',
+      defaultDependency: '#flattenedList',
+    }),
+
+    indices: input({
+      validate: validateArrayItems(isWholeNumber),
+      defaultDependency: '#flattenedIndices',
+    }),
+
+    filter: input({
+      type: 'boolean',
+      defaultValue: true,
+    }),
+  },
+
+  outputs: ['#unflattenedList'],
+
+  steps: () => [
+    {
+      dependencies: [input('list'), input('indices'), input('filter')],
+      compute(continuation, {
+        [input('list')]: list,
+        [input('indices')]: indices,
+        [input('filter')]: filter,
+      }) {
+        const unflattenedList = [];
+
+        for (let i = 0; i < indices.length; i++) {
+          const startIndex = indices[i];
+          const endIndex =
+            (i === indices.length - 1
+              ? list.length
+              : indices[i + 1]);
+
+          const values = list.slice(startIndex, endIndex);
+          unflattenedList.push(
+            (filter
+              ? values.filter(value => value !== null && value !== undefined)
+              : values));
+        }
+
+        return continuation({
+          ['#unflattenedList']: unflattenedList,
+        });
+      },
+    },
+  ],
+});
diff --git a/src/data/composite/things/album/index.js b/src/data/composite/things/album/index.js
new file mode 100644
index 00000000..8139f10e
--- /dev/null
+++ b/src/data/composite/things/album/index.js
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 00000000..baa3cb4a
--- /dev/null
+++ b/src/data/composite/things/album/withTrackSections.js
@@ -0,0 +1,128 @@
+import {input, templateCompositeFrom} from '#composite';
+import find from '#find';
+import {empty, stitchArrays} from '#sugar';
+import {isTrackSectionList} from '#validators';
+import {filterMultipleArrays} from '#wiki-data';
+
+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: 'trackData',
+      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: 'trackData',
+      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
new file mode 100644
index 00000000..dcea6593
--- /dev/null
+++ b/src/data/composite/things/album/withTracks.js
@@ -0,0 +1,51 @@
+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: 'trackData',
+      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: 'trackData',
+      find: input.value(find.track),
+    }),
+
+    {
+      dependencies: ['#resolvedReferenceList'],
+      compute: (continuation, {
+        ['#resolvedReferenceList']: resolvedReferenceList,
+      }) => continuation({
+        ['#tracks']: resolvedReferenceList,
+      })
+    },
+  ],
+});
diff --git a/src/data/composite/things/track/exitWithoutUniqueCoverArt.js b/src/data/composite/things/track/exitWithoutUniqueCoverArt.js
new file mode 100644
index 00000000..f47086d9
--- /dev/null
+++ b/src/data/composite/things/track/exitWithoutUniqueCoverArt.js
@@ -0,0 +1,26 @@
+// 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
new file mode 100644
index 00000000..3354b1c4
--- /dev/null
+++ b/src/data/composite/things/track/index.js
@@ -0,0 +1,9 @@
+export {default as exitWithoutUniqueCoverArt} from './exitWithoutUniqueCoverArt.js';
+export {default as inheritFromOriginalRelease} from './inheritFromOriginalRelease.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';
diff --git a/src/data/composite/things/track/inheritFromOriginalRelease.js b/src/data/composite/things/track/inheritFromOriginalRelease.js
new file mode 100644
index 00000000..a9d57f86
--- /dev/null
+++ b/src/data/composite/things/track/inheritFromOriginalRelease.js
@@ -0,0 +1,43 @@
+// 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.
+
+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}),
+  },
+
+  steps: () => [
+    withOriginalRelease(),
+
+    {
+      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/trackReverseReferenceList.js b/src/data/composite/things/track/trackReverseReferenceList.js
new file mode 100644
index 00000000..e7bfedf3
--- /dev/null
+++ b/src/data/composite/things/track/trackReverseReferenceList.js
@@ -0,0 +1,38 @@
+// Like a normal reverse reference list ("objects which reference this object
+// under a specified property"), only excluding re-releases from the possible
+// outputs. While it's useful to travel from a re-release to the tracks it
+// references, re-releases aren't generally relevant from the perspective of
+// the tracks *being* referenced. Apart from hiding re-releases 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
new file mode 100644
index 00000000..34845ab0
--- /dev/null
+++ b/src/data/composite/things/track/withAlbum.js
@@ -0,0 +1,57 @@
+// 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.
+
+import {input, templateCompositeFrom} from '#composite';
+import {is} from '#validators';
+
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+
+export default templateCompositeFrom({
+  annotation: `withAlbum`,
+
+  inputs: {
+    notFoundMode: input({
+      validate: is('exit', 'null'),
+      defaultValue: 'null',
+    }),
+  },
+
+  outputs: ['#album'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: 'albumData',
+      mode: input.value('empty'),
+      output: input.value({
+        ['#album']: null,
+      }),
+    }),
+
+    {
+      dependencies: [input.myself(), 'albumData'],
+      compute: (continuation, {
+        [input.myself()]: track,
+        ['albumData']: albumData,
+      }) =>
+        continuation({
+          ['#album']:
+            albumData.find(album => album.tracks.includes(track)),
+        }),
+    },
+
+    raiseOutputWithoutDependency({
+      dependency: '#album',
+      output: input.value({
+        ['#album']: null,
+      }),
+    }),
+
+    {
+      dependencies: ['#album'],
+      compute: (continuation, {'#album': album}) =>
+        continuation.raiseOutput({'#album': album}),
+    },
+  ],
+});
diff --git a/src/data/composite/things/track/withAlwaysReferenceByDirectory.js b/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
new file mode 100644
index 00000000..d27f7b23
--- /dev/null
+++ b/src/data/composite/things/track/withAlwaysReferenceByDirectory.js
@@ -0,0 +1,91 @@
+// 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.
+//
+// See the implementation for an important caveat about matching the original
+// track against other tracks, which uses a custom implementation pulling (and
+// duplicating) details from #find instead of using withOriginalRelease and the
+// usual withResolvedReference / find.track() utilities.
+//
+
+import {input, templateCompositeFrom} from '#composite';
+import {isBoolean} from '#validators';
+
+import {exitWithoutDependency, exposeUpdateValueOrContinue}
+  from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+
+// TODO: Kludge. (The usage of this, not so much the import.)
+import CacheableObject from '../../../things/cacheable-object.js';
+
+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),
+    }),
+
+    // "Slow" / uncached, manual search from trackData (with this track
+    // excluded). Otherwise there end up being pretty bad recursion issues
+    // (track1.alwaysReferencedByDirectory depends on searching through data
+    // including track2, which depends on evaluating track2.alwaysReferenced-
+    // ByDirectory, which depends on searcing through data including track1...)
+    // That said, this is 100% a kludge, since it involves duplicating find
+    // logic on a completely unrelated context.
+    {
+      dependencies: [input.myself(), 'trackData', 'originalReleaseTrack'],
+      compute: (continuation, {
+        [input.myself()]: thisTrack,
+        ['trackData']: trackData,
+        ['originalReleaseTrack']: ref,
+      }) => continuation({
+        ['#originalRelease']:
+          (ref.startsWith('track:')
+            ? trackData.find(track => track.directory === ref.slice('track:'.length))
+            : trackData.find(track =>
+                track !== thisTrack &&
+                !CacheableObject.getUpdateValue(track, 'originalReleaseTrack') &&
+                track.name.toLowerCase() === ref.toLowerCase())),
+      })
+    },
+
+    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
new file mode 100644
index 00000000..b2e5f2b3
--- /dev/null
+++ b/src/data/composite/things/track/withContainingTrackSection.js
@@ -0,0 +1,63 @@
+// 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
new file mode 100644
index 00000000..96078d5f
--- /dev/null
+++ b/src/data/composite/things/track/withHasUniqueCoverArt.js
@@ -0,0 +1,61 @@
+// 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
new file mode 100644
index 00000000..d2ee39df
--- /dev/null
+++ b/src/data/composite/things/track/withOriginalRelease.js
@@ -0,0 +1,59 @@
+// 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. Note that this will early exit if the original release is
+// specified by reference and that reference doesn't resolve to anything.
+// Outputs to '#originalRelease' by default.
+
+import {input, templateCompositeFrom} from '#composite';
+import find from '#find';
+import {validateWikiData} from '#validators';
+
+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',
+    }),
+  },
+
+  outputs: ['#originalRelease'],
+
+  steps: () => [
+    withResolvedReference({
+      ref: 'originalReleaseTrack',
+      data: input('data'),
+      find: input.value(find.track),
+      notFoundMode: input.value('exit'),
+    }).outputs({
+      ['#resolvedReference']: '#originalRelease',
+    }),
+
+    {
+      dependencies: [
+        input.myself(),
+        input('selfIfOriginal'),
+        '#originalRelease',
+      ],
+
+      compute: (continuation, {
+        [input.myself()]: track,
+        [input('selfIfOriginal')]: selfIfOriginal,
+        ['#originalRelease']: originalRelease,
+      }) =>
+        continuation({
+          ['#originalRelease']:
+            (originalRelease ??
+              (selfIfOriginal
+                ? track
+                : null)),
+        }),
+    },
+  ],
+});
diff --git a/src/data/composite/things/track/withOtherReleases.js b/src/data/composite/things/track/withOtherReleases.js
new file mode 100644
index 00000000..84420cf8
--- /dev/null
+++ b/src/data/composite/things/track/withOtherReleases.js
@@ -0,0 +1,40 @@
+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),
+    }),
+
+    {
+      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
new file mode 100644
index 00000000..b236a6e8
--- /dev/null
+++ b/src/data/composite/things/track/withPropertyFromAlbum.js
@@ -0,0 +1,49 @@
+// 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,
+      }),
+    },
+  ],
+});
diff --git a/src/data/composite/wiki-data/exitWithoutContribs.js b/src/data/composite/wiki-data/exitWithoutContribs.js
new file mode 100644
index 00000000..2c8219fc
--- /dev/null
+++ b/src/data/composite/wiki-data/exitWithoutContribs.js
@@ -0,0 +1,47 @@
+// Shorthand for exiting if the contribution list (usually a property's update
+// value) resolves to empty - ensuring that the later computed results are only
+// returned if these contributions are present.
+
+import {input, templateCompositeFrom} from '#composite';
+import {isContributionList} from '#validators';
+
+import {withResultOfAvailabilityCheck} from '#composite/control-flow';
+
+import withResolvedContribs from './withResolvedContribs.js';
+
+export default templateCompositeFrom({
+  annotation: `exitWithoutContribs`,
+
+  inputs: {
+    contribs: input({
+      validate: isContributionList,
+      acceptsNull: true,
+    }),
+
+    value: input({defaultValue: null}),
+  },
+
+  steps: () => [
+    withResolvedContribs({
+      from: input('contribs'),
+    }),
+
+    // TODO: Fairly certain exitWithoutDependency would be sufficient here.
+
+    withResultOfAvailabilityCheck({
+      from: '#resolvedContribs',
+      mode: input.value('empty'),
+    }),
+
+    {
+      dependencies: ['#availability', input('value')],
+      compute: (continuation, {
+        ['#availability']: availability,
+        [input('value')]: value,
+      }) =>
+        (availability
+          ? continuation()
+          : continuation.exit(value)),
+    },
+  ],
+});
diff --git a/src/data/composite/wiki-data/index.js b/src/data/composite/wiki-data/index.js
new file mode 100644
index 00000000..1d0400fc
--- /dev/null
+++ b/src/data/composite/wiki-data/index.js
@@ -0,0 +1,7 @@
+export {default as exitWithoutContribs} from './exitWithoutContribs.js';
+export {default as inputThingClass} from './inputThingClass.js';
+export {default as inputWikiData} from './inputWikiData.js';
+export {default as withResolvedContribs} from './withResolvedContribs.js';
+export {default as withResolvedReference} from './withResolvedReference.js';
+export {default as withResolvedReferenceList} from './withResolvedReferenceList.js';
+export {default as withReverseReferenceList} from './withReverseReferenceList.js';
diff --git a/src/data/composite/wiki-data/inputThingClass.js b/src/data/composite/wiki-data/inputThingClass.js
new file mode 100644
index 00000000..d70480e6
--- /dev/null
+++ b/src/data/composite/wiki-data/inputThingClass.js
@@ -0,0 +1,23 @@
+// Please note that this input, used in a variety of #composite/wiki-data
+// utilities, is basically always a kludge. Any usage of it depends on
+// referencing Thing class values defined outside of the #composite folder.
+
+import {input} from '#composite';
+import {isType} from '#validators';
+
+// TODO: Kludge.
+import Thing from '../../things/thing.js';
+
+export default function inputThingClass() {
+  return input.staticValue({
+    validate(thingClass) {
+      isType(thingClass, 'function');
+
+      if (!Object.hasOwn(thingClass, Thing.referenceType)) {
+        throw new TypeError(`Expected a Thing constructor, missing Thing.referenceType`);
+      }
+
+      return true;
+    },
+  });
+}
diff --git a/src/data/composite/wiki-data/inputWikiData.js b/src/data/composite/wiki-data/inputWikiData.js
new file mode 100644
index 00000000..cf7a7c2c
--- /dev/null
+++ b/src/data/composite/wiki-data/inputWikiData.js
@@ -0,0 +1,17 @@
+import {input} from '#composite';
+import {validateWikiData} from '#validators';
+
+// TODO: This doesn't access a class's own ThingSubclass[Thing.referenceType]
+// value because classes aren't initialized by when templateCompositeFrom gets
+// called (see: circular imports). So the reference types have to be hard-coded,
+// which somewhat defeats the point of storing them on the class in the first
+// place...
+export default function inputWikiData({
+  referenceType = '',
+  allowMixedTypes = false,
+} = {}) {
+  return input({
+    validate: validateWikiData({referenceType, allowMixedTypes}),
+    acceptsNull: true,
+  });
+}
diff --git a/src/data/composite/wiki-data/withResolvedContribs.js b/src/data/composite/wiki-data/withResolvedContribs.js
new file mode 100644
index 00000000..eda24160
--- /dev/null
+++ b/src/data/composite/wiki-data/withResolvedContribs.js
@@ -0,0 +1,77 @@
+// Resolves the contribsByRef contained in the provided dependency,
+// providing (named by the second argument) the result. "Resolving"
+// means mapping the "who" reference of each contribution to an artist
+// object, and filtering out those whose "who" doesn't match any artist.
+
+import {input, templateCompositeFrom} from '#composite';
+import find from '#find';
+import {stitchArrays} from '#sugar';
+import {is, isContributionList} from '#validators';
+import {filterMultipleArrays} from '#wiki-data';
+
+import {
+  raiseOutputWithoutDependency,
+} from '#composite/control-flow';
+
+import {
+  withPropertiesFromList,
+} from '#composite/data';
+
+import withResolvedReferenceList from './withResolvedReferenceList.js';
+
+export default templateCompositeFrom({
+  annotation: `withResolvedContribs`,
+
+  inputs: {
+    from: input({
+      validate: isContributionList,
+      acceptsNull: true,
+    }),
+
+    notFoundMode: input({
+      validate: is('exit', 'filter', 'null'),
+      defaultValue: 'null',
+    }),
+  },
+
+  outputs: ['#resolvedContribs'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: input('from'),
+      mode: input.value('empty'),
+      output: input.value({
+        ['#resolvedContribs']: [],
+      }),
+    }),
+
+    withPropertiesFromList({
+      list: input('from'),
+      properties: input.value(['who', 'what']),
+      prefix: input.value('#contribs'),
+    }),
+
+    withResolvedReferenceList({
+      list: '#contribs.who',
+      data: 'artistData',
+      find: input.value(find.artist),
+      notFoundMode: input('notFoundMode'),
+    }).outputs({
+      ['#resolvedReferenceList']: '#contribs.who',
+    }),
+
+    {
+      dependencies: ['#contribs.who', '#contribs.what'],
+
+      compute(continuation, {
+        ['#contribs.who']: who,
+        ['#contribs.what']: what,
+      }) {
+        filterMultipleArrays(who, what, (who, _what) => who);
+        return continuation({
+          ['#resolvedContribs']: stitchArrays({who, what}),
+        });
+      },
+    },
+  ],
+});
diff --git a/src/data/composite/wiki-data/withResolvedReference.js b/src/data/composite/wiki-data/withResolvedReference.js
new file mode 100644
index 00000000..0fa5c554
--- /dev/null
+++ b/src/data/composite/wiki-data/withResolvedReference.js
@@ -0,0 +1,73 @@
+// Resolves a reference by using the provided find function to match it
+// within the provided thingData dependency. This will early exit if the
+// data dependency is null, or, if notFoundMode is set to 'exit', if the find
+// function doesn't match anything for the reference. Otherwise, the data
+// object is provided on the output dependency; or null, if the reference
+// doesn't match anything or itself was null to begin with.
+
+import {input, templateCompositeFrom} from '#composite';
+import {is} from '#validators';
+
+import {
+  exitWithoutDependency,
+  raiseOutputWithoutDependency,
+} from '#composite/control-flow';
+
+import inputWikiData from './inputWikiData.js';
+
+export default templateCompositeFrom({
+  annotation: `withResolvedReference`,
+
+  inputs: {
+    ref: input({type: 'string', acceptsNull: true}),
+
+    data: inputWikiData({allowMixedTypes: false}),
+    find: input({type: 'function'}),
+
+    notFoundMode: input({
+      validate: is('null', 'exit'),
+      defaultValue: 'null',
+    }),
+  },
+
+  outputs: ['#resolvedReference'],
+
+  steps: () => [
+    raiseOutputWithoutDependency({
+      dependency: input('ref'),
+      output: input.value({
+        ['#resolvedReference']: null,
+      }),
+    }),
+
+    exitWithoutDependency({
+      dependency: input('data'),
+    }),
+
+    {
+      dependencies: [
+        input('ref'),
+        input('data'),
+        input('find'),
+        input('notFoundMode'),
+      ],
+
+      compute(continuation, {
+        [input('ref')]: ref,
+        [input('data')]: data,
+        [input('find')]: findFunction,
+        [input('notFoundMode')]: notFoundMode,
+      }) {
+        const match = findFunction(ref, data, {mode: 'quiet'});
+
+        if (match === null && notFoundMode === 'exit') {
+          return continuation.exit(null);
+        }
+
+        return continuation.raiseOutput({
+          ['#resolvedReference']: match ?? null,
+        });
+      },
+    },
+  ],
+});
diff --git a/src/data/composite/wiki-data/withResolvedReferenceList.js b/src/data/composite/wiki-data/withResolvedReferenceList.js
new file mode 100644
index 00000000..1d39e5b2
--- /dev/null
+++ b/src/data/composite/wiki-data/withResolvedReferenceList.js
@@ -0,0 +1,101 @@
+// Resolves a list of references, with each reference matched with provided
+// data in the same way as withResolvedReference. This will early exit if the
+// data dependency is null (even if the reference list is empty). By default
+// it will filter out references which don't match, but this can be changed
+// to early exit ({notFoundMode: 'exit'}) or leave null in place ('null').
+
+import {input, templateCompositeFrom} from '#composite';
+import {is, isString, validateArrayItems} from '#validators';
+
+import {
+  exitWithoutDependency,
+  raiseOutputWithoutDependency,
+} from '#composite/control-flow';
+
+import inputWikiData from './inputWikiData.js';
+
+export default templateCompositeFrom({
+  annotation: `withResolvedReferenceList`,
+
+  inputs: {
+    list: input({
+      validate: validateArrayItems(isString),
+      acceptsNull: true,
+    }),
+
+    data: inputWikiData({allowMixedTypes: false}),
+    find: input({type: 'function'}),
+
+    notFoundMode: input({
+      validate: is('exit', 'filter', 'null'),
+      defaultValue: 'filter',
+    }),
+  },
+
+  outputs: ['#resolvedReferenceList'],
+
+  steps: () => [
+    exitWithoutDependency({
+      dependency: input('data'),
+      value: input.value([]),
+    }),
+
+    raiseOutputWithoutDependency({
+      dependency: input('list'),
+      mode: input.value('empty'),
+      output: input.value({
+        ['#resolvedReferenceList']: [],
+      }),
+    }),
+
+    {
+      dependencies: [input('list'), input('data'), input('find')],
+      compute: (continuation, {
+        [input('list')]: list,
+        [input('data')]: data,
+        [input('find')]: findFunction,
+      }) =>
+        continuation({
+          '#matches': list.map(ref => findFunction(ref, data, {mode: 'quiet'})),
+        }),
+    },
+
+    {
+      dependencies: ['#matches'],
+      compute: (continuation, {'#matches': matches}) =>
+        (matches.every(match => match)
+          ? continuation.raiseOutput({
+              ['#resolvedReferenceList']: matches,
+            })
+          : continuation()),
+    },
+
+    {
+      dependencies: ['#matches', input('notFoundMode')],
+      compute(continuation, {
+        ['#matches']: matches,
+        [input('notFoundMode')]: notFoundMode,
+      }) {
+        switch (notFoundMode) {
+          case 'exit':
+            return continuation.exit([]);
+
+          case 'filter':
+            return continuation.raiseOutput({
+              ['#resolvedReferenceList']:
+                matches.filter(match => match),
+            });
+
+          case 'null':
+            return continuation.raiseOutput({
+              ['#resolvedReferenceList']:
+                matches.map(match => match ?? null),
+            });
+
+          default:
+            throw new TypeError(`Expected notFoundMode to be exit, filter, or null`);
+        }
+      },
+    },
+  ],
+});
diff --git a/src/data/composite/wiki-data/withReverseReferenceList.js b/src/data/composite/wiki-data/withReverseReferenceList.js
new file mode 100644
index 00000000..a025b5ed
--- /dev/null
+++ b/src/data/composite/wiki-data/withReverseReferenceList.js
@@ -0,0 +1,41 @@
+// Check out the info on reverseReferenceList!
+// This is its composable form.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import {exitWithoutDependency} from '#composite/control-flow';
+
+import inputWikiData from './inputWikiData.js';
+
+export default templateCompositeFrom({
+  annotation: `withReverseReferenceList`,
+
+  inputs: {
+    data: inputWikiData({allowMixedTypes: false}),
+    list: input({type: 'string'}),
+  },
+
+  outputs: ['#reverseReferenceList'],
+
+  steps: () => [
+    exitWithoutDependency({
+      dependency: input('data'),
+      value: input.value([]),
+      mode: input.value('empty'),
+    }),
+
+    {
+      dependencies: [input.myself(), input('data'), input('list')],
+
+      compute: (continuation, {
+        [input.myself()]: thisThing,
+        [input('data')]: data,
+        [input('list')]: refListProperty,
+      }) =>
+        continuation({
+          ['#reverseReferenceList']:
+            data.filter(thing => thing[refListProperty].includes(thisThing)),
+        }),
+    },
+  ],
+});
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/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..fbea9d5c
--- /dev/null
+++ b/src/data/composite/wiki-properties/commentary.js
@@ -0,0 +1,12 @@
+// Artist commentary! Generally present on tracks and albums.
+
+import {isCommentary} from '#validators';
+
+// TODO: Not templateCompositeFrom.
+
+export default function() {
+  return {
+    flags: {update: true, expose: true},
+    update: {validate: isCommentary},
+  };
+}
diff --git a/src/data/composite/wiki-properties/commentatorArtists.js b/src/data/composite/wiki-properties/commentatorArtists.js
new file mode 100644
index 00000000..52aeb868
--- /dev/null
+++ b/src/data/composite/wiki-properties/commentatorArtists.js
@@ -0,0 +1,55 @@
+// This one's kinda tricky: it parses artist "references" from the
+// commentary content, and finds the matching artist for each reference.
+// This is mostly useful for credits and listings on artist pages.
+
+import {input, templateCompositeFrom} from '#composite';
+import find from '#find';
+import {unique} from '#sugar';
+
+import {exitWithoutDependency} from '#composite/control-flow';
+import {withResolvedReferenceList} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `commentatorArtists`,
+
+  compose: false,
+
+  steps: () => [
+    exitWithoutDependency({
+      dependency: 'commentary',
+      mode: input.value('falsy'),
+      value: input.value([]),
+    }),
+
+    {
+      dependencies: ['commentary'],
+      compute: (continuation, {commentary}) =>
+        continuation({
+          '#artistRefs':
+            Array.from(
+              commentary
+                .replace(/<\/?b>/g, '')
+                .matchAll(/<i>(?<who>.*?):<\/i>/g))
+              .map(({groups: {who}}) => who),
+        }),
+    },
+
+    withResolvedReferenceList({
+      list: '#artistRefs',
+      data: 'artistData',
+      find: input.value(find.artist),
+    }).outputs({
+      '#resolvedReferenceList': '#artists',
+    }),
+
+    {
+      flags: {expose: true},
+
+      expose: {
+        dependencies: ['#artists'],
+        compute: ({'#artists': artists}) =>
+          unique(artists),
+      },
+    },
+  ],
+});
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..8fde2caa
--- /dev/null
+++ b/src/data/composite/wiki-properties/contributionList.js
@@ -0,0 +1,35 @@
+// 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:
+//
+//   [
+//     {who: 'Artist Name', what: 'Viola'},
+//     {who: 'artist:john-cena', what: null},
+//     ...
+//   ]
+//
+// ...typically as processed from YAML, spreadsheet, or elsewhere.
+// Exposes as the same, but with the "who" 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} from '#validators';
+
+import {exposeConstant, exposeDependencyOrContinue} from '#composite/control-flow';
+import {withResolvedContribs} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `contributionList`,
+
+  compose: false,
+
+  update: {validate: isContributionList},
+
+  steps: () => [
+    withResolvedContribs({from: input.updateValue()}),
+    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..0b2181c9
--- /dev/null
+++ b/src/data/composite/wiki-properties/directory.js
@@ -0,0 +1,23 @@
+// 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 {isDirectory} from '#validators';
+import {getKebabCase} from '#wiki-data';
+
+// TODO: Not templateCompositeFrom.
+
+export default function() {
+  return {
+    flags: {update: true, expose: true},
+    update: {validate: isDirectory},
+    expose: {
+      dependencies: ['name'],
+      transform(directory, {name}) {
+        if (directory === null && name === null) return null;
+        else if (directory === null) return getKebabCase(name);
+        else return 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/index.js b/src/data/composite/wiki-properties/index.js
new file mode 100644
index 00000000..2462b047
--- /dev/null
+++ b/src/data/composite/wiki-properties/index.js
@@ -0,0 +1,20 @@
+export {default as additionalFiles} from './additionalFiles.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 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 name} from './name.js';
+export {default as referenceList} from './referenceList.js';
+export {default as reverseReferenceList} from './reverseReferenceList.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 urls} from './urls.js';
+export {default as wikiData} from './wikiData.js';
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..f5b6c58e
--- /dev/null
+++ b/src/data/composite/wiki-properties/referenceList.js
@@ -0,0 +1,47 @@
+// Stores and exposes a list of references to other data objects; all items
+// must be references to the same type, which is specified on the class input.
+//
+// See also:
+//  - singleReference
+//  - withResolvedReferenceList
+//
+
+import {input, templateCompositeFrom} from '#composite';
+import {validateReferenceList} from '#validators';
+
+import {exposeDependency} from '#composite/control-flow';
+import {inputThingClass, inputWikiData, withResolvedReferenceList}
+  from '#composite/wiki-data';
+
+// TODO: Kludge.
+import Thing from '../../things/thing.js';
+
+export default templateCompositeFrom({
+  annotation: `referenceList`,
+
+  compose: false,
+
+  inputs: {
+    class: inputThingClass(),
+
+    data: inputWikiData({allowMixedTypes: false}),
+    find: input({type: 'function'}),
+  },
+
+  update: ({
+    [input.staticValue('class')]: thingClass,
+  }) => {
+    const {[Thing.referenceType]: referenceType} = thingClass;
+    return {validate: validateReferenceList(referenceType)};
+  },
+
+  steps: () => [
+    withResolvedReferenceList({
+      list: input.updateValue(),
+      data: input('data'),
+      find: input('find'),
+    }),
+
+    exposeDependency({dependency: '#resolvedReferenceList'}),
+  ],
+});
diff --git a/src/data/composite/wiki-properties/reverseReferenceList.js b/src/data/composite/wiki-properties/reverseReferenceList.js
new file mode 100644
index 00000000..84ba67df
--- /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. Naturally, the passed ref list property is of the things in the
+// wiki data provided, not the requesting Thing itself.
+
+import {input, templateCompositeFrom} from '#composite';
+
+import {exposeDependency} from '#composite/control-flow';
+import {inputWikiData, withReverseReferenceList} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `reverseReferenceList`,
+
+  compose: false,
+
+  inputs: {
+    data: inputWikiData({allowMixedTypes: false}),
+    list: input({type: 'string'}),
+  },
+
+  steps: () => [
+    withReverseReferenceList({
+      data: input('data'),
+      list: input('list'),
+    }),
+
+    exposeDependency({dependency: '#reverseReferenceList'}),
+  ],
+});
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..18d65146
--- /dev/null
+++ b/src/data/composite/wiki-properties/simpleString.js
@@ -0,0 +1,14 @@
+// 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';
+
+// TODO: Not templateCompositeFrom.
+
+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..34bd2e6d
--- /dev/null
+++ b/src/data/composite/wiki-properties/singleReference.js
@@ -0,0 +1,47 @@
+// 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 {validateReference} from '#validators';
+
+import {exposeDependency} from '#composite/control-flow';
+import {inputThingClass, inputWikiData, withResolvedReference}
+  from '#composite/wiki-data';
+
+// TODO: Kludge.
+import Thing from '../../things/thing.js';
+
+export default templateCompositeFrom({
+  annotation: `singleReference`,
+
+  compose: false,
+
+  inputs: {
+    class: inputThingClass(),
+    find: input({type: 'function'}),
+    data: inputWikiData({allowMixedTypes: false}),
+  },
+
+  update: ({
+    [input.staticValue('class')]: thingClass,
+  }) => {
+    const {[Thing.referenceType]: referenceType} = thingClass;
+    return {validate: validateReference(referenceType)};
+  },
+
+  steps: () => [
+    withResolvedReference({
+      ref: input.updateValue(),
+      data: input('data'),
+      find: input('find'),
+    }),
+
+    exposeDependency({dependency: '#resolvedReference'}),
+  ],
+});
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/wikiData.js b/src/data/composite/wiki-properties/wikiData.js
new file mode 100644
index 00000000..4ea47785
--- /dev/null
+++ b/src/data/composite/wiki-properties/wikiData.js
@@ -0,0 +1,17 @@
+// General purpose wiki data constructor, for properties like artistData,
+// trackData, etc.
+
+import {validateArrayItems, validateInstanceOf} from '#validators';
+
+// TODO: Not templateCompositeFrom.
+
+// TODO: This should validate with validateWikiData.
+
+export default function(thingClass) {
+  return {
+    flags: {update: true},
+    update: {
+      validate: validateArrayItems(validateInstanceOf(thingClass)),
+    },
+  };
+}
diff --git a/src/data/things/album.js b/src/data/things/album.js
index c012c243..f451a7e9 100644
--- a/src/data/things/album.js
+++ b/src/data/things/album.js
@@ -1,163 +1,143 @@
-import {empty} from '#sugar';
+import {input} from '#composite';
 import find from '#find';
+import {isDate} from '#validators';
+
+import {exposeDependency, exposeUpdateValueOrContinue}
+  from '#composite/control-flow';
+import {exitWithoutContribs} from '#composite/wiki-data';
+
+import {
+  additionalFiles,
+  commentary,
+  color,
+  commentatorArtists,
+  contribsPresent,
+  contributionList,
+  dimensions,
+  directory,
+  fileExtension,
+  flag,
+  name,
+  referenceList,
+  simpleDate,
+  simpleString,
+  urls,
+  wikiData,
+} from '#composite/wiki-properties';
+
+import {
+  withTracks,
+  withTrackSections,
+} from '#composite/things/album';
 
 import Thing from './thing.js';
 
 export class Album extends Thing {
   static [Thing.referenceType] = 'album';
 
-  static [Thing.getPropertyDescriptors] = ({
-    ArtTag,
-    Artist,
-    Group,
-    Track,
-
-    validators: {
-      isDate,
-      isDimensions,
-      isTrackSectionList,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({ArtTag, Artist, Group, Track}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Album'),
-    color: Thing.common.color(),
-    directory: Thing.common.directory(),
-    urls: Thing.common.urls(),
-
-    date: Thing.common.simpleDate(),
-    trackArtDate: Thing.common.simpleDate(),
-    dateAddedToWiki: Thing.common.simpleDate(),
-
-    coverArtDate: {
-      flags: {update: true, expose: true},
-
-      update: {validate: isDate},
-
-      expose: {
-        dependencies: ['date', 'coverArtistContribsByRef'],
-        transform: (coverArtDate, {
-          coverArtistContribsByRef,
-          date,
-        }) =>
-          (!empty(coverArtistContribsByRef)
-            ? coverArtDate ?? date ?? null
-            : null),
-      },
-    },
-
-    artistContribsByRef: Thing.common.contribsByRef(),
-    coverArtistContribsByRef: Thing.common.contribsByRef(),
-    trackCoverArtistContribsByRef: Thing.common.contribsByRef(),
-    wallpaperArtistContribsByRef: Thing.common.contribsByRef(),
-    bannerArtistContribsByRef: Thing.common.contribsByRef(),
-
-    groupsByRef: Thing.common.referenceList(Group),
-    artTagsByRef: Thing.common.referenceList(ArtTag),
-
-    trackSections: {
-      flags: {update: true, expose: true},
-
-      update: {
-        validate: isTrackSectionList,
-      },
-
-      expose: {
-        dependencies: ['color', 'trackData'],
-        transform(trackSections, {
-          color: albumColor,
-          trackData,
-        }) {
-          let startIndex = 0;
-          return trackSections?.map(section => ({
-            name: section.name ?? null,
-            color: section.color ?? albumColor ?? null,
-            dateOriginallyReleased: section.dateOriginallyReleased ?? null,
-            isDefaultTrackSection: section.isDefaultTrackSection ?? false,
-
-            startIndex: (
-              startIndex += section.tracksByRef.length,
-              startIndex - section.tracksByRef.length
-            ),
-
-            tracksByRef: section.tracksByRef ?? [],
-            tracks:
-              (trackData && section.tracksByRef
-                ?.map(ref => find.track(ref, trackData, {mode: 'quiet'}))
-                .filter(Boolean)) ??
-              [],
-          }));
-        },
-      },
-    },
-
-    coverArtFileExtension: Thing.common.fileExtension('jpg'),
-    trackCoverArtFileExtension: Thing.common.fileExtension('jpg'),
-
-    wallpaperStyle: Thing.common.simpleString(),
-    wallpaperFileExtension: Thing.common.fileExtension('jpg'),
-
-    bannerStyle: Thing.common.simpleString(),
-    bannerFileExtension: Thing.common.fileExtension('jpg'),
-    bannerDimensions: {
-      flags: {update: true, expose: true},
-      update: {validate: isDimensions},
-    },
-
-    hasTrackNumbers: Thing.common.flag(true),
-    isListedOnHomepage: Thing.common.flag(true),
-    isListedInGalleries: Thing.common.flag(true),
-
-    commentary: Thing.common.commentary(),
-    additionalFiles: Thing.common.additionalFiles(),
+    name: name('Unnamed Album'),
+    color: color(),
+    directory: directory(),
+    urls: urls(),
+
+    date: simpleDate(),
+    trackArtDate: simpleDate(),
+    dateAddedToWiki: simpleDate(),
+
+    coverArtDate: [
+      exitWithoutContribs({contribs: 'coverArtistContribs'}),
+
+      exposeUpdateValueOrContinue({
+        validate: input.value(isDate),
+      }),
+
+      exposeDependency({dependency: 'date'}),
+    ],
+
+    coverArtFileExtension: [
+      exitWithoutContribs({contribs: 'coverArtistContribs'}),
+      fileExtension('jpg'),
+    ],
+
+    trackCoverArtFileExtension: fileExtension('jpg'),
+
+    wallpaperFileExtension: [
+      exitWithoutContribs({contribs: 'wallpaperArtistContribs'}),
+      fileExtension('jpg'),
+    ],
+
+    bannerFileExtension: [
+      exitWithoutContribs({contribs: 'bannerArtistContribs'}),
+      fileExtension('jpg'),
+    ],
+
+    wallpaperStyle: [
+      exitWithoutContribs({contribs: 'wallpaperArtistContribs'}),
+      simpleString(),
+    ],
+
+    bannerStyle: [
+      exitWithoutContribs({contribs: 'bannerArtistContribs'}),
+      simpleString(),
+    ],
+
+    bannerDimensions: [
+      exitWithoutContribs({contribs: 'bannerArtistContribs'}),
+      dimensions(),
+    ],
+
+    hasTrackNumbers: flag(true),
+    isListedOnHomepage: flag(true),
+    isListedInGalleries: flag(true),
+
+    commentary: commentary(),
+    additionalFiles: additionalFiles(),
+
+    trackSections: [
+      withTrackSections(),
+      exposeDependency({dependency: '#trackSections'}),
+    ],
+
+    artistContribs: contributionList(),
+    coverArtistContribs: contributionList(),
+    trackCoverArtistContribs: contributionList(),
+    wallpaperArtistContribs: contributionList(),
+    bannerArtistContribs: contributionList(),
+
+    groups: referenceList({
+      class: input.value(Group),
+      find: input.value(find.group),
+      data: 'groupData',
+    }),
+
+    artTags: referenceList({
+      class: input.value(ArtTag),
+      find: input.value(find.artTag),
+      data: 'artTagData',
+    }),
 
     // Update only
 
-    artistData: Thing.common.wikiData(Artist),
-    artTagData: Thing.common.wikiData(ArtTag),
-    groupData: Thing.common.wikiData(Group),
-    trackData: Thing.common.wikiData(Track),
+    artistData: wikiData(Artist),
+    artTagData: wikiData(ArtTag),
+    groupData: wikiData(Group),
+    trackData: wikiData(Track),
 
     // Expose only
 
-    artistContribs: Thing.common.dynamicContribs('artistContribsByRef'),
-    coverArtistContribs: Thing.common.dynamicContribs('coverArtistContribsByRef'),
-    trackCoverArtistContribs: Thing.common.dynamicContribs('trackCoverArtistContribsByRef'),
-    wallpaperArtistContribs: Thing.common.dynamicContribs('wallpaperArtistContribsByRef'),
-    bannerArtistContribs: Thing.common.dynamicContribs('bannerArtistContribsByRef'),
-
-    commentatorArtists: Thing.common.commentatorArtists(),
-
-    hasCoverArt: Thing.common.contribsPresent('coverArtistContribsByRef'),
-    hasWallpaperArt: Thing.common.contribsPresent('wallpaperArtistContribsByRef'),
-    hasBannerArt: Thing.common.contribsPresent('bannerArtistContribsByRef'),
-
-    tracks: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['trackSections', 'trackData'],
-        compute: ({trackSections, trackData}) =>
-          trackSections && trackData
-            ? trackSections
-                .flatMap((section) => section.tracksByRef ?? [])
-                .map((ref) => find.track(ref, trackData, {mode: 'quiet'}))
-                .filter(Boolean)
-            : [],
-      },
-    },
-
-    groups: Thing.common.dynamicThingsFromReferenceList(
-      'groupsByRef',
-      'groupData',
-      find.group
-    ),
-
-    artTags: Thing.common.dynamicThingsFromReferenceList(
-      'artTagsByRef',
-      'artTagData',
-      find.artTag
-    ),
+    commentatorArtists: commentatorArtists(),
+
+    hasCoverArt: contribsPresent({contribs: 'coverArtistContribs'}),
+    hasWallpaperArt: contribsPresent({contribs: 'wallpaperArtistContribs'}),
+    hasBannerArt: contribsPresent({contribs: 'bannerArtistContribs'}),
+
+    tracks: [
+      withTracks(),
+      exposeDependency({dependency: '#tracks'}),
+    ],
   });
 
   static [Thing.getSerializeDescriptors] = ({
@@ -202,9 +182,9 @@ export class Album extends Thing {
 
 export class TrackSectionHelper extends Thing {
   static [Thing.getPropertyDescriptors] = () => ({
-    name: Thing.common.name('Unnamed Track Group'),
-    color: Thing.common.color(),
-    dateOriginallyReleased: Thing.common.simpleDate(),
-    isDefaultTrackGroup: Thing.common.flag(false),
+    name: name('Unnamed Track Section'),
+    color: color(),
+    dateOriginallyReleased: simpleDate(),
+    isDefaultTrackGroup: flag(false),
   })
 }
diff --git a/src/data/things/art-tag.js b/src/data/things/art-tag.js
index c103c4d5..1266a4e0 100644
--- a/src/data/things/art-tag.js
+++ b/src/data/things/art-tag.js
@@ -1,35 +1,46 @@
+import {input} from '#composite';
 import {sortAlbumsTracksChronologically} from '#wiki-data';
+import {isName} from '#validators';
+
+import {exposeUpdateValueOrContinue} from '#composite/control-flow';
+
+import {
+  color,
+  directory,
+  flag,
+  name,
+  wikiData,
+} from '#composite/wiki-properties';
 
 import Thing from './thing.js';
 
 export class ArtTag extends Thing {
   static [Thing.referenceType] = 'tag';
 
-  static [Thing.getPropertyDescriptors] = ({
-    Album,
-    Track,
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({Album, Track}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Art Tag'),
-    directory: Thing.common.directory(),
-    color: Thing.common.color(),
-    isContentWarning: Thing.common.flag(false),
+    name: name('Unnamed Art Tag'),
+    directory: directory(),
+    color: color(),
+    isContentWarning: flag(false),
 
-    nameShort: {
-      flags: {update: true, expose: true},
+    nameShort: [
+      exposeUpdateValueOrContinue({
+        validate: input.value(isName),
+      }),
 
-      expose: {
+      {
         dependencies: ['name'],
-        transform: (value, {name}) =>
-          value ?? name.replace(/ \(.*?\)$/, ''),
+        compute: ({name}) =>
+          name.replace(/ \([^)]*?\)$/, ''),
       },
-    },
+    ],
 
     // Update only
 
-    albumData: Thing.common.wikiData(Album),
-    trackData: Thing.common.wikiData(Track),
+    albumData: wikiData(Album),
+    trackData: wikiData(Track),
 
     // Expose only
 
@@ -37,8 +48,8 @@ export class ArtTag extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['albumData', 'trackData'],
-        compute: ({albumData, trackData, [ArtTag.instance]: artTag}) =>
+        dependencies: ['this', 'albumData', 'trackData'],
+        compute: ({this: artTag, albumData, trackData}) =>
           sortAlbumsTracksChronologically(
             [...albumData, ...trackData]
               .filter(({artTags}) => artTags.includes(artTag)),
diff --git a/src/data/things/artist.js b/src/data/things/artist.js
index 522ca5f9..ff9f8aee 100644
--- a/src/data/things/artist.js
+++ b/src/data/things/artist.js
@@ -1,29 +1,33 @@
+import {input} from '#composite';
 import find from '#find';
+import {isName, validateArrayItems} from '#validators';
+
+import {
+  directory,
+  fileExtension,
+  flag,
+  name,
+  simpleString,
+  singleReference,
+  urls,
+  wikiData,
+} from '#composite/wiki-properties';
 
 import Thing from './thing.js';
 
 export class Artist extends Thing {
   static [Thing.referenceType] = 'artist';
 
-  static [Thing.getPropertyDescriptors] = ({
-    Album,
-    Flash,
-    Track,
-
-    validators: {
-      isName,
-      validateArrayItems,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({Album, Flash, Track}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Artist'),
-    directory: Thing.common.directory(),
-    urls: Thing.common.urls(),
-    contextNotes: Thing.common.simpleString(),
+    name: name('Unnamed Artist'),
+    directory: directory(),
+    urls: urls(),
+    contextNotes: simpleString(),
 
-    hasAvatar: Thing.common.flag(false),
-    avatarFileExtension: Thing.common.fileExtension('jpg'),
+    hasAvatar: flag(false),
+    avatarFileExtension: fileExtension('jpg'),
 
     aliasNames: {
       flags: {update: true, expose: true},
@@ -31,30 +35,23 @@ export class Artist extends Thing {
       expose: {transform: (names) => names ?? []},
     },
 
-    isAlias: Thing.common.flag(),
-    aliasedArtistRef: Thing.common.singleReference(Artist),
+    isAlias: flag(),
+
+    aliasedArtist: singleReference({
+      class: input.value(Artist),
+      find: input.value(find.artist),
+      data: 'artistData',
+    }),
 
     // Update only
 
-    albumData: Thing.common.wikiData(Album),
-    artistData: Thing.common.wikiData(Artist),
-    flashData: Thing.common.wikiData(Flash),
-    trackData: Thing.common.wikiData(Track),
+    albumData: wikiData(Album),
+    artistData: wikiData(Artist),
+    flashData: wikiData(Flash),
+    trackData: wikiData(Track),
 
     // Expose only
 
-    aliasedArtist: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['artistData', 'aliasedArtistRef'],
-        compute: ({artistData, aliasedArtistRef}) =>
-          aliasedArtistRef && artistData
-            ? find.artist(aliasedArtistRef, artistData, {mode: 'quiet'})
-            : null,
-      },
-    },
-
     tracksAsArtist:
       Artist.filterByContrib('trackData', 'artistContribs'),
     tracksAsContributor:
@@ -66,14 +63,14 @@ export class Artist extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['trackData'],
+        dependencies: ['this', 'trackData'],
 
-        compute: ({trackData, [Artist.instance]: artist}) =>
+        compute: ({this: artist, trackData}) =>
           trackData?.filter((track) =>
             [
-              ...track.artistContribs,
-              ...track.contributorContribs,
-              ...track.coverArtistContribs,
+              ...track.artistContribs ?? [],
+              ...track.contributorContribs ?? [],
+              ...track.coverArtistContribs ?? [],
             ].some(({who}) => who === artist)) ?? [],
       },
     },
@@ -82,9 +79,9 @@ export class Artist extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['trackData'],
+        dependencies: ['this', 'trackData'],
 
-        compute: ({trackData, [Artist.instance]: artist}) =>
+        compute: ({this: artist, trackData}) =>
           trackData?.filter(({commentatorArtists}) =>
             commentatorArtists.includes(artist)) ?? [],
       },
@@ -103,18 +100,16 @@ export class Artist extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['albumData'],
+        dependencies: [this, 'albumData'],
 
-        compute: ({albumData, [Artist.instance]: artist}) =>
+        compute: ({this: artist, albumData}) =>
           albumData?.filter(({commentatorArtists}) =>
             commentatorArtists.includes(artist)) ?? [],
       },
     },
 
-    flashesAsContributor: Artist.filterByContrib(
-      'flashData',
-      'contributorContribs'
-    ),
+    flashesAsContributor:
+      Artist.filterByContrib('flashData', 'contributorContribs'),
   });
 
   static [Thing.getSerializeDescriptors] = ({
@@ -148,15 +143,15 @@ export class Artist extends Thing {
     flags: {expose: true},
 
     expose: {
-      dependencies: [thingDataProperty],
+      dependencies: ['this', thingDataProperty],
 
       compute: ({
+        this: artist,
         [thingDataProperty]: thingData,
-        [Artist.instance]: artist
       }) =>
         thingData?.filter(thing =>
           thing[contribsProperty]
-            .some(contrib => contrib.who === artist)) ?? [],
+            ?.some(contrib => contrib.who === artist)) ?? [],
     },
   });
 }
diff --git a/src/data/things/cacheable-object.js b/src/data/things/cacheable-object.js
index ea705a61..4bc3668d 100644
--- a/src/data/things/cacheable-object.js
+++ b/src/data/things/cacheable-object.js
@@ -76,28 +76,24 @@
 
 import {inspect as nodeInspect} from 'node:util';
 
-import {color, ENABLE_COLOR} from '#cli';
+import {colors, ENABLE_COLOR} from '#cli';
 
 function inspect(value) {
   return nodeInspect(value, {colors: ENABLE_COLOR});
 }
 
 export default class CacheableObject {
-  static instance = Symbol('CacheableObject `this` instance');
-
   #propertyUpdateValues = Object.create(null);
   #propertyUpdateCacheInvalidators = Object.create(null);
 
-  /*
-    // Note the constructor doesn't take an initial data source. Due to a quirk
-    // of JavaScript, private members can't be accessed before the superclass's
-    // constructor is finished processing - so if we call the overridden
-    // update() function from inside this constructor, it will error when
-    // writing to private members. Pretty bad!
-    //
-    // That means initial data must be provided by following up with update()
-    // after constructing the new instance of the Thing (sub)class.
-    */
+  // Note the constructor doesn't take an initial data source. Due to a quirk
+  // of JavaScript, private members can't be accessed before the superclass's
+  // constructor is finished processing - so if we call the overridden
+  // update() function from inside this constructor, it will error when
+  // writing to private members. Pretty bad!
+  //
+  // That means initial data must be provided by following up with update()
+  // after constructing the new instance of the Thing (sub)class.
 
   constructor() {
     this.#defineProperties();
@@ -143,7 +139,7 @@ export default class CacheableObject {
 
       const definition = {
         configurable: false,
-        enumerable: true,
+        enumerable: flags.expose,
       };
 
       if (flags.update) {
@@ -185,7 +181,7 @@ export default class CacheableObject {
           }
         } catch (error) {
           error.message = [
-            `Property ${color.green(property)}`,
+            `Property ${colors.green(property)}`,
             `(${inspect(this[property])} -> ${inspect(newValue)}):`,
             error.message
           ].join(' ');
@@ -250,20 +246,27 @@ export default class CacheableObject {
 
     let getAllDependencies;
 
-    const dependencyKeys = expose.dependencies;
-    if (dependencyKeys?.length > 0) {
-      const reflectionEntry = [this.constructor.instance, this];
-      const dependencyGetters = dependencyKeys
-        .map(key => () => [key, this.#propertyUpdateValues[key]]);
+    if (expose.dependencies?.length > 0) {
+      const dependencyKeys = expose.dependencies.slice();
+      const shouldReflect = dependencyKeys.includes('this');
+
+      getAllDependencies = () => {
+        const dependencies = Object.create(null);
+
+        for (const key of dependencyKeys) {
+          dependencies[key] = this.#propertyUpdateValues[key];
+        }
+
+        if (shouldReflect) {
+          dependencies.this = this;
+        }
 
-      getAllDependencies = () =>
-        Object.fromEntries(dependencyGetters
-          .map(f => f())
-          .concat([reflectionEntry]));
+        return dependencies;
+      };
     } else {
-      const allDependencies = {[this.constructor.instance]: this};
-      Object.freeze(allDependencies);
-      getAllDependencies = () => allDependencies;
+      const dependencies = Object.create(null);
+      Object.freeze(dependencies);
+      getAllDependencies = () => dependencies;
     }
 
     if (flags.update) {
@@ -347,4 +350,12 @@ export default class CacheableObject {
       console.log(` - ${line}`);
     }
   }
+
+  static getUpdateValue(object, key) {
+    if (!Object.hasOwn(object, key)) {
+      return undefined;
+    }
+
+    return object.#propertyUpdateValues[key] ?? null;
+  }
 }
diff --git a/src/data/things/composite.js b/src/data/things/composite.js
new file mode 100644
index 00000000..51525bc1
--- /dev/null
+++ b/src/data/things/composite.js
@@ -0,0 +1,1301 @@
+import {inspect} from 'node:util';
+
+import {colors} from '#cli';
+import {TupleMap} from '#wiki-data';
+import {a} from '#validators';
+
+import {
+  decorateErrorWithIndex,
+  empty,
+  filterProperties,
+  openAggregate,
+  stitchArrays,
+  typeAppearance,
+  unique,
+  withAggregate,
+} from '#sugar';
+
+const globalCompositeCache = {};
+
+const _valueIntoToken = shape =>
+  (value = null) =>
+    (value === null
+      ? Symbol.for(`hsmusic.composite.${shape}`)
+   : typeof value === 'string'
+      ? Symbol.for(`hsmusic.composite.${shape}:${value}`)
+      : {
+          symbol: Symbol.for(`hsmusic.composite.input`),
+          shape,
+          value,
+        });
+
+export const input = _valueIntoToken('input');
+input.symbol = Symbol.for('hsmusic.composite.input');
+
+input.value = _valueIntoToken('input.value');
+input.dependency = _valueIntoToken('input.dependency');
+
+input.myself = () => Symbol.for(`hsmusic.composite.input.myself`);
+
+input.updateValue = _valueIntoToken('input.updateValue');
+
+input.staticDependency = _valueIntoToken('input.staticDependency');
+input.staticValue = _valueIntoToken('input.staticValue');
+
+function isInputToken(token) {
+  if (token === null) {
+    return false;
+  } else if (typeof token === 'object') {
+    return token.symbol === Symbol.for('hsmusic.composite.input');
+  } else if (typeof token === 'symbol') {
+    return token.description.startsWith('hsmusic.composite.input');
+  } else {
+    return false;
+  }
+}
+
+function getInputTokenShape(token) {
+  if (!isInputToken(token)) {
+    throw new TypeError(`Expected an input token, got ${typeAppearance(token)}`);
+  }
+
+  if (typeof token === 'object') {
+    return token.shape;
+  } else {
+    return token.description.match(/hsmusic\.composite\.(input.*?)(:|$)/)[1];
+  }
+}
+
+function getInputTokenValue(token) {
+  if (!isInputToken(token)) {
+    throw new TypeError(`Expected an input token, got ${typeAppearance(token)}`);
+  }
+
+  if (typeof token === 'object') {
+    return token.value;
+  } else {
+    return token.description.match(/hsmusic\.composite\.input.*?:(.*)/)?.[1] ?? null;
+  }
+}
+
+function getStaticInputMetadata(inputOptions) {
+  const metadata = {};
+
+  for (const [name, token] of Object.entries(inputOptions)) {
+    if (typeof token === 'string') {
+      metadata[input.staticDependency(name)] = token;
+      metadata[input.staticValue(name)] = null;
+    } else if (isInputToken(token)) {
+      const tokenShape = getInputTokenShape(token);
+      const tokenValue = getInputTokenValue(token);
+
+      metadata[input.staticDependency(name)] =
+        (tokenShape === 'input.dependency'
+          ? tokenValue
+          : null);
+
+      metadata[input.staticValue(name)] =
+        (tokenShape === 'input.value'
+          ? tokenValue
+          : null);
+    } else {
+      metadata[input.staticDependency(name)] = null;
+      metadata[input.staticValue(name)] = null;
+    }
+  }
+
+  return metadata;
+}
+
+function getCompositionName(description) {
+  return (
+    (description.annotation
+      ? description.annotation
+      : `unnamed composite`));
+}
+
+function validateInputValue(value, description) {
+  const tokenValue = getInputTokenValue(description);
+
+  const {acceptsNull, defaultValue, type, validate} = tokenValue || {};
+
+  if (value === null || value === undefined) {
+    if (acceptsNull || defaultValue === null) {
+      return true;
+    } else {
+      throw new TypeError(
+        (type
+          ? `Expected ${a(type)}, got ${typeAppearance(value)}`
+          : `Expected a value, got ${typeAppearance(value)}`));
+    }
+  }
+
+  if (type) {
+    // Note: null is already handled earlier in this function, so it won't
+    // cause any trouble here.
+    const typeofValue =
+      (typeof value === 'object'
+        ? Array.isArray(value) ? 'array' : 'object'
+        : typeof value);
+
+    if (typeofValue !== type) {
+      throw new TypeError(`Expected ${a(type)}, got ${typeAppearance(value)}`);
+    }
+  }
+
+  if (validate) {
+    validate(value);
+  }
+
+  return true;
+}
+
+export function templateCompositeFrom(description) {
+  const compositionName = getCompositionName(description);
+
+  withAggregate({message: `Errors in description for ${compositionName}`}, ({map, nest, push}) => {
+    if ('steps' in description) {
+      if (Array.isArray(description.steps)) {
+        push(new TypeError(`Wrap steps array in a function`));
+      } else if (typeof description.steps !== 'function') {
+        push(new TypeError(`Expected steps to be a function (returning an array)`));
+      }
+    }
+
+    validateInputs:
+    if ('inputs' in description) {
+      if (
+        Array.isArray(description.inputs) ||
+        typeof description.inputs !== 'object'
+      ) {
+        push(new Error(`Expected inputs to be object, got ${typeAppearance(description.inputs)}`));
+        break validateInputs;
+      }
+
+      nest({message: `Errors in static input descriptions for ${compositionName}`}, ({push}) => {
+        const missingCallsToInput = [];
+        const wrongCallsToInput = [];
+
+        for (const [name, value] of Object.entries(description.inputs)) {
+          if (!isInputToken(value)) {
+            missingCallsToInput.push(name);
+            continue;
+          }
+
+          if (!['input', 'input.staticDependency', 'input.staticValue'].includes(getInputTokenShape(value))) {
+            wrongCallsToInput.push(name);
+          }
+        }
+
+        for (const name of missingCallsToInput) {
+          push(new Error(`${name}: Missing call to input()`));
+        }
+
+        for (const name of wrongCallsToInput) {
+          const shape = getInputTokenShape(description.inputs[name]);
+          push(new Error(`${name}: Expected call to input, input.staticDependency, or input.staticValue, got ${shape}`));
+        }
+      });
+    }
+
+    validateOutputs:
+    if ('outputs' in description) {
+      if (
+        !Array.isArray(description.outputs) &&
+        typeof description.outputs !== 'function'
+      ) {
+        push(new Error(`Expected outputs to be array or function, got ${typeAppearance(description.outputs)}`));
+        break validateOutputs;
+      }
+
+      if (Array.isArray(description.outputs)) {
+        map(
+          description.outputs,
+          decorateErrorWithIndex(value => {
+            if (typeof value !== 'string') {
+              throw new Error(`${value}: Expected string, got ${typeAppearance(value)}`)
+            } else if (!value.startsWith('#')) {
+              throw new Error(`${value}: Expected "#" at start`);
+            }
+          }),
+          {message: `Errors in output descriptions for ${compositionName}`});
+      }
+    }
+  });
+
+  const expectedInputNames =
+    (description.inputs
+      ? Object.keys(description.inputs)
+      : []);
+
+  const instantiate = (inputOptions = {}) => {
+    withAggregate({message: `Errors in input options passed to ${compositionName}`}, ({push}) => {
+      const providedInputNames = Object.keys(inputOptions);
+
+      const misplacedInputNames =
+        providedInputNames
+          .filter(name => !expectedInputNames.includes(name));
+
+      const missingInputNames =
+        expectedInputNames
+          .filter(name => !providedInputNames.includes(name))
+          .filter(name => {
+            const inputDescription = getInputTokenValue(description.inputs[name]);
+            if (!inputDescription) return true;
+            if ('defaultValue' in inputDescription) return false;
+            if ('defaultDependency' in inputDescription) return false;
+            return true;
+          });
+
+      const wrongTypeInputNames = [];
+
+      const expectedStaticValueInputNames = [];
+      const expectedStaticDependencyInputNames = [];
+      const expectedValueProvidingTokenInputNames = [];
+
+      const validateFailedErrors = [];
+
+      for (const [name, value] of Object.entries(inputOptions)) {
+        if (misplacedInputNames.includes(name)) {
+          continue;
+        }
+
+        if (typeof value !== 'string' && !isInputToken(value)) {
+          wrongTypeInputNames.push(name);
+          continue;
+        }
+
+        const descriptionShape = getInputTokenShape(description.inputs[name]);
+
+        const tokenShape = (isInputToken(value) ? getInputTokenShape(value) : null);
+        const tokenValue = (isInputToken(value) ? getInputTokenValue(value) : null);
+
+        switch (descriptionShape) {
+          case'input.staticValue':
+            if (tokenShape !== 'input.value') {
+              expectedStaticValueInputNames.push(name);
+              continue;
+            }
+            break;
+
+          case 'input.staticDependency':
+            if (typeof value !== 'string' && tokenShape !== 'input.dependency') {
+              expectedStaticDependencyInputNames.push(name);
+              continue;
+            }
+            break;
+
+          case 'input':
+            if (typeof value !== 'string' && ![
+              'input',
+              'input.value',
+              'input.dependency',
+              'input.myself',
+              'input.updateValue',
+            ].includes(tokenShape)) {
+              expectedValueProvidingTokenInputNames.push(name);
+              continue;
+            }
+            break;
+        }
+
+        if (tokenShape === 'input.value') {
+          try {
+            validateInputValue(tokenValue, description.inputs[name]);
+          } catch (error) {
+            error.message = `${name}: ${error.message}`;
+            validateFailedErrors.push(error);
+          }
+        }
+      }
+
+      if (!empty(misplacedInputNames)) {
+        push(new Error(`Unexpected input names: ${misplacedInputNames.join(', ')}`));
+      }
+
+      if (!empty(missingInputNames)) {
+        push(new Error(`Required these inputs: ${missingInputNames.join(', ')}`));
+      }
+
+      const inputAppearance = name =>
+        (isInputToken(inputOptions[name])
+          ? `${getInputTokenShape(inputOptions[name])}() call`
+          : `dependency name`);
+
+      for (const name of expectedStaticDependencyInputNames) {
+        const appearance = inputAppearance(name);
+        push(new Error(`${name}: Expected dependency name, got ${appearance}`));
+      }
+
+      for (const name of expectedStaticValueInputNames) {
+        const appearance = inputAppearance(name)
+        push(new Error(`${name}: Expected input.value() call, got ${appearance}`));
+      }
+
+      for (const name of expectedValueProvidingTokenInputNames) {
+        const appearance = getInputTokenShape(inputOptions[name]);
+        push(new Error(`${name}: Expected dependency name or value-providing input() call, got ${appearance}`));
+      }
+
+      for (const name of wrongTypeInputNames) {
+        const type = typeAppearance(inputOptions[name]);
+        push(new Error(`${name}: Expected dependency name or input() call, got ${type}`));
+      }
+
+      for (const error of validateFailedErrors) {
+        push(error);
+      }
+    });
+
+    const inputMetadata = getStaticInputMetadata(inputOptions);
+
+    const expectedOutputNames =
+      (Array.isArray(description.outputs)
+        ? description.outputs
+     : typeof description.outputs === 'function'
+        ? description.outputs(inputMetadata)
+            .map(name =>
+              (name.startsWith('#')
+                ? name
+                : '#' + name))
+        : []);
+
+    const ownUpdateDescription =
+      (typeof description.update === 'object'
+        ? description.update
+     : typeof description.update === 'function'
+        ? description.update(inputMetadata)
+        : null);
+
+    const outputOptions = {};
+
+    const instantiatedTemplate = {
+      symbol: templateCompositeFrom.symbol,
+
+      outputs(providedOptions) {
+        withAggregate({message: `Errors in output options passed to ${compositionName}`}, ({push}) => {
+          const misplacedOutputNames = [];
+          const wrongTypeOutputNames = [];
+
+          for (const [name, value] of Object.entries(providedOptions)) {
+            if (!expectedOutputNames.includes(name)) {
+              misplacedOutputNames.push(name);
+              continue;
+            }
+
+            if (typeof value !== 'string') {
+              wrongTypeOutputNames.push(name);
+              continue;
+            }
+          }
+
+          if (!empty(misplacedOutputNames)) {
+            push(new Error(`Unexpected output names: ${misplacedOutputNames.join(', ')}`));
+          }
+
+          for (const name of wrongTypeOutputNames) {
+            const appearance = typeAppearance(providedOptions[name]);
+            push(new Error(`${name}: Expected string, got ${appearance}`));
+          }
+        });
+
+        Object.assign(outputOptions, providedOptions);
+        return instantiatedTemplate;
+      },
+
+      toDescription() {
+        const finalDescription = {};
+
+        if ('annotation' in description) {
+          finalDescription.annotation = description.annotation;
+        }
+
+        if ('compose' in description) {
+          finalDescription.compose = description.compose;
+        }
+
+        if (ownUpdateDescription) {
+          finalDescription.update = ownUpdateDescription;
+        }
+
+        if ('inputs' in description) {
+          const inputMapping = {};
+
+          for (const [name, token] of Object.entries(description.inputs)) {
+            const tokenValue = getInputTokenValue(token);
+            if (name in inputOptions) {
+              if (typeof inputOptions[name] === 'string') {
+                inputMapping[name] = input.dependency(inputOptions[name]);
+              } else {
+                inputMapping[name] = inputOptions[name];
+              }
+            } else if (tokenValue.defaultValue) {
+              inputMapping[name] = input.value(tokenValue.defaultValue);
+            } else if (tokenValue.defaultDependency) {
+              inputMapping[name] = input.dependency(tokenValue.defaultDependency);
+            } else {
+              inputMapping[name] = input.value(null);
+            }
+          }
+
+          finalDescription.inputMapping = inputMapping;
+          finalDescription.inputDescriptions = description.inputs;
+        }
+
+        if ('outputs' in description) {
+          const finalOutputs = {};
+
+          for (const name of expectedOutputNames) {
+            if (name in outputOptions) {
+              finalOutputs[name] = outputOptions[name];
+            } else {
+              finalOutputs[name] = name;
+            }
+          }
+
+          finalDescription.outputs = finalOutputs;
+        }
+
+        if ('steps' in description) {
+          finalDescription.steps = description.steps;
+        }
+
+        return finalDescription;
+      },
+
+      toResolvedComposition() {
+        const ownDescription = instantiatedTemplate.toDescription();
+
+        const finalDescription = {...ownDescription};
+
+        const aggregate = openAggregate({message: `Errors resolving ${compositionName}`});
+
+        const steps = ownDescription.steps();
+
+        const resolvedSteps =
+          aggregate.map(
+            steps,
+            decorateErrorWithIndex(step =>
+              (step.symbol === templateCompositeFrom.symbol
+                ? compositeFrom(step.toResolvedComposition())
+                : step)),
+            {message: `Errors resolving steps`});
+
+        aggregate.close();
+
+        finalDescription.steps = resolvedSteps;
+
+        return finalDescription;
+      },
+    };
+
+    return instantiatedTemplate;
+  };
+
+  instantiate.inputs = instantiate;
+
+  return instantiate;
+}
+
+templateCompositeFrom.symbol = Symbol();
+
+export const continuationSymbol = Symbol.for('compositeFrom: continuation symbol');
+export const noTransformSymbol = Symbol.for('compositeFrom: no-transform symbol');
+
+export function compositeFrom(description) {
+  const {annotation} = description;
+  const compositionName = getCompositionName(description);
+
+  const debug = fn => {
+    if (compositeFrom.debug === true) {
+      const label =
+        (annotation
+          ? colors.dim(`[composite: ${annotation}]`)
+          : colors.dim(`[composite]`));
+      const result = fn();
+      if (Array.isArray(result)) {
+        console.log(label, ...result.map(value =>
+          (typeof value === 'object'
+            ? inspect(value, {depth: 1, colors: true, compact: true, breakLength: Infinity})
+            : value)));
+      } else {
+        console.log(label, result);
+      }
+    }
+  };
+
+  if (!Array.isArray(description.steps)) {
+    throw new TypeError(
+      `Expected steps to be array, got ${typeAppearance(description.steps)}` +
+      (annotation ? ` (${annotation})` : ''));
+  }
+
+  const composition =
+    description.steps.map(step =>
+      ('toResolvedComposition' in step
+        ? compositeFrom(step.toResolvedComposition())
+        : step));
+
+  const inputMetadata = getStaticInputMetadata(description.inputMapping ?? {});
+
+  function _mapDependenciesToOutputs(providedDependencies) {
+    if (!description.outputs) {
+      return {};
+    }
+
+    if (!providedDependencies) {
+      return {};
+    }
+
+    return (
+      Object.fromEntries(
+        Object.entries(description.outputs)
+          .map(([continuationName, outputName]) => [
+            outputName,
+            (continuationName in providedDependencies
+              ? providedDependencies[continuationName]
+              : providedDependencies[continuationName.replace(/^#/, '')]),
+          ])));
+  }
+
+  // These dependencies were all provided by the composition which this one is
+  // nested inside, so input('name')-shaped tokens are going to be evaluated
+  // in the context of the containing composition.
+  const dependenciesFromInputs =
+    Object.values(description.inputMapping ?? {})
+      .map(token => {
+        const tokenShape = getInputTokenShape(token);
+        const tokenValue = getInputTokenValue(token);
+        switch (tokenShape) {
+          case 'input.dependency':
+            return tokenValue;
+          case 'input':
+          case 'input.updateValue':
+            return token;
+          case 'input.myself':
+            return 'this';
+          default:
+            return null;
+        }
+      })
+      .filter(Boolean);
+
+  const anyInputsUseUpdateValue =
+    dependenciesFromInputs
+      .filter(dependency => isInputToken(dependency))
+      .some(token => getInputTokenShape(token) === 'input.updateValue');
+
+  const inputNames =
+    Object.keys(description.inputMapping ?? {});
+
+  const inputSymbols =
+    inputNames.map(name => input(name));
+
+  const inputsMayBeDynamicValue =
+    stitchArrays({
+      mappingToken: Object.values(description.inputMapping ?? {}),
+      descriptionToken: Object.values(description.inputDescriptions ?? {}),
+    }).map(({mappingToken, descriptionToken}) => {
+        if (getInputTokenShape(descriptionToken) === 'input.staticValue') return false;
+        if (getInputTokenShape(mappingToken) === 'input.value') return false;
+        return true;
+      });
+
+  const inputDescriptions =
+    Object.values(description.inputDescriptions ?? {});
+
+  /*
+  const inputsAcceptNull =
+    Object.values(description.inputDescriptions ?? {})
+      .map(token => {
+        const tokenValue = getInputTokenValue(token);
+        if (!tokenValue) return false;
+        if ('acceptsNull' in tokenValue) return tokenValue.acceptsNull;
+        if ('defaultValue' in tokenValue) return tokenValue.defaultValue === null;
+        return false;
+      });
+  */
+
+  // Update descriptions passed as the value in an input.updateValue() token,
+  // as provided as inputs for this composition.
+  const inputUpdateDescriptions =
+    Object.values(description.inputMapping ?? {})
+      .map(token =>
+        (getInputTokenShape(token) === 'input.updateValue'
+          ? getInputTokenValue(token)
+          : null))
+      .filter(Boolean);
+
+  const base = composition.at(-1);
+  const steps = composition.slice();
+
+  const aggregate = openAggregate({
+    message:
+      `Errors preparing composition` +
+      (annotation ? ` (${annotation})` : ''),
+  });
+
+  const compositionNests = description.compose ?? true;
+
+  // Steps default to exposing if using a shorthand syntax where flags aren't
+  // specified at all.
+  const stepsExpose =
+    steps
+      .map(step =>
+        (step.flags
+          ? step.flags.expose ?? false
+          : true));
+
+  // Steps default to composing if using a shorthand syntax where flags aren't
+  // specified at all - *and* aren't the base (final step), unless the whole
+  // composition is nestable.
+  const stepsCompose =
+    steps
+      .map((step, index, {length}) =>
+        (step.flags
+          ? step.flags.compose ?? false
+          : (index === length - 1
+              ? compositionNests
+              : true)));
+
+  // Steps update if the corresponding flag is explicitly set, if a transform
+  // function is provided, or if the dependencies include an input.updateValue
+  // token.
+  const stepsUpdate =
+    steps
+      .map(step =>
+        (step.flags
+          ? step.flags.update ?? false
+          : !!step.transform ||
+            !!step.dependencies?.some(dependency =>
+                isInputToken(dependency) &&
+                getInputTokenShape(dependency) === 'input.updateValue')));
+
+  // The expose description for a step is just the entire step object, when
+  // using the shorthand syntax where {flags: {expose: true}} is left implied.
+  const stepExposeDescriptions =
+    steps
+      .map((step, index) =>
+        (stepsExpose[index]
+          ? (step.flags
+              ? step.expose ?? null
+              : step)
+          : null));
+
+  // The update description for a step, if present at all, is always set
+  // explicitly. There may be multiple per step - namely that step's own
+  // {update} description, and any descriptions passed as the value in an
+  // input.updateValue({...}) token.
+  const stepUpdateDescriptions =
+    steps
+      .map((step, index) =>
+        (stepsUpdate[index]
+          ? [
+              step.update ?? null,
+              ...(stepExposeDescriptions[index]?.dependencies ?? [])
+                .filter(dependency => isInputToken(dependency))
+                .filter(token => getInputTokenShape(token) === 'input.updateValue')
+                .map(token => getInputTokenValue(token)),
+            ].filter(Boolean)
+          : []));
+
+  // Indicates presence of a {compute} function on the expose description.
+  const stepsCompute =
+    stepExposeDescriptions
+      .map(expose => !!expose?.compute);
+
+  // Indicates presence of a {transform} function on the expose description.
+  const stepsTransform =
+    stepExposeDescriptions
+      .map(expose => !!expose?.transform);
+
+  const dependenciesFromSteps =
+    unique(
+      stepExposeDescriptions
+        .flatMap(expose => expose?.dependencies ?? [])
+        .map(dependency => {
+          if (typeof dependency === 'string')
+            return (dependency.startsWith('#') ? null : dependency);
+
+          const tokenShape = getInputTokenShape(dependency);
+          const tokenValue = getInputTokenValue(dependency);
+          switch (tokenShape) {
+            case 'input.dependency':
+              return (tokenValue.startsWith('#') ? null : tokenValue);
+            case 'input.myself':
+              return 'this';
+            default:
+              return null;
+          }
+        })
+        .filter(Boolean));
+
+  const anyStepsUseUpdateValue =
+    stepExposeDescriptions
+      .some(expose =>
+        (expose?.dependencies
+          ? expose.dependencies.includes(input.updateValue())
+          : false));
+
+  const anyStepsExpose =
+    stepsExpose.includes(true);
+
+  const anyStepsUpdate =
+    stepsUpdate.includes(true);
+
+  const anyStepsCompute =
+    stepsCompute.includes(true);
+
+  const anyStepsTransform =
+    stepsTransform.includes(true);
+
+  const compositionExposes =
+    anyStepsExpose;
+
+  const compositionUpdates =
+    'update' in description ||
+    anyInputsUseUpdateValue ||
+    anyStepsUseUpdateValue ||
+    anyStepsUpdate;
+
+  const stepEntries = stitchArrays({
+    step: steps,
+    stepComposes: stepsCompose,
+    stepComputes: stepsCompute,
+    stepTransforms: stepsTransform,
+  });
+
+  for (let i = 0; i < stepEntries.length; i++) {
+    const {
+      step,
+      stepComposes,
+      stepComputes,
+      stepTransforms,
+    } = stepEntries[i];
+
+    const isBase = i === stepEntries.length - 1;
+    const message =
+      `Errors in step #${i + 1}` +
+      (isBase ? ` (base)` : ``) +
+      (step.annotation ? ` (${step.annotation})` : ``);
+
+    aggregate.nest({message}, ({push}) => {
+      if (isBase && stepComposes !== compositionNests) {
+        return push(new TypeError(
+          (compositionNests
+            ? `Base must compose, this composition is nestable`
+            : `Base must not compose, this composition isn't nestable`)));
+      } else if (!isBase && !stepComposes) {
+        return push(new TypeError(
+          (compositionNests
+            ? `All steps must compose`
+            : `All steps (except base) must compose`)));
+      }
+
+      if (
+        !compositionNests && !compositionUpdates &&
+        stepTransforms && !stepComputes
+      ) {
+        return push(new TypeError(
+          `Steps which only transform can't be used in a composition that doesn't update`));
+      }
+    });
+  }
+
+  if (!compositionNests && !anyStepsCompute && !anyStepsTransform) {
+    aggregate.push(new TypeError(`Expected at least one step to compute or transform`));
+  }
+
+  aggregate.close();
+
+  function _prepareContinuation(callingTransformForThisStep) {
+    const continuationStorage = {
+      returnedWith: null,
+      providedDependencies: undefined,
+      providedValue: undefined,
+    };
+
+    const continuation =
+      (callingTransformForThisStep
+        ? (providedValue, providedDependencies = null) => {
+            continuationStorage.returnedWith = 'continuation';
+            continuationStorage.providedDependencies = providedDependencies;
+            continuationStorage.providedValue = providedValue;
+            return continuationSymbol;
+          }
+        : (providedDependencies = null) => {
+            continuationStorage.returnedWith = 'continuation';
+            continuationStorage.providedDependencies = providedDependencies;
+            return continuationSymbol;
+          });
+
+    continuation.exit = (providedValue) => {
+      continuationStorage.returnedWith = 'exit';
+      continuationStorage.providedValue = providedValue;
+      return continuationSymbol;
+    };
+
+    if (compositionNests) {
+      const makeRaiseLike = returnWith =>
+        (callingTransformForThisStep
+          ? (providedValue, providedDependencies = null) => {
+              continuationStorage.returnedWith = returnWith;
+              continuationStorage.providedDependencies = providedDependencies;
+              continuationStorage.providedValue = providedValue;
+              return continuationSymbol;
+            }
+          : (providedDependencies = null) => {
+              continuationStorage.returnedWith = returnWith;
+              continuationStorage.providedDependencies = providedDependencies;
+              return continuationSymbol;
+            });
+
+      continuation.raiseOutput = makeRaiseLike('raiseOutput');
+      continuation.raiseOutputAbove = makeRaiseLike('raiseOutputAbove');
+    }
+
+    return {continuation, continuationStorage};
+  }
+
+  function _computeOrTransform(initialValue, continuationIfApplicable, initialDependencies) {
+    const expectingTransform = initialValue !== noTransformSymbol;
+
+    let valueSoFar =
+      (expectingTransform
+        ? initialValue
+        : undefined);
+
+    const availableDependencies = {...initialDependencies};
+
+    const inputValues =
+      Object.values(description.inputMapping ?? {})
+        .map(token => {
+          const tokenShape = getInputTokenShape(token);
+          const tokenValue = getInputTokenValue(token);
+          switch (tokenShape) {
+            case 'input.dependency':
+              return initialDependencies[tokenValue];
+            case 'input.value':
+              return tokenValue;
+            case 'input.updateValue':
+              if (!expectingTransform)
+                throw new Error(`Unexpected input.updateValue() accessed on non-transform call`);
+              return valueSoFar;
+            case 'input.myself':
+              return initialDependencies['this'];
+            case 'input':
+              return initialDependencies[token];
+            default:
+              throw new TypeError(`Unexpected input shape ${tokenShape}`);
+          }
+        });
+
+    withAggregate({message: `Errors in input values provided to ${compositionName}`}, ({push}) => {
+      for (const {dynamic, name, value, description} of stitchArrays({
+        dynamic: inputsMayBeDynamicValue,
+        name: inputNames,
+        value: inputValues,
+        description: inputDescriptions,
+      })) {
+        if (!dynamic) continue;
+        try {
+          validateInputValue(value, description);
+        } catch (error) {
+          error.message = `${name}: ${error.message}`;
+          push(error);
+        }
+      }
+    });
+
+    if (expectingTransform) {
+      debug(() => [colors.bright(`begin composition - transforming from:`), initialValue]);
+    } else {
+      debug(() => colors.bright(`begin composition - not transforming`));
+    }
+
+    for (let i = 0; i < steps.length; i++) {
+      const step = steps[i];
+      const isBase = i === steps.length - 1;
+
+      debug(() => [
+        `step #${i+1}` +
+        (isBase
+          ? ` (base):`
+          : ` of ${steps.length}:`),
+        step]);
+
+      const expose =
+        (step.flags
+          ? step.expose
+          : step);
+
+      if (!expose) {
+        if (!isBase) {
+          debug(() => `step #${i+1} - no expose description, nothing to do for this step`);
+          continue;
+        }
+
+        if (expectingTransform) {
+          debug(() => `step #${i+1} (base) - no expose description, returning so-far update value:`, valueSoFar);
+          if (continuationIfApplicable) {
+            debug(() => colors.bright(`end composition - raise (inferred - composing)`));
+            return continuationIfApplicable(valueSoFar);
+          } else {
+            debug(() => colors.bright(`end composition - exit (inferred - not composing)`));
+            return valueSoFar;
+          }
+        } else {
+          debug(() => `step #${i+1} (base) - no expose description, nothing to continue with`);
+          if (continuationIfApplicable) {
+            debug(() => colors.bright(`end composition - raise (inferred - composing)`));
+            return continuationIfApplicable();
+          } else {
+            debug(() => colors.bright(`end composition - exit (inferred - not composing)`));
+            return null;
+          }
+        }
+      }
+
+      const callingTransformForThisStep =
+        expectingTransform && expose.transform;
+
+      let continuationStorage;
+
+      const inputDictionary =
+        Object.fromEntries(
+          stitchArrays({symbol: inputSymbols, value: inputValues})
+            .map(({symbol, value}) => [symbol, value]));
+
+      const filterableDependencies = {
+        ...availableDependencies,
+        ...inputMetadata,
+        ...inputDictionary,
+        ...
+          (expectingTransform
+            ? {[input.updateValue()]: valueSoFar}
+            : {}),
+        [input.myself()]: initialDependencies?.['this'] ?? null,
+      };
+
+      const selectDependencies =
+        (expose.dependencies ?? []).map(dependency => {
+          if (!isInputToken(dependency)) return dependency;
+          const tokenShape = getInputTokenShape(dependency);
+          const tokenValue = getInputTokenValue(dependency);
+          switch (tokenShape) {
+            case 'input':
+            case 'input.staticDependency':
+            case 'input.staticValue':
+              return dependency;
+            case 'input.myself':
+              return input.myself();
+            case 'input.dependency':
+              return tokenValue;
+            case 'input.updateValue':
+              return input.updateValue();
+            default:
+              throw new Error(`Unexpected token ${tokenShape} as dependency`);
+          }
+        })
+
+      const filteredDependencies =
+        filterProperties(filterableDependencies, selectDependencies);
+
+      debug(() => [
+        `step #${i+1} - ${callingTransformForThisStep ? 'transform' : 'compute'}`,
+        `with dependencies:`, filteredDependencies,
+        `selecting:`, selectDependencies,
+        `from available:`, filterableDependencies,
+        ...callingTransformForThisStep ? [`from value:`, valueSoFar] : []]);
+
+      let result;
+
+      const getExpectedEvaluation = () =>
+        (callingTransformForThisStep
+          ? (filteredDependencies
+              ? ['transform', valueSoFar, continuationSymbol, filteredDependencies]
+              : ['transform', valueSoFar, continuationSymbol])
+          : (filteredDependencies
+              ? ['compute', continuationSymbol, filteredDependencies]
+              : ['compute', continuationSymbol]));
+
+      const naturalEvaluate = () => {
+        const [name, ...argsLayout] = getExpectedEvaluation();
+
+        let args;
+
+        if (isBase && !compositionNests) {
+          args =
+            argsLayout.filter(arg => arg !== continuationSymbol);
+        } else {
+          let continuation;
+
+          ({continuation, continuationStorage} =
+            _prepareContinuation(callingTransformForThisStep));
+
+          args =
+            argsLayout.map(arg =>
+              (arg === continuationSymbol
+                ? continuation
+                : arg));
+        }
+
+        return expose[name](...args);
+      }
+
+      switch (step.cache) {
+        // Warning! Highly WIP!
+        case 'aggressive': {
+          const hrnow = () => {
+            const hrTime = process.hrtime();
+            return hrTime[0] * 1000000000 + hrTime[1];
+          };
+
+          const [name, ...args] = getExpectedEvaluation();
+
+          let cache = globalCompositeCache[step.annotation];
+          if (!cache) {
+            cache = globalCompositeCache[step.annotation] = {
+              transform: new TupleMap(),
+              compute: new TupleMap(),
+              times: {
+                read: [],
+                evaluate: [],
+              },
+            };
+          }
+
+          const tuplefied = args
+            .flatMap(arg => [
+              Symbol.for('compositeFrom: tuplefied arg divider'),
+              ...(typeof arg !== 'object' || Array.isArray(arg)
+                ? [arg]
+                : Object.entries(arg).flat()),
+            ]);
+
+          const readTime = hrnow();
+          const cacheContents = cache[name].get(tuplefied);
+          cache.times.read.push(hrnow() - readTime);
+
+          if (cacheContents) {
+            ({result, continuationStorage} = cacheContents);
+          } else {
+            const evaluateTime = hrnow();
+            result = naturalEvaluate();
+            cache.times.evaluate.push(hrnow() - evaluateTime);
+            cache[name].set(tuplefied, {result, continuationStorage});
+          }
+
+          break;
+        }
+
+        default: {
+          result = naturalEvaluate();
+          break;
+        }
+      }
+
+      if (result !== continuationSymbol) {
+        debug(() => [`step #${i+1} - result: exit (inferred) ->`, result]);
+
+        if (compositionNests) {
+          throw new TypeError(`Inferred early-exit is disallowed in nested compositions`);
+        }
+
+        debug(() => colors.bright(`end composition - exit (inferred)`));
+
+        return result;
+      }
+
+      const {returnedWith} = continuationStorage;
+
+      if (returnedWith === 'exit') {
+        const {providedValue} = continuationStorage;
+
+        debug(() => [`step #${i+1} - result: exit (explicit) ->`, providedValue]);
+        debug(() => colors.bright(`end composition - exit (explicit)`));
+
+        if (compositionNests) {
+          return continuationIfApplicable.exit(providedValue);
+        } else {
+          return providedValue;
+        }
+      }
+
+      const {providedValue, providedDependencies} = continuationStorage;
+
+      const continuationArgs = [];
+      if (expectingTransform) {
+        continuationArgs.push(
+          (callingTransformForThisStep
+            ? providedValue ?? null
+            : valueSoFar ?? null));
+      }
+
+      debug(() => {
+        const base = `step #${i+1} - result: ` + returnedWith;
+        const parts = [];
+
+        if (callingTransformForThisStep) {
+          parts.push('value:', providedValue);
+        }
+
+        if (providedDependencies !== null) {
+          parts.push(`deps:`, providedDependencies);
+        } else {
+          parts.push(`(no deps)`);
+        }
+
+        if (empty(parts)) {
+          return base;
+        } else {
+          return [base + ' ->', ...parts];
+        }
+      });
+
+      switch (returnedWith) {
+        case 'raiseOutput':
+          debug(() =>
+            (isBase
+              ? colors.bright(`end composition - raiseOutput (base: explicit)`)
+              : colors.bright(`end composition - raiseOutput`)));
+          continuationArgs.push(_mapDependenciesToOutputs(providedDependencies));
+          return continuationIfApplicable(...continuationArgs);
+
+        case 'raiseOutputAbove':
+          debug(() => colors.bright(`end composition - raiseOutputAbove`));
+          continuationArgs.push(_mapDependenciesToOutputs(providedDependencies));
+          return continuationIfApplicable.raiseOutput(...continuationArgs);
+
+        case 'continuation':
+          if (isBase) {
+            debug(() => colors.bright(`end composition - raiseOutput (inferred)`));
+            continuationArgs.push(_mapDependenciesToOutputs(providedDependencies));
+            return continuationIfApplicable(...continuationArgs);
+          } else {
+            Object.assign(availableDependencies, providedDependencies);
+            if (callingTransformForThisStep && providedValue !== null) {
+              valueSoFar = providedValue;
+            }
+            break;
+          }
+      }
+    }
+  }
+
+  const constructedDescriptor = {};
+
+  if (annotation) {
+    constructedDescriptor.annotation = annotation;
+  }
+
+  constructedDescriptor.flags = {
+    update: compositionUpdates,
+    expose: compositionExposes,
+    compose: compositionNests,
+  };
+
+  if (compositionUpdates) {
+    // TODO: This is a dumb assign statement, and it could probably do more
+    // interesting things, like combining validation functions.
+    constructedDescriptor.update =
+      Object.assign(
+        {...description.update ?? {}},
+        ...inputUpdateDescriptions,
+        ...stepUpdateDescriptions.flat());
+  }
+
+  if (compositionExposes) {
+    const expose = constructedDescriptor.expose = {};
+
+    expose.dependencies =
+      unique([
+        ...dependenciesFromInputs,
+        ...dependenciesFromSteps,
+      ]);
+
+    const _wrapper = (...args) => {
+      try {
+        return _computeOrTransform(...args);
+      } catch (thrownError) {
+        const error = new Error(
+          `Error computing composition` +
+          (annotation ? ` ${annotation}` : ''));
+        error.cause = thrownError;
+        throw error;
+      }
+    };
+
+    if (compositionNests) {
+      if (compositionUpdates) {
+        expose.transform = (value, continuation, dependencies) =>
+          _wrapper(value, continuation, dependencies);
+      }
+
+      if (anyStepsCompute && !anyStepsUseUpdateValue && !anyInputsUseUpdateValue) {
+        expose.compute = (continuation, dependencies) =>
+          _wrapper(noTransformSymbol, continuation, dependencies);
+      }
+
+      if (base.cacheComposition) {
+        expose.cache = base.cacheComposition;
+      }
+    } else if (compositionUpdates) {
+      expose.transform = (value, dependencies) =>
+        _wrapper(value, null, dependencies);
+    } else {
+      expose.compute = (dependencies) =>
+        _wrapper(noTransformSymbol, null, dependencies);
+    }
+  }
+
+  return constructedDescriptor;
+}
+
+export function displayCompositeCacheAnalysis() {
+  const showTimes = (cache, key) => {
+    const times = cache.times[key].slice().sort();
+
+    const all = times;
+    const worst10pc = times.slice(-times.length / 10);
+    const best10pc = times.slice(0, times.length / 10);
+    const middle50pc = times.slice(times.length / 4, -times.length / 4);
+    const middle80pc = times.slice(times.length / 10, -times.length / 10);
+
+    const fmt = val => `${(val / 1000).toFixed(2)}ms`.padStart(9);
+    const avg = times => times.reduce((a, b) => a + b, 0) / times.length;
+
+    const left = ` - ${key}: `;
+    const indn = ' '.repeat(left.length);
+    console.log(left + `${fmt(avg(all))} (all ${all.length})`);
+    console.log(indn + `${fmt(avg(worst10pc))} (worst 10%)`);
+    console.log(indn + `${fmt(avg(best10pc))} (best 10%)`);
+    console.log(indn + `${fmt(avg(middle80pc))} (middle 80%)`);
+    console.log(indn + `${fmt(avg(middle50pc))} (middle 50%)`);
+  };
+
+  for (const [annotation, cache] of Object.entries(globalCompositeCache)) {
+    console.log(`Cached ${annotation}:`);
+    showTimes(cache, 'evaluate');
+    showTimes(cache, 'read');
+  }
+}
+
+// Evaluates a function with composite debugging enabled, turns debugging
+// off again, and returns the result of the function. This is mostly syntax
+// sugar, but also helps avoid unit tests avoid accidentally printing debug
+// info for a bunch of unrelated composites (due to property enumeration
+// when displaying an unexpected result). Use as so:
+//
+//   Without debugging:
+//     t.same(thing.someProp, value)
+//
+//   With debugging:
+//     t.same(debugComposite(() => thing.someProp), value)
+//
+export function debugComposite(fn) {
+  compositeFrom.debug = true;
+  const value = fn();
+  compositeFrom.debug = false;
+  return value;
+}
diff --git a/src/data/things/flash.js b/src/data/things/flash.js
index 6eb5234f..8fb1edfa 100644
--- a/src/data/things/flash.js
+++ b/src/data/things/flash.js
@@ -1,25 +1,35 @@
+import {input} from '#composite';
 import find from '#find';
 
+import {
+  isColor,
+  isDirectory,
+  isNumber,
+  isString,
+  oneOf,
+} from '#validators';
+
+import {
+  color,
+  contributionList,
+  fileExtension,
+  name,
+  referenceList,
+  simpleDate,
+  simpleString,
+  urls,
+  wikiData,
+} from '#composite/wiki-properties';
+
 import Thing from './thing.js';
 
 export class Flash extends Thing {
   static [Thing.referenceType] = 'flash';
 
-  static [Thing.getPropertyDescriptors] = ({
-    Artist,
-    Track,
-    FlashAct,
-
-    validators: {
-      isDirectory,
-      isNumber,
-      isString,
-      oneOf,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({Artist, Track, FlashAct}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Flash'),
+    name: name('Unnamed Flash'),
 
     directory: {
       flags: {update: true, expose: true},
@@ -47,39 +57,35 @@ export class Flash extends Thing {
       },
     },
 
-    date: Thing.common.simpleDate(),
+    date: simpleDate(),
 
-    coverArtFileExtension: Thing.common.fileExtension('jpg'),
+    coverArtFileExtension: fileExtension('jpg'),
 
-    contributorContribsByRef: Thing.common.contribsByRef(),
+    contributorContribs: contributionList(),
 
-    featuredTracksByRef: Thing.common.referenceList(Track),
+    featuredTracks: referenceList({
+      class: input.value(Track),
+      find: input.value(find.track),
+      data: 'trackData',
+    }),
 
-    urls: Thing.common.urls(),
+    urls: urls(),
 
     // Update only
 
-    artistData: Thing.common.wikiData(Artist),
-    trackData: Thing.common.wikiData(Track),
-    flashActData: Thing.common.wikiData(FlashAct),
+    artistData: wikiData(Artist),
+    trackData: wikiData(Track),
+    flashActData: wikiData(FlashAct),
 
     // Expose only
 
-    contributorContribs: Thing.common.dynamicContribs('contributorContribsByRef'),
-
-    featuredTracks: Thing.common.dynamicThingsFromReferenceList(
-      'featuredTracksByRef',
-      'trackData',
-      find.track
-    ),
-
     act: {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['flashActData'],
+        dependencies: ['this', 'flashActData'],
 
-        compute: ({flashActData, [Flash.instance]: flash}) =>
+        compute: ({this: flash, flashActData}) =>
           flashActData.find((act) => act.flashes.includes(flash)) ?? null,
       },
     },
@@ -88,9 +94,9 @@ export class Flash extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['flashActData'],
+        dependencies: ['this', 'flashActData'],
 
-        compute: ({flashActData, [Flash.instance]: flash}) =>
+        compute: ({this: flash, flashActData}) =>
           flashActData.find((act) => act.flashes.includes(flash))?.color ?? null,
       },
     },
@@ -111,17 +117,13 @@ export class Flash extends Thing {
 }
 
 export class FlashAct extends Thing {
-  static [Thing.getPropertyDescriptors] = ({
-    validators: {
-      isColor,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Flash Act'),
-    color: Thing.common.color(),
-    anchor: Thing.common.simpleString(),
-    jump: Thing.common.simpleString(),
+    name: name('Unnamed Flash Act'),
+    color: color(),
+    anchor: simpleString(),
+    jump: simpleString(),
 
     jumpColor: {
       flags: {update: true, expose: true},
@@ -133,18 +135,14 @@ export class FlashAct extends Thing {
       }
     },
 
-    flashesByRef: Thing.common.referenceList(Flash),
+    flashes: referenceList({
+      class: input.value(Flash),
+      find: input.value(find.flash),
+      data: 'flashData',
+    }),
 
     // Update only
 
-    flashData: Thing.common.wikiData(Flash),
-
-    // Expose only
-
-    flashes: Thing.common.dynamicThingsFromReferenceList(
-      'flashesByRef',
-      'flashData',
-      find.flash
-    ),
+    flashData: wikiData(Flash),
   })
 }
diff --git a/src/data/things/group.js b/src/data/things/group.js
index ba339b3e..d5ae03e7 100644
--- a/src/data/things/group.js
+++ b/src/data/things/group.js
@@ -1,33 +1,44 @@
+import {input} from '#composite';
 import find from '#find';
 
+import {
+  color,
+  directory,
+  name,
+  referenceList,
+  simpleString,
+  urls,
+  wikiData,
+} from '#composite/wiki-properties';
+
 import Thing from './thing.js';
 
 export class Group extends Thing {
   static [Thing.referenceType] = 'group';
 
-  static [Thing.getPropertyDescriptors] = ({
-    Album,
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({Album}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Group'),
-    directory: Thing.common.directory(),
+    name: name('Unnamed Group'),
+    directory: directory(),
 
-    description: Thing.common.simpleString(),
+    description: simpleString(),
 
-    urls: Thing.common.urls(),
+    urls: urls(),
 
-    featuredAlbumsByRef: Thing.common.referenceList(Album),
+    featuredAlbums: referenceList({
+      class: input.value(Album),
+      find: input.value(find.album),
+      data: 'albumData',
+    }),
 
     // Update only
 
-    albumData: Thing.common.wikiData(Album),
-    groupCategoryData: Thing.common.wikiData(GroupCategory),
+    albumData: wikiData(Album),
+    groupCategoryData: wikiData(GroupCategory),
 
     // Expose only
 
-    featuredAlbums: Thing.common.dynamicThingsFromReferenceList('featuredAlbumsByRef', 'albumData', find.album),
-
     descriptionShort: {
       flags: {expose: true},
 
@@ -41,8 +52,8 @@ export class Group extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['albumData'],
-        compute: ({albumData, [Group.instance]: group}) =>
+        dependencies: ['this', 'albumData'],
+        compute: ({this: group, albumData}) =>
           albumData?.filter((album) => album.groups.includes(group)) ?? [],
       },
     },
@@ -51,9 +62,8 @@ export class Group extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['groupCategoryData'],
-
-        compute: ({groupCategoryData, [Group.instance]: group}) =>
+        dependencies: ['this', 'groupCategoryData'],
+        compute: ({this: group, groupCategoryData}) =>
           groupCategoryData.find((category) => category.groups.includes(group))
             ?.color,
       },
@@ -63,8 +73,8 @@ export class Group extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['groupCategoryData'],
-        compute: ({groupCategoryData, [Group.instance]: group}) =>
+        dependencies: ['this', 'groupCategoryData'],
+        compute: ({this: group, groupCategoryData}) =>
           groupCategoryData.find((category) => category.groups.includes(group)) ??
           null,
       },
@@ -73,26 +83,20 @@ export class Group extends Thing {
 }
 
 export class GroupCategory extends Thing {
-  static [Thing.getPropertyDescriptors] = ({
-    Group,
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({Group}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Group Category'),
-    color: Thing.common.color(),
+    name: name('Unnamed Group Category'),
+    color: color(),
 
-    groupsByRef: Thing.common.referenceList(Group),
+    groups: referenceList({
+      class: input.value(Group),
+      find: input.value(find.group),
+      data: 'groupData',
+    }),
 
     // Update only
 
-    groupData: Thing.common.wikiData(Group),
-
-    // Expose only
-
-    groups: Thing.common.dynamicThingsFromReferenceList(
-      'groupsByRef',
-      'groupData',
-      find.group
-    ),
+    groupData: wikiData(Group),
   });
 }
diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js
index ec9e9556..de9d0e50 100644
--- a/src/data/things/homepage-layout.js
+++ b/src/data/things/homepage-layout.js
@@ -1,20 +1,35 @@
+import {input} from '#composite';
 import find from '#find';
 
+import {
+  is,
+  isCountingNumber,
+  isString,
+  isStringNonEmpty,
+  oneOf,
+  validateArrayItems,
+  validateInstanceOf,
+  validateReference,
+} from '#validators';
+
+import {exposeDependency} from '#composite/control-flow';
+import {withResolvedReference} from '#composite/wiki-data';
+
+import {
+  color,
+  name,
+  referenceList,
+  simpleString,
+  wikiData,
+} from '#composite/wiki-properties';
+
 import Thing from './thing.js';
 
 export class HomepageLayout extends Thing {
-  static [Thing.getPropertyDescriptors] = ({
-    HomepageLayoutRow,
-
-    validators: {
-      isStringNonEmpty,
-      validateArrayItems,
-      validateInstanceOf,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({HomepageLayoutRow}) => ({
     // Update & expose
 
-    sidebarContent: Thing.common.simpleString(),
+    sidebarContent: simpleString(),
 
     navbarLinks: {
       flags: {update: true, expose: true},
@@ -32,13 +47,10 @@ export class HomepageLayout extends Thing {
 }
 
 export class HomepageLayoutRow extends Thing {
-  static [Thing.getPropertyDescriptors] = ({
-    Album,
-    Group,
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({Album, Group}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Homepage Row'),
+    name: name('Unnamed Homepage Row'),
 
     type: {
       flags: {update: true, expose: true},
@@ -50,30 +62,20 @@ export class HomepageLayoutRow extends Thing {
       },
     },
 
-    color: Thing.common.color(),
+    color: color(),
 
     // Update only
 
     // These aren't necessarily used by every HomepageLayoutRow subclass, but
     // for convenience of providing this data, every row accepts all wiki data
     // arrays depended upon by any subclass's behavior.
-    albumData: Thing.common.wikiData(Album),
-    groupData: Thing.common.wikiData(Group),
+    albumData: wikiData(Album),
+    groupData: wikiData(Group),
   });
 }
 
 export class HomepageLayoutAlbumsRow extends HomepageLayoutRow {
-  static [Thing.getPropertyDescriptors] = (opts, {
-    Album,
-    Group,
-
-    validators: {
-      is,
-      isCountingNumber,
-      isString,
-      validateArrayItems,
-    },
-  } = opts) => ({
+  static [Thing.getPropertyDescriptors] = (opts, {Album, Group} = opts) => ({
     ...HomepageLayoutRow[Thing.getPropertyDescriptors](opts),
 
     // Update & expose
@@ -104,8 +106,39 @@ export class HomepageLayoutAlbumsRow extends HomepageLayoutRow {
       },
     },
 
-    sourceGroupByRef: Thing.common.singleReference(Group),
-    sourceAlbumsByRef: Thing.common.referenceList(Album),
+    sourceGroup: [
+      {
+        flags: {expose: true, update: true, compose: true},
+
+        update: {
+          validate:
+            oneOf(
+              is('new-releases', 'new-additions'),
+              validateReference(Group[Thing.referenceType])),
+        },
+
+        expose: {
+          transform: (value, continuation) =>
+            (value === 'new-releases' || value === 'new-additions'
+              ? value
+              : continuation(value)),
+        },
+      },
+
+      withResolvedReference({
+        ref: input.updateValue(),
+        data: 'groupData',
+        find: input.value(find.group),
+      }),
+
+      exposeDependency({dependency: '#resolvedReference'}),
+    ],
+
+    sourceAlbums: referenceList({
+      class: input.value(Album),
+      find: input.value(find.album),
+      data: 'albumData',
+    }),
 
     countAlbumsFromGroup: {
       flags: {update: true, expose: true},
@@ -116,19 +149,5 @@ export class HomepageLayoutAlbumsRow extends HomepageLayoutRow {
       flags: {update: true, expose: true},
       update: {validate: validateArrayItems(isString)},
     },
-
-    // Expose only
-
-    sourceGroup: Thing.common.dynamicThingFromSingleReference(
-      'sourceGroupByRef',
-      'groupData',
-      find.group
-    ),
-
-    sourceAlbums: Thing.common.dynamicThingsFromReferenceList(
-      'sourceAlbumsByRef',
-      'albumData',
-      find.album
-    ),
   });
 }
diff --git a/src/data/things/index.js b/src/data/things/index.js
index 591cdc3b..77e5fa76 100644
--- a/src/data/things/index.js
+++ b/src/data/things/index.js
@@ -2,9 +2,9 @@ import * as path from 'node:path';
 import {fileURLToPath} from 'node:url';
 
 import {logError} from '#cli';
+import {compositeFrom} from '#composite';
 import * as serialize from '#serialize';
 import {openAggregate, showAggregate} from '#sugar';
-import * as validators from '#validators';
 
 import Thing from './thing.js';
 
@@ -82,6 +82,8 @@ function errorDuplicateClassNames() {
 function flattenClassLists() {
   for (const classes of Object.values(allClassLists)) {
     for (const [name, constructor] of Object.entries(classes)) {
+      if (typeof constructor !== 'function') continue;
+      if (!(constructor.prototype instanceof Thing)) continue;
       allClasses[name] = constructor;
     }
   }
@@ -119,7 +121,7 @@ function descriptorAggregateHelper({
 }
 
 function evaluatePropertyDescriptors() {
-  const opts = {...allClasses, validators};
+  const opts = {...allClasses};
 
   return descriptorAggregateHelper({
     message: `Errors evaluating Thing class property descriptors`,
@@ -129,8 +131,21 @@ function evaluatePropertyDescriptors() {
         throw new Error(`Missing [Thing.getPropertyDescriptors] function`);
       }
 
-      constructor.propertyDescriptors =
-        constructor[Thing.getPropertyDescriptors](opts);
+      const results = constructor[Thing.getPropertyDescriptors](opts);
+
+      for (const [key, value] of Object.entries(results)) {
+        if (Array.isArray(value)) {
+          results[key] = compositeFrom({
+            annotation: `${constructor.name}.${key}`,
+            compose: false,
+            steps: value,
+          });
+        } else if (value.toResolvedComposition) {
+          results[key] = compositeFrom(value.toResolvedComposition());
+        }
+      }
+
+      constructor.propertyDescriptors = results;
     },
 
     showFailedClasses(failedClasses) {
diff --git a/src/data/things/language.js b/src/data/things/language.js
index afa9f1ee..fe74f7bf 100644
--- a/src/data/things/language.js
+++ b/src/data/things/language.js
@@ -1,16 +1,17 @@
-import Thing from './thing.js';
-
 import {Tag} from '#html';
 import {isLanguageCode} from '#validators';
 
+import {
+  externalFunction,
+  flag,
+  simpleString,
+} from '#composite/wiki-properties';
+
 import CacheableObject from './cacheable-object.js';
+import Thing from './thing.js';
 
 export class Language extends Thing {
-  static [Thing.getPropertyDescriptors] = ({
-    validators: {
-      isLanguageCode,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
 
     // General language code. This is used to identify the language distinctly
@@ -23,7 +24,7 @@ export class Language extends Thing {
 
     // Human-readable name. This should be the language's own native name, not
     // localized to any other language.
-    name: Thing.common.simpleString(),
+    name: simpleString(),
 
     // Language code specific to JavaScript's Internationalization (Intl) API.
     // Usually this will be the same as the language's general code, but it
@@ -45,7 +46,7 @@ export class Language extends Thing {
     // with languages that are currently in development and not ready for
     // formal release, or which are just kept hidden as "experimental zones"
     // for wiki development or content testing.
-    hidden: Thing.common.flag(false),
+    hidden: flag(false),
 
     // Mapping of translation keys to values (strings). Generally, don't
     // access this object directly - use methods instead.
@@ -73,7 +74,7 @@ export class Language extends Thing {
 
     // Update only
 
-    escapeHTML: Thing.common.externalFunction({expose: true}),
+    escapeHTML: externalFunction(),
 
     // Expose only
 
@@ -192,7 +193,7 @@ export class Language extends Thing {
   // html.Tag objects, which are treated as sanitized by default (so that they
   // can be nested inside strings at all).
   #sanitizeStringArg(arg) {
-    const escapeHTML = this.escapeHTML;
+    const escapeHTML = CacheableObject.getUpdateValue(this, 'escapeHTML');
 
     if (!escapeHTML) {
       throw new Error(`escapeHTML unavailable`);
@@ -224,7 +225,7 @@ export class Language extends Thing {
   // contents of a slot directly, it should be manually sanitized with this
   // function first.
   sanitize(arg) {
-    const escapeHTML = this.escapeHTML;
+    const escapeHTML = CacheableObject.getUpdateValue(this, 'escapeHTML');
 
     if (!escapeHTML) {
       throw new Error(`escapeHTML unavailable`);
diff --git a/src/data/things/news-entry.js b/src/data/things/news-entry.js
index 43911410..ba065c25 100644
--- a/src/data/things/news-entry.js
+++ b/src/data/things/news-entry.js
@@ -1,3 +1,10 @@
+import {
+  directory,
+  name,
+  simpleDate,
+  simpleString,
+} from '#composite/wiki-properties';
+
 import Thing from './thing.js';
 
 export class NewsEntry extends Thing {
@@ -6,11 +13,11 @@ export class NewsEntry extends Thing {
   static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed News Entry'),
-    directory: Thing.common.directory(),
-    date: Thing.common.simpleDate(),
+    name: name('Unnamed News Entry'),
+    directory: directory(),
+    date: simpleDate(),
 
-    content: Thing.common.simpleString(),
+    content: simpleString(),
 
     // Expose only
 
diff --git a/src/data/things/static-page.js b/src/data/things/static-page.js
index 3d8d474c..f03e4405 100644
--- a/src/data/things/static-page.js
+++ b/src/data/things/static-page.js
@@ -1,16 +1,20 @@
+import {isName} from '#validators';
+
+import {
+  directory,
+  name,
+  simpleString,
+} from '#composite/wiki-properties';
+
 import Thing from './thing.js';
 
 export class StaticPage extends Thing {
   static [Thing.referenceType] = 'static';
 
-  static [Thing.getPropertyDescriptors] = ({
-    validators: {
-      isName,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = () => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Static Page'),
+    name: name('Unnamed Static Page'),
 
     nameShort: {
       flags: {update: true, expose: true},
@@ -22,8 +26,8 @@ export class StaticPage extends Thing {
       },
     },
 
-    directory: Thing.common.directory(),
-    content: Thing.common.simpleString(),
-    stylesheet: Thing.common.simpleString(),
+    directory: directory(),
+    content: simpleString(),
+    stylesheet: simpleString(),
   });
 }
diff --git a/src/data/things/thing.js b/src/data/things/thing.js
index 5705ee7e..a47f8506 100644
--- a/src/data/things/thing.js
+++ b/src/data/things/thing.js
@@ -1,399 +1,18 @@
-// Thing: base class for wiki data types, providing wiki-specific utility
-// functions on top of essential CacheableObject behavior.
+// Thing: base class for wiki data types, providing interfaces generally useful
+// to all wiki data objects on top of foundational CacheableObject behavior.
 
 import {inspect} from 'node:util';
 
-import {color} from '#cli';
-import find from '#find';
-import {empty} from '#sugar';
-import {getKebabCase} from '#wiki-data';
-
-import {
-  isAdditionalFileList,
-  isBoolean,
-  isCommentary,
-  isColor,
-  isContributionList,
-  isDate,
-  isDirectory,
-  isFileExtension,
-  isName,
-  isString,
-  isURL,
-  validateArrayItems,
-  validateInstanceOf,
-  validateReference,
-  validateReferenceList,
-} from '#validators';
+import {colors} from '#cli';
 
 import CacheableObject from './cacheable-object.js';
 
 export default class Thing extends CacheableObject {
-  static referenceType = Symbol('Thing.referenceType');
+  static referenceType = Symbol.for('Thing.referenceType');
 
   static getPropertyDescriptors = Symbol('Thing.getPropertyDescriptors');
   static getSerializeDescriptors = Symbol('Thing.getSerializeDescriptors');
 
-  // Regularly reused property descriptors, for ease of access and generally
-  // duplicating less code across wiki data types. These are specialized utility
-  // functions, so check each for how its own arguments behave!
-  static common = {
-    name: (defaultName) => ({
-      flags: {update: true, expose: true},
-      update: {validate: isName, default: defaultName},
-    }),
-
-    color: () => ({
-      flags: {update: true, expose: true},
-      update: {validate: isColor},
-    }),
-
-    directory: () => ({
-      flags: {update: true, expose: true},
-      update: {validate: isDirectory},
-      expose: {
-        dependencies: ['name'],
-        transform(directory, {name}) {
-          if (directory === null && name === null) return null;
-          else if (directory === null) return getKebabCase(name);
-          else return directory;
-        },
-      },
-    }),
-
-    urls: () => ({
-      flags: {update: true, expose: true},
-      update: {validate: validateArrayItems(isURL)},
-      expose: {transform: (value) => value ?? []},
-    }),
-
-    // A file extension! Or the default, if provided when calling this.
-    fileExtension: (defaultFileExtension = null) => ({
-      flags: {update: true, expose: true},
-      update: {validate: isFileExtension},
-      expose: {transform: (value) => value ?? defaultFileExtension},
-    }),
-
-    // Straightforward flag descriptor for a variety of property purposes.
-    // Provide a default value, true or false!
-    flag: (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},
-      };
-    },
-
-    // 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.
-    simpleDate: () => ({
-      flags: {update: true, expose: true},
-      update: {validate: isDate},
-    }),
-
-    // 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.
-    simpleString: () => ({
-      flags: {update: true, expose: true},
-      update: {validate: isString},
-    }),
-
-    // External function. These should only be used as dependencies for other
-    // properties, so they're left unexposed.
-    externalFunction: ({expose = false} = {}) => ({
-      flags: {update: true, expose},
-      update: {validate: (t) => typeof t === 'function'},
-    }),
-
-    // Super simple "contributions by reference" list, used for a variety of
-    // properties (Artists, Cover Artists, etc). This is the property which is
-    // externally provided, in the form:
-    //
-    //     [
-    //         {who: 'Artist Name', what: 'Viola'},
-    //         {who: 'artist:john-cena', what: null},
-    //         ...
-    //     ]
-    //
-    // ...processed from YAML, spreadsheet, or any other kind of input.
-    contribsByRef: () => ({
-      flags: {update: true, expose: true},
-      update: {validate: isContributionList},
-    }),
-
-    // Artist commentary! Generally present on tracks and albums.
-    commentary: () => ({
-      flags: {update: true, expose: true},
-      update: {validate: isCommentary},
-    }),
-
-    // 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: [...]},
-    //         ...
-    //     ]
-    //
-    additionalFiles: () => ({
-      flags: {update: true, expose: true},
-      update: {validate: isAdditionalFileList},
-      expose: {
-        transform: (additionalFiles) =>
-          additionalFiles ?? [],
-      },
-    }),
-
-    // A reference list! Keep in mind this is for general references to wiki
-    // objects of (usually) other Thing subclasses, not specifically leitmotif
-    // references in tracks (although that property uses referenceList too!).
-    //
-    // The underlying function validateReferenceList expects a string like
-    // 'artist' or 'track', but this utility keeps from having to hard-code the
-    // string in multiple places by referencing the value saved on the class
-    // instead.
-    referenceList: (thingClass) => {
-      const {[Thing.referenceType]: referenceType} = thingClass;
-      if (!referenceType) {
-        throw new Error(`The passed constructor ${thingClass.name} doesn't define Thing.referenceType!`);
-      }
-
-      return {
-        flags: {update: true, expose: true},
-        update: {validate: validateReferenceList(referenceType)},
-      };
-    },
-
-    // Corresponding function for a single reference.
-    singleReference: (thingClass) => {
-      const {[Thing.referenceType]: referenceType} = thingClass;
-      if (!referenceType) {
-        throw new Error(`The passed constructor ${thingClass.name} doesn't define Thing.referenceType!`);
-      }
-
-      return {
-        flags: {update: true, expose: true},
-        update: {validate: validateReference(referenceType)},
-      };
-    },
-
-    // Corresponding dynamic property to referenceList, which takes the values
-    // in the provided property and searches the specified wiki data for
-    // matching actual Thing-subclass objects.
-    dynamicThingsFromReferenceList: (
-      referenceListProperty,
-      thingDataProperty,
-      findFn
-    ) => ({
-      flags: {expose: true},
-
-      expose: {
-        dependencies: [referenceListProperty, thingDataProperty],
-        compute: ({
-          [referenceListProperty]: refs,
-          [thingDataProperty]: thingData,
-        }) =>
-          refs && thingData
-            ? refs
-                .map((ref) => findFn(ref, thingData, {mode: 'quiet'}))
-                .filter(Boolean)
-            : [],
-      },
-    }),
-
-    // Corresponding function for a single reference.
-    dynamicThingFromSingleReference: (
-      singleReferenceProperty,
-      thingDataProperty,
-      findFn
-    ) => ({
-      flags: {expose: true},
-
-      expose: {
-        dependencies: [singleReferenceProperty, thingDataProperty],
-        compute: ({
-          [singleReferenceProperty]: ref,
-          [thingDataProperty]: thingData,
-        }) => (ref && thingData ? findFn(ref, thingData, {mode: 'quiet'}) : null),
-      },
-    }),
-
-    // Corresponding dynamic property to contribsByRef, which takes the values
-    // in the provided property and searches the object's artistData for
-    // matching actual Artist objects. The computed structure has the same form
-    // as contribsByRef, but with Artist objects instead of string references:
-    //
-    //     [
-    //         {who: (an Artist), what: 'Viola'},
-    //         {who: (an Artist), what: null},
-    //         ...
-    //     ]
-    //
-    // Contributions whose "who" values don't match anything in artistData are
-    // filtered out. (So if the list is all empty, chances are that either the
-    // reference list is somehow messed up, or artistData isn't being provided
-    // properly.)
-    dynamicContribs: (contribsByRefProperty) => ({
-      flags: {expose: true},
-      expose: {
-        dependencies: ['artistData', contribsByRefProperty],
-        compute: ({artistData, [contribsByRefProperty]: contribsByRef}) =>
-          contribsByRef && artistData
-            ? contribsByRef
-                .map(({who: ref, what}) => ({
-                  who: find.artist(ref, artistData),
-                  what,
-                }))
-                .filter(({who}) => who)
-            : [],
-      },
-    }),
-
-    // Dynamically inherit a contribution list from some other object, if it
-    // hasn't been overridden on this object. This is handy for solo albums
-    // where all tracks have the same artist, for example.
-    dynamicInheritContribs: (
-      // If this property is explicitly false, the contribution list returned
-      // will always be empty.
-      nullerProperty,
-
-      // Property holding contributions on the current object.
-      contribsByRefProperty,
-
-      // Property holding corresponding "default" contributions on the parent
-      // object, which will fallen back to if the object doesn't have its own
-      // contribs.
-      parentContribsByRefProperty,
-
-      // Data array to search in and "find" function to locate parent object
-      // (which will be passed the child object and the wiki data array).
-      thingDataProperty,
-      findFn
-    ) => ({
-      flags: {expose: true},
-      expose: {
-        dependencies: [
-          contribsByRefProperty,
-          thingDataProperty,
-          nullerProperty,
-          'artistData',
-        ].filter(Boolean),
-
-        compute({
-          [Thing.instance]: thing,
-          [nullerProperty]: nuller,
-          [contribsByRefProperty]: contribsByRef,
-          [thingDataProperty]: thingData,
-          artistData,
-        }) {
-          if (!artistData) return [];
-          if (nuller === false) return [];
-          const refs =
-            contribsByRef ??
-            findFn(thing, thingData, {mode: 'quiet'})?.[parentContribsByRefProperty];
-          if (!refs) return [];
-          return refs
-            .map(({who: ref, what}) => ({
-              who: find.artist(ref, artistData),
-              what,
-            }))
-            .filter(({who}) => who);
-        },
-      },
-    }),
-
-    // Nice 'n simple shorthand for an exposed-only flag which is true when any
-    // contributions are present in the specified property.
-    contribsPresent: (contribsByRefProperty) => ({
-      flags: {expose: true},
-      expose: {
-        dependencies: [contribsByRefProperty],
-        compute({
-          [contribsByRefProperty]: contribsByRef,
-        }) {
-          return !empty(contribsByRef);
-        },
-      }
-    }),
-
-    // 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. Naturally, the passed ref list property is of the things in the
-    // wiki data provided, not the requesting Thing itself.
-    reverseReferenceList: (thingDataProperty, referencerRefListProperty) => ({
-      flags: {expose: true},
-
-      expose: {
-        dependencies: [thingDataProperty],
-
-        compute: ({[thingDataProperty]: thingData, [Thing.instance]: thing}) =>
-          thingData?.filter(t => t[referencerRefListProperty].includes(thing)) ?? [],
-      },
-    }),
-
-    // Corresponding function for single references. Note that the return value
-    // is still a list - this is for matching all the objects whose single
-    // reference (in the given property) matches this Thing.
-    reverseSingleReference: (thingDataProperty, referencerRefListProperty) => ({
-      flags: {expose: true},
-
-      expose: {
-        dependencies: [thingDataProperty],
-
-        compute: ({[thingDataProperty]: thingData, [Thing.instance]: thing}) =>
-          thingData?.filter((t) => t[referencerRefListProperty] === thing) ?? [],
-      },
-    }),
-
-    // General purpose wiki data constructor, for properties like artistData,
-    // trackData, etc.
-    wikiData: (thingClass) => ({
-      flags: {update: true},
-      update: {
-        validate: validateArrayItems(validateInstanceOf(thingClass)),
-      },
-    }),
-
-    // This one's kinda tricky: it parses artist "references" from the
-    // commentary content, and finds the matching artist for each reference.
-    // This is mostly useful for credits and listings on artist pages.
-    commentatorArtists: () => ({
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['artistData', 'commentary'],
-
-        compute: ({artistData, commentary}) =>
-          artistData && commentary
-            ? Array.from(
-                new Set(
-                  Array.from(
-                    commentary
-                      .replace(/<\/?b>/g, '')
-                      .matchAll(/<i>(?<who>.*?):<\/i>/g)
-                  ).map(({groups: {who}}) =>
-                    find.artist(who, artistData, {mode: 'quiet'})
-                  )
-                )
-              )
-            : [],
-      },
-    }),
-  };
-
   // Default custom inspect function, which may be overridden by Thing
   // subclasses. This will be used when displaying aggregate errors and other
   // command-line logging - it's the place to provide information useful in
@@ -402,8 +21,8 @@ export default class Thing extends CacheableObject {
     const cname = this.constructor.name;
 
     return (
-      (this.name ? `${cname} ${color.green(`"${this.name}"`)}` : `${cname}`) +
-      (this.directory ? ` (${color.blue(Thing.getReference(this))})` : '')
+      (this.name ? `${cname} ${colors.green(`"${this.name}"`)}` : `${cname}`) +
+      (this.directory ? ` (${colors.blue(Thing.getReference(this))})` : '')
     );
   }
 
diff --git a/src/data/things/track.js b/src/data/things/track.js
index 14510d96..193ad891 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -1,495 +1,322 @@
 import {inspect} from 'node:util';
 
-import {color} from '#cli';
+import {colors} from '#cli';
+import {input} from '#composite';
 import find from '#find';
-import {empty} from '#sugar';
 
+import {
+  isColor,
+  isContributionList,
+  isDate,
+  isFileExtension,
+} from '#validators';
+
+import {withPropertyFromObject} from '#composite/data';
+import {withResolvedContribs} from '#composite/wiki-data';
+
+import {
+  exitWithoutDependency,
+  exposeConstant,
+  exposeDependency,
+  exposeDependencyOrContinue,
+  exposeUpdateValueOrContinue,
+} from '#composite/control-flow';
+
+import {
+  additionalFiles,
+  commentary,
+  commentatorArtists,
+  contributionList,
+  directory,
+  duration,
+  flag,
+  name,
+  referenceList,
+  reverseReferenceList,
+  simpleDate,
+  singleReference,
+  simpleString,
+  urls,
+  wikiData,
+} from '#composite/wiki-properties';
+
+import {
+  exitWithoutUniqueCoverArt,
+  inheritFromOriginalRelease,
+  trackReverseReferenceList,
+  withAlbum,
+  withAlwaysReferenceByDirectory,
+  withContainingTrackSection,
+  withHasUniqueCoverArt,
+  withOtherReleases,
+  withPropertyFromAlbum,
+} from '#composite/things/track';
+
+import CacheableObject from './cacheable-object.js';
 import Thing from './thing.js';
 
 export class Track extends Thing {
   static [Thing.referenceType] = 'track';
 
-  static [Thing.getPropertyDescriptors] = ({
-    Album,
-    ArtTag,
-    Artist,
-    Flash,
-
-    validators: {
-      isBoolean,
-      isColor,
-      isDate,
-      isDuration,
-      isFileExtension,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({Album, ArtTag, Artist, Flash}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Track'),
-    directory: Thing.common.directory(),
-
-    duration: {
-      flags: {update: true, expose: true},
-      update: {validate: isDuration},
-    },
-
-    urls: Thing.common.urls(),
-    dateFirstReleased: Thing.common.simpleDate(),
-
-    // 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.
-    alwaysReferenceByDirectory: {
-      flags: {update: true, expose: true},
-
-      // Deliberately defaults to null - this will fall back to false in most
-      // cases.
-      update: {validate: isBoolean, default: null},
-
-      expose: {
-        dependencies: ['name', 'originalReleaseTrackByRef', 'trackData'],
-
-        transform(value, {
-          name,
-          originalReleaseTrackByRef,
-          trackData,
-          [Track.instance]: thisTrack,
-        }) {
-          if (value !== null) return value;
-
-          const original =
-            find.track(
-              originalReleaseTrackByRef,
-              trackData.filter(track => track !== thisTrack),
-              {quiet: true});
-
-          if (!original) return false;
-
-          return name === original.name;
-        }
-      },
-    },
-
-    artistContribsByRef: Thing.common.contribsByRef(),
-    contributorContribsByRef: Thing.common.contribsByRef(),
-    coverArtistContribsByRef: Thing.common.contribsByRef(),
-
-    referencedTracksByRef: Thing.common.referenceList(Track),
-    sampledTracksByRef: Thing.common.referenceList(Track),
-    artTagsByRef: Thing.common.referenceList(ArtTag),
-
-    hasCoverArt: {
-      flags: {update: true, expose: true},
-
-      update: {
-        validate(value) {
-          if (value !== false) {
-            throw new TypeError(`Expected false or null`);
-          }
-
-          return true;
-        },
-      },
-
-      expose: {
-        dependencies: ['albumData', 'coverArtistContribsByRef'],
-        transform: (hasCoverArt, {
-          albumData,
-          coverArtistContribsByRef,
-          [Track.instance]: track,
-        }) =>
-          Track.hasCoverArt(
-            track,
-            albumData,
-            coverArtistContribsByRef,
-            hasCoverArt
-          ),
-      },
-    },
-
-    coverArtFileExtension: {
-      flags: {update: true, expose: true},
-
-      update: {validate: isFileExtension},
-
-      expose: {
-        dependencies: ['albumData', 'coverArtistContribsByRef'],
-        transform: (coverArtFileExtension, {
-          albumData,
-          coverArtistContribsByRef,
-          hasCoverArt,
-          [Track.instance]: track,
-        }) =>
-          coverArtFileExtension ??
-          (Track.hasCoverArt(
-            track,
-            albumData,
-            coverArtistContribsByRef,
-            hasCoverArt
-          )
-            ? Track.findAlbum(track, albumData)?.trackCoverArtFileExtension
-            : Track.findAlbum(track, albumData)?.coverArtFileExtension) ??
-          'jpg',
-      },
-    },
-
-    originalReleaseTrackByRef: Thing.common.singleReference(Track),
-
-    dataSourceAlbumByRef: Thing.common.singleReference(Album),
-
-    commentary: Thing.common.commentary(),
-    lyrics: Thing.common.simpleString(),
-    additionalFiles: Thing.common.additionalFiles(),
-    sheetMusicFiles: Thing.common.additionalFiles(),
-    midiProjectFiles: Thing.common.additionalFiles(),
+    name: name('Unnamed Track'),
+    directory: directory(),
+
+    duration: duration(),
+    urls: urls(),
+    dateFirstReleased: simpleDate(),
+
+    color: [
+      exposeUpdateValueOrContinue({
+        validate: input.value(isColor),
+      }),
+
+      withContainingTrackSection(),
+
+      withPropertyFromObject({
+        object: '#trackSection',
+        property: input.value('color'),
+      }),
+
+      exposeDependencyOrContinue({dependency: '#trackSection.color'}),
+
+      withPropertyFromAlbum({
+        property: input.value('color'),
+      }),
+
+      exposeDependency({dependency: '#album.color'}),
+    ],
+
+    alwaysReferenceByDirectory: [
+      withAlwaysReferenceByDirectory(),
+      exposeDependency({dependency: '#alwaysReferenceByDirectory'}),
+    ],
+
+    // Disables presenting the track as though it has its own unique artwork.
+    // This flag should only be used in select circumstances, i.e. to override
+    // an album's trackCoverArtists. This flag supercedes that property, as well
+    // as the track's own coverArtists.
+    disableUniqueCoverArt: flag(),
+
+    // File extension for track's corresponding media file. This represents the
+    // track's unique cover artwork, if any, and does not inherit the extension
+    // of the album's main artwork. It does inherit trackCoverArtFileExtension,
+    // if present on the album.
+    coverArtFileExtension: [
+      exitWithoutUniqueCoverArt(),
+
+      exposeUpdateValueOrContinue({
+        validate: input.value(isFileExtension),
+      }),
+
+      withPropertyFromAlbum({
+        property: input.value('trackCoverArtFileExtension'),
+      }),
+
+      exposeDependencyOrContinue({dependency: '#album.trackCoverArtFileExtension'}),
+
+      exposeConstant({
+        value: input.value('jpg'),
+      }),
+    ],
+
+    // Date of cover art release. Like coverArtFileExtension, this represents
+    // only the track's own unique cover artwork, if any. This exposes only as
+    // the track's own coverArtDate or its album's trackArtDate, so if neither
+    // is specified, this value is null.
+    coverArtDate: [
+      withHasUniqueCoverArt(),
+
+      exitWithoutDependency({
+        dependency: '#hasUniqueCoverArt',
+        mode: input.value('falsy'),
+      }),
+
+      exposeUpdateValueOrContinue({
+        validate: input.value(isDate),
+      }),
+
+      withPropertyFromAlbum({
+        property: input.value('trackArtDate'),
+      }),
+
+      exposeDependency({dependency: '#album.trackArtDate'}),
+    ],
+
+    commentary: commentary(),
+    lyrics: simpleString(),
+
+    additionalFiles: additionalFiles(),
+    sheetMusicFiles: additionalFiles(),
+    midiProjectFiles: additionalFiles(),
+
+    originalReleaseTrack: singleReference({
+      class: input.value(Track),
+      find: input.value(find.track),
+      data: 'trackData',
+    }),
+
+    // Internal use only - for directly identifying an album inside a track's
+    // util.inspect display, if it isn't indirectly available (by way of being
+    // included in an album's track list).
+    dataSourceAlbum: singleReference({
+      class: input.value(Album),
+      find: input.value(find.album),
+      data: 'albumData',
+    }),
+
+    artistContribs: [
+      inheritFromOriginalRelease({
+        property: input.value('artistContribs'),
+      }),
+
+      withResolvedContribs({
+        from: input.updateValue({validate: isContributionList}),
+      }).outputs({
+        '#resolvedContribs': '#artistContribs',
+      }),
+
+      exposeDependencyOrContinue({dependency: '#artistContribs'}),
+
+      withPropertyFromAlbum({
+        property: input.value('artistContribs'),
+      }),
+
+      exposeDependency({dependency: '#album.artistContribs'}),
+    ],
+
+    contributorContribs: [
+      inheritFromOriginalRelease({
+        property: input.value('contributorContribs'),
+      }),
+
+      contributionList(),
+    ],
+
+    // Cover artists aren't inherited from the original release, since it
+    // typically varies by release and isn't defined by the musical qualities
+    // of the track.
+    coverArtistContribs: [
+      exitWithoutUniqueCoverArt(),
+
+      withResolvedContribs({
+        from: input.updateValue({validate: isContributionList}),
+      }).outputs({
+        '#resolvedContribs': '#coverArtistContribs',
+      }),
+
+      exposeDependencyOrContinue({dependency: '#coverArtistContribs'}),
+
+      withPropertyFromAlbum({
+        property: input.value('trackCoverArtistContribs'),
+      }),
+
+      exposeDependency({dependency: '#album.trackCoverArtistContribs'}),
+    ],
+
+    referencedTracks: [
+      inheritFromOriginalRelease({
+        property: input.value('referencedTracks'),
+      }),
+
+      referenceList({
+        class: input.value(Track),
+        find: input.value(find.track),
+        data: 'trackData',
+      }),
+    ],
+
+    sampledTracks: [
+      inheritFromOriginalRelease({
+        property: input.value('sampledTracks'),
+      }),
+
+      referenceList({
+        class: input.value(Track),
+        find: input.value(find.track),
+        data: 'trackData',
+      }),
+    ],
+
+    artTags: referenceList({
+      class: input.value(ArtTag),
+      find: input.value(find.artTag),
+      data: 'artTagData',
+    }),
 
     // Update only
 
-    albumData: Thing.common.wikiData(Album),
-    artistData: Thing.common.wikiData(Artist),
-    artTagData: Thing.common.wikiData(ArtTag),
-    flashData: Thing.common.wikiData(Flash),
-    trackData: Thing.common.wikiData(Track),
+    albumData: wikiData(Album),
+    artistData: wikiData(Artist),
+    artTagData: wikiData(ArtTag),
+    flashData: wikiData(Flash),
+    trackData: wikiData(Track),
 
     // Expose only
 
-    commentatorArtists: Thing.common.commentatorArtists(),
-
-    album: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['albumData'],
-        compute: ({[Track.instance]: track, albumData}) =>
-          albumData?.find((album) => album.tracks.includes(track)) ?? null,
-      },
-    },
-
-    // Note - this is an internal property used only to help identify a track.
-    // It should not be assumed in general that the album and dataSourceAlbum match
-    // (i.e. a track may dynamically be moved from one album to another, at
-    // which point dataSourceAlbum refers to where it was originally from, and is
-    // not generally relevant information). It's also not guaranteed that
-    // dataSourceAlbum is available (depending on the Track creator to optionally
-    // provide dataSourceAlbumByRef).
-    dataSourceAlbum: Thing.common.dynamicThingFromSingleReference(
-      'dataSourceAlbumByRef',
-      'albumData',
-      find.album
-    ),
-
-    date: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['albumData', 'dateFirstReleased'],
-        compute: ({albumData, dateFirstReleased, [Track.instance]: track}) =>
-          dateFirstReleased ?? Track.findAlbum(track, albumData)?.date ?? null,
-      },
-    },
-
-    color: {
-      flags: {update: true, expose: true},
-
-      update: {validate: isColor},
-
-      expose: {
-        dependencies: ['albumData'],
-
-        transform: (color, {albumData, [Track.instance]: track}) =>
-          color ??
-            Track.findAlbum(track, albumData)
-              ?.trackSections.find(({tracks}) => tracks.includes(track))
-                ?.color ?? null,
-      },
-    },
-
-    coverArtDate: {
-      flags: {update: true, expose: true},
-
-      update: {validate: isDate},
-
-      expose: {
-        dependencies: [
-          'albumData',
-          'coverArtistContribsByRef',
-          'dateFirstReleased',
-          'hasCoverArt',
-        ],
-        transform: (coverArtDate, {
-          albumData,
-          coverArtistContribsByRef,
-          dateFirstReleased,
-          hasCoverArt,
-          [Track.instance]: track,
-        }) =>
-          (Track.hasCoverArt(track, albumData, coverArtistContribsByRef, hasCoverArt)
-            ? coverArtDate ??
-              dateFirstReleased ??
-              Track.findAlbum(track, albumData)?.trackArtDate ??
-              Track.findAlbum(track, albumData)?.date ??
-              null
-            : null),
-      },
-    },
-
-    hasUniqueCoverArt: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['albumData', 'coverArtistContribsByRef', 'hasCoverArt'],
-        compute: ({
-          albumData,
-          coverArtistContribsByRef,
-          hasCoverArt,
-          [Track.instance]: track,
-        }) =>
-          Track.hasUniqueCoverArt(
-            track,
-            albumData,
-            coverArtistContribsByRef,
-            hasCoverArt
-          ),
-      },
-    },
-
-    originalReleaseTrack: Thing.common.dynamicThingFromSingleReference(
-      'originalReleaseTrackByRef',
-      'trackData',
-      find.track
-    ),
-
-    otherReleases: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['originalReleaseTrackByRef', 'trackData'],
-
-        compute: ({
-          originalReleaseTrackByRef: t1origRef,
-          trackData,
-          [Track.instance]: t1,
-        }) => {
-          if (!trackData) {
-            return [];
-          }
-
-          const t1orig = find.track(t1origRef, trackData);
-
-          return [
-            t1orig,
-            ...trackData.filter((t2) => {
-              const {originalReleaseTrack: t2orig} = t2;
-              return t2 !== t1 && t2orig && (t2orig === t1orig || t2orig === t1);
-            }),
-          ].filter(Boolean);
-        },
-      },
-    },
-
-    artistContribs:
-      Track.inheritFromOriginalRelease('artistContribs', [],
-        Thing.common.dynamicInheritContribs(
-          null,
-          'artistContribsByRef',
-          'artistContribsByRef',
-          'albumData',
-          Track.findAlbum)),
-
-    contributorContribs:
-      Track.inheritFromOriginalRelease('contributorContribs', [],
-        Thing.common.dynamicContribs('contributorContribsByRef')),
-
-    // Cover artists aren't inherited from the original release, since it
-    // typically varies by release and isn't defined by the musical qualities
-    // of the track.
-    coverArtistContribs:
-      Thing.common.dynamicInheritContribs(
-        'hasCoverArt',
-        'coverArtistContribsByRef',
-        'trackCoverArtistContribsByRef',
-        'albumData',
-        Track.findAlbum),
-
-    referencedTracks:
-      Track.inheritFromOriginalRelease('referencedTracks', [],
-        Thing.common.dynamicThingsFromReferenceList(
-          'referencedTracksByRef',
-          'trackData',
-          find.track)),
-
-    sampledTracks:
-      Track.inheritFromOriginalRelease('sampledTracks', [],
-        Thing.common.dynamicThingsFromReferenceList(
-          'sampledTracksByRef',
-          'trackData',
-          find.track)),
-
-    // Specifically exclude re-releases from this list - while it's useful to
-    // get from a re-release to the tracks it references, re-releases aren't
-    // generally relevant from the perspective of the tracks being referenced.
-    // Filtering them from data here hides them from the corresponding field
-    // on the site (obviously), and has the bonus of not counting them when
-    // counting the number of times a track has been referenced, for use in
-    // the "Tracks - by Times Referenced" listing page (or other data
-    // processing).
-    referencedByTracks: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['trackData'],
-
-        compute: ({trackData, [Track.instance]: track}) =>
-          trackData
-            ? trackData
-                .filter((t) => !t.originalReleaseTrack)
-                .filter((t) => t.referencedTracks?.includes(track))
-            : [],
-      },
-    },
-
-    // For the same reasoning, exclude re-releases from sampled tracks too.
-    sampledByTracks: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['trackData'],
-
-        compute: ({trackData, [Track.instance]: track}) =>
-          trackData
-            ? trackData
-                .filter((t) => !t.originalReleaseTrack)
-                .filter((t) => t.sampledTracks?.includes(track))
-            : [],
-      },
-    },
-
-    featuredInFlashes: Thing.common.reverseReferenceList(
-      'flashData',
-      'featuredTracks'
-    ),
-
-    artTags: Thing.common.dynamicThingsFromReferenceList(
-      'artTagsByRef',
-      'artTagData',
-      find.artTag
-    ),
-  });
-
-  // This is a quick utility function for now, since the same code is reused in
-  // several places. Ideally it wouldn't be - we'd just reuse the `album`
-  // property - but support for that hasn't been coded yet :P
-  static findAlbum = (track, albumData) =>
-    albumData?.find((album) => album.tracks.includes(track));
-
-  // Another reused utility function. This one's logic is a bit more complicated.
-  static hasCoverArt(
-    track,
-    albumData,
-    coverArtistContribsByRef,
-    hasCoverArt
-  ) {
-    if (!empty(coverArtistContribsByRef)) {
-      return true;
-    }
+    commentatorArtists: commentatorArtists(),
 
-    const album = Track.findAlbum(track, albumData);
-    if (album && !empty(album.trackCoverArtistContribsByRef)) {
-      return true;
-    }
+    album: [
+      withAlbum(),
+      exposeDependency({dependency: '#album'}),
+    ],
 
-    return false;
-  }
+    date: [
+      exposeDependencyOrContinue({dependency: 'dateFirstReleased'}),
 
-  static hasUniqueCoverArt(
-    track,
-    albumData,
-    coverArtistContribsByRef,
-    hasCoverArt
-  ) {
-    if (!empty(coverArtistContribsByRef)) {
-      return true;
-    }
+      withPropertyFromAlbum({
+        property: input.value('date'),
+      }),
 
-    if (hasCoverArt === false) {
-      return false;
-    }
+      exposeDependency({dependency: '#album.date'}),
+    ],
 
-    const album = Track.findAlbum(track, albumData);
-    if (album && !empty(album.trackCoverArtistContribsByRef)) {
-      return true;
-    }
+    hasUniqueCoverArt: [
+      withHasUniqueCoverArt(),
+      exposeDependency({dependency: '#hasUniqueCoverArt'}),
+    ],
 
-    return false;
-  }
+    otherReleases: [
+      withOtherReleases(),
+      exposeDependency({dependency: '#otherReleases'}),
+    ],
 
-  static inheritFromOriginalRelease(
-    originalProperty,
-    originalMissingValue,
-    ownPropertyDescriptor
-  ) {
-    return {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: [
-          ...ownPropertyDescriptor.expose.dependencies,
-          'originalReleaseTrackByRef',
-          'trackData',
-        ],
-
-        compute(dependencies) {
-          const {
-            originalReleaseTrackByRef,
-            trackData,
-          } = dependencies;
-
-          if (originalReleaseTrackByRef) {
-            if (!trackData) return originalMissingValue;
-            const original = find.track(originalReleaseTrackByRef, trackData, {mode: 'quiet'});
-            if (!original) return originalMissingValue;
-            return original[originalProperty];
-          }
-
-          return ownPropertyDescriptor.expose.compute(dependencies);
-        },
-      },
-    };
-  }
+    referencedByTracks: trackReverseReferenceList({
+      list: input.value('referencedTracks'),
+    }),
 
-  [inspect.custom]() {
-    const base = Thing.prototype[inspect.custom].apply(this);
+    sampledByTracks: trackReverseReferenceList({
+      list: input.value('sampledTracks'),
+    }),
 
-    const rereleasePart =
-      (this.originalReleaseTrackByRef
-        ? `${color.yellow('[rerelease]')} `
-        : ``);
+    featuredInFlashes: reverseReferenceList({
+      data: 'flashData',
+      list: input.value('featuredTracks'),
+    }),
+  });
 
-    const {album, dataSourceAlbum} = this;
+  [inspect.custom](depth) {
+    const parts = [];
 
-    const albumName =
-      (album
-        ? album.name
-        : dataSourceAlbum?.name);
+    parts.push(Thing.prototype[inspect.custom].apply(this));
 
-    const albumIndex =
-      albumName &&
-        (album
-          ? album.tracks.indexOf(this)
-          : dataSourceAlbum.tracks.indexOf(this));
+    if (CacheableObject.getUpdateValue(this, 'originalReleaseTrack')) {
+      parts.unshift(`${colors.yellow('[rerelease]')} `);
+    }
 
-    const trackNum =
-      albumName &&
+    let album;
+    if (depth >= 0 && (album = this.album ?? this.dataSourceAlbum)) {
+      const albumName = album.name;
+      const albumIndex = album.tracks.indexOf(this);
+      const trackNum =
         (albumIndex === -1
           ? '#?'
           : `#${albumIndex + 1}`);
+      parts.push(` (${colors.yellow(trackNum)} in ${colors.green(albumName)})`);
+    }
 
-    const albumPart =
-      albumName
-        ? ` (${color.yellow(trackNum)} in ${color.green(albumName)})`
-        : ``;
-
-    return rereleasePart + base + albumPart;
+    return parts.join('');
   }
 }
diff --git a/src/data/things/validators.js b/src/data/things/validators.js
index 5748eacf..ee301f15 100644
--- a/src/data/things/validators.js
+++ b/src/data/things/validators.js
@@ -1,7 +1,7 @@
 import {inspect as nodeInspect} from 'node:util';
 
-import {color, ENABLE_COLOR} from '#cli';
-import {withAggregate} from '#sugar';
+import {colors, ENABLE_COLOR} from '#cli';
+import {empty, typeAppearance, withAggregate} from '#sugar';
 
 function inspect(value) {
   return nodeInspect(value, {colors: ENABLE_COLOR});
@@ -9,13 +9,13 @@ function inspect(value) {
 
 // Basic types (primitives)
 
-function a(noun) {
+export function a(noun) {
   return /[aeiou]/.test(noun[0]) ? `an ${noun}` : `a ${noun}`;
 }
 
-function isType(value, type) {
+export function isType(value, type) {
   if (typeof value !== type)
-    throw new TypeError(`Expected ${a(type)}, got ${typeof value}`);
+    throw new TypeError(`Expected ${a(type)}, got ${typeAppearance(value)}`);
 
   return true;
 }
@@ -132,7 +132,7 @@ export function isObject(value) {
 
 export function isArray(value) {
   if (typeof value !== 'object' || value === null || !Array.isArray(value))
-    throw new TypeError(`Expected an array, got ${value}`);
+    throw new TypeError(`Expected an array, got ${typeAppearance(value)}`);
 
   return true;
 }
@@ -174,7 +174,8 @@ function validateArrayItemsHelper(itemValidator) {
         throw new Error(`Expected validator to return true`);
       }
     } catch (error) {
-      error.message = `(index: ${color.yellow(`#${index}`)}, item: ${inspect(item)}) ${error.message}`;
+      error.message = `(index: ${colors.yellow(`${index}`)}, item: ${inspect(item)}) ${error.message}`;
+      error[Symbol.for('hsmusic.decorate.indexInSourceArray')] = index;
       throw error;
     }
   };
@@ -264,7 +265,7 @@ export function validateProperties(spec) {
           try {
             specValidator(value);
           } catch (error) {
-            error.message = `(key: ${color.green(specKey)}, value: ${inspect(value)}) ${error.message}`;
+            error.message = `(key: ${colors.green(specKey)}, value: ${inspect(value)}) ${error.message}`;
             throw error;
           }
         });
@@ -308,7 +309,7 @@ export const isTrackSection = validateProperties({
   color: optional(isColor),
   dateOriginallyReleased: optional(isDate),
   isDefaultTrackSection: optional(isBoolean),
-  tracksByRef: optional(validateReferenceList('track')),
+  tracks: optional(validateReferenceList('track')),
 });
 
 export const isTrackSectionList = validateArrayItems(isTrackSection);
@@ -404,6 +405,76 @@ export function validateReferenceList(type = '') {
   return validateArrayItems(validateReference(type));
 }
 
+const validateWikiData_cache = {};
+
+export function validateWikiData({
+  referenceType = '',
+  allowMixedTypes = false,
+}) {
+  if (referenceType && allowMixedTypes) {
+    throw new TypeError(`Don't specify both referenceType and allowMixedTypes`);
+  }
+
+  validateWikiData_cache[referenceType] ??= {};
+  validateWikiData_cache[referenceType][allowMixedTypes] ??= new WeakMap();
+
+  const isArrayOfObjects = validateArrayItems(isObject);
+
+  return (array) => {
+    const subcache = validateWikiData_cache[referenceType][allowMixedTypes];
+    if (subcache.has(array)) return subcache.get(array);
+
+    let OK = false;
+
+    try {
+      isArrayOfObjects(array);
+
+      if (empty(array)) {
+        OK = true; return true;
+      }
+
+      const allRefTypes =
+        new Set(array.map(object =>
+          object.constructor[Symbol.for('Thing.referenceType')]));
+
+      if (allRefTypes.has(undefined)) {
+        if (allRefTypes.size === 1) {
+          throw new TypeError(`Expected array of wiki data objects, got array of other objects`);
+        } else {
+          throw new TypeError(`Expected array of wiki data objects, got mixed items`);
+        }
+      }
+
+      if (allRefTypes.size > 1) {
+        if (allowMixedTypes) {
+          OK = true; return true;
+        }
+
+        const types = () => Array.from(allRefTypes).join(', ');
+
+        if (referenceType) {
+          if (allRefTypes.has(referenceType)) {
+            allRefTypes.remove(referenceType);
+            throw new TypeError(`Expected array of only ${referenceType}, also got other types: ${types()}`)
+          } else {
+            throw new TypeError(`Expected array of only ${referenceType}, got other types: ${types()}`);
+          }
+        }
+
+        throw new TypeError(`Expected array of unmixed reference types, got multiple: ${types()}`);
+      }
+
+      if (referenceType && !allRefTypes.has(referenceType)) {
+        throw new TypeError(`Expected array of ${referenceType}, got array of ${allRefTypes[0]}`)
+      }
+
+      OK = true; return true;
+    } finally {
+      subcache.set(array, OK);
+    }
+  };
+}
+
 // Compositional utilities
 
 export function oneOf(...checks) {
diff --git a/src/data/things/wiki-info.js b/src/data/things/wiki-info.js
index e906cab1..0460f272 100644
--- a/src/data/things/wiki-info.js
+++ b/src/data/things/wiki-info.js
@@ -1,20 +1,23 @@
+import {input} from '#composite';
 import find from '#find';
+import {isLanguageCode, isName, isURL} from '#validators';
+
+import {
+  color,
+  flag,
+  name,
+  referenceList,
+  simpleString,
+  wikiData,
+} from '#composite/wiki-properties';
 
 import Thing from './thing.js';
 
 export class WikiInfo extends Thing {
-  static [Thing.getPropertyDescriptors] = ({
-    Group,
-
-    validators: {
-      isLanguageCode,
-      isName,
-      isURL,
-    },
-  }) => ({
+  static [Thing.getPropertyDescriptors] = ({Group}) => ({
     // Update & expose
 
-    name: Thing.common.name('Unnamed Wiki'),
+    name: name('Unnamed Wiki'),
 
     // Displayed in nav bar.
     nameShort: {
@@ -27,12 +30,12 @@ export class WikiInfo extends Thing {
       },
     },
 
-    color: Thing.common.color(),
+    color: color(),
 
     // One-line description used for <meta rel="description"> tag.
-    description: Thing.common.simpleString(),
+    description: simpleString(),
 
-    footerContent: Thing.common.simpleString(),
+    footerContent: simpleString(),
 
     defaultLanguage: {
       flags: {update: true, expose: true},
@@ -44,25 +47,21 @@ export class WikiInfo extends Thing {
       update: {validate: isURL},
     },
 
-    divideTrackListsByGroupsByRef: Thing.common.referenceList(Group),
+    divideTrackListsByGroups: referenceList({
+      class: input.value(Group),
+      find: input.value(find.group),
+      data: 'groupData',
+    }),
 
     // Feature toggles
-    enableFlashesAndGames: Thing.common.flag(false),
-    enableListings: Thing.common.flag(false),
-    enableNews: Thing.common.flag(false),
-    enableArtTagUI: Thing.common.flag(false),
-    enableGroupUI: Thing.common.flag(false),
+    enableFlashesAndGames: flag(false),
+    enableListings: flag(false),
+    enableNews: flag(false),
+    enableArtTagUI: flag(false),
+    enableGroupUI: flag(false),
 
     // Update only
 
-    groupData: Thing.common.wikiData(Group),
-
-    // Expose only
-
-    divideTrackListsByGroups: Thing.common.dynamicThingsFromReferenceList(
-      'divideTrackListsByGroupsByRef',
-      'groupData',
-      find.group
-    ),
+    groupData: wikiData(Group),
   });
 }
diff --git a/src/data/yaml.js b/src/data/yaml.js
index 07e0a3d2..c799be5f 100644
--- a/src/data/yaml.js
+++ b/src/data/yaml.js
@@ -7,10 +7,10 @@ import {inspect as nodeInspect} from 'node:util';
 
 import yaml from 'js-yaml';
 
-import {color, ENABLE_COLOR, logInfo, logWarn} from '#cli';
+import {colors, ENABLE_COLOR, logInfo, logWarn} from '#cli';
 import find, {bindFind} from '#find';
 import {traverse} from '#node-utils';
-import T from '#things';
+import T, {CacheableObject, Thing} from '#things';
 
 import {
   conditionallySuppressError,
@@ -137,7 +137,7 @@ function makeProcessDocument(
         const name = document[nameField];
         error.message = name
           ? `(name: ${inspect(name)}) ${error.message}`
-          : `(${color.dim(`no name found`)}) ${error.message}`;
+          : `(${colors.dim(`no name found`)}) ${error.message}`;
         throw error;
       }
     };
@@ -195,7 +195,7 @@ function makeProcessDocument(
 
     const thing = Reflect.construct(thingClass, []);
 
-    withAggregate({message: `Errors applying ${color.green(thingClass.name)} properties`}, ({call}) => {
+    withAggregate({message: `Errors applying ${colors.green(thingClass.name)} properties`}, ({call}) => {
       for (const [property, value] of Object.entries(sourceProperties)) {
         call(() => (thing[property] = value));
       }
@@ -228,7 +228,7 @@ makeProcessDocument.FieldCombinationsError = class FieldCombinationsError extend
 makeProcessDocument.FieldCombinationError = class FieldCombinationError extends Error {
   constructor(fields, message) {
     const fieldNames = Object.keys(fields);
-    const combinePart = `Don't combine ${fieldNames.map(field => color.red(field)).join(', ')}`;
+    const combinePart = `Don't combine ${fieldNames.map(field => colors.red(field)).join(', ')}`;
 
     const messagePart =
       (typeof message === 'function'
@@ -278,11 +278,11 @@ export const processAlbumDocument = makeProcessDocument(T.Album, {
     coverArtFileExtension: 'Cover Art File Extension',
     trackCoverArtFileExtension: 'Track Art File Extension',
 
-    wallpaperArtistContribsByRef: 'Wallpaper Artists',
+    wallpaperArtistContribs: 'Wallpaper Artists',
     wallpaperStyle: 'Wallpaper Style',
     wallpaperFileExtension: 'Wallpaper File Extension',
 
-    bannerArtistContribsByRef: 'Banner Artists',
+    bannerArtistContribs: 'Banner Artists',
     bannerStyle: 'Banner Style',
     bannerFileExtension: 'Banner File Extension',
     bannerDimensions: 'Banner Dimensions',
@@ -290,11 +290,11 @@ export const processAlbumDocument = makeProcessDocument(T.Album, {
     commentary: 'Commentary',
     additionalFiles: 'Additional Files',
 
-    artistContribsByRef: 'Artists',
-    coverArtistContribsByRef: 'Cover Artists',
-    trackCoverArtistContribsByRef: 'Default Track Cover Artists',
-    groupsByRef: 'Groups',
-    artTagsByRef: 'Art Tags',
+    artistContribs: 'Artists',
+    coverArtistContribs: 'Cover Artists',
+    trackCoverArtistContribs: 'Default Track Cover Artists',
+    groups: 'Groups',
+    artTags: 'Art Tags',
   },
 });
 
@@ -316,6 +316,10 @@ export const processTrackDocument = makeProcessDocument(T.Track, {
 
     'Date First Released': (value) => new Date(value),
     'Cover Art Date': (value) => new Date(value),
+    'Has Cover Art': (value) =>
+      (value === true ? false :
+       value === false ? true :
+       value),
 
     'Artists': parseContributors,
     'Contributors': parseContributors,
@@ -336,7 +340,7 @@ export const processTrackDocument = makeProcessDocument(T.Track, {
     dateFirstReleased: 'Date First Released',
     coverArtDate: 'Cover Art Date',
     coverArtFileExtension: 'Cover Art File Extension',
-    hasCoverArt: 'Has Cover Art',
+    disableUniqueCoverArt: 'Has Cover Art', // This gets transformed to flip true/false.
 
     alwaysReferenceByDirectory: 'Always Reference By Directory',
 
@@ -346,13 +350,13 @@ export const processTrackDocument = makeProcessDocument(T.Track, {
     sheetMusicFiles: 'Sheet Music Files',
     midiProjectFiles: 'MIDI Project Files',
 
-    originalReleaseTrackByRef: 'Originally Released As',
-    referencedTracksByRef: 'Referenced Tracks',
-    sampledTracksByRef: 'Sampled Tracks',
-    artistContribsByRef: 'Artists',
-    contributorContribsByRef: 'Contributors',
-    coverArtistContribsByRef: 'Cover Artists',
-    artTagsByRef: 'Art Tags',
+    originalReleaseTrack: 'Originally Released As',
+    referencedTracks: 'Referenced Tracks',
+    sampledTracks: 'Sampled Tracks',
+    artistContribs: 'Artists',
+    contributorContribs: 'Contributors',
+    coverArtistContribs: 'Cover Artists',
+    artTags: 'Art Tags',
   },
 
   invalidFieldCombinations: [
@@ -422,8 +426,8 @@ export const processFlashDocument = makeProcessDocument(T.Flash, {
     date: 'Date',
     coverArtFileExtension: 'Cover Art File Extension',
 
-    featuredTracksByRef: 'Featured Tracks',
-    contributorContribsByRef: 'Contributors',
+    featuredTracks: 'Featured Tracks',
+    contributorContribs: 'Contributors',
   },
 });
 
@@ -468,7 +472,7 @@ export const processGroupDocument = makeProcessDocument(T.Group, {
     description: 'Description',
     urls: 'URLs',
 
-    featuredAlbumsByRef: 'Featured Albums',
+    featuredAlbums: 'Featured Albums',
   },
 });
 
@@ -499,7 +503,7 @@ export const processWikiInfoDocument = makeProcessDocument(T.WikiInfo, {
     footerContent: 'Footer Content',
     defaultLanguage: 'Default Language',
     canonicalBase: 'Canonical Base',
-    divideTrackListsByGroupsByRef: 'Divide Track Lists By Groups',
+    divideTrackListsByGroups: 'Divide Track Lists By Groups',
     enableFlashesAndGames: 'Enable Flashes & Games',
     enableListings: 'Enable Listings',
     enableNews: 'Enable News',
@@ -534,9 +538,9 @@ export const homepageLayoutRowTypeProcessMapping = {
   albums: makeProcessHomepageLayoutRowDocument(T.HomepageLayoutAlbumsRow, {
     propertyFieldMapping: {
       displayStyle: 'Display Style',
-      sourceGroupByRef: 'Group',
+      sourceGroup: 'Group',
       countAlbumsFromGroup: 'Count',
-      sourceAlbumsByRef: 'Albums',
+      sourceAlbums: 'Albums',
       actionLinks: 'Actions',
     },
   }),
@@ -769,13 +773,13 @@ export const dataSteps = [
         let currentTrackSection = {
           name: `Default Track Section`,
           isDefaultTrackSection: true,
-          tracksByRef: [],
+          tracks: [],
         };
 
-        const albumRef = T.Thing.getReference(album);
+        const albumRef = Thing.getReference(album);
 
         const closeCurrentTrackSection = () => {
-          if (!empty(currentTrackSection.tracksByRef)) {
+          if (!empty(currentTrackSection.tracks)) {
             trackSections.push(currentTrackSection);
           }
         };
@@ -789,7 +793,7 @@ export const dataSteps = [
               color: entry.color,
               dateOriginallyReleased: entry.dateOriginallyReleased,
               isDefaultTrackSection: false,
-              tracksByRef: [],
+              tracks: [],
             };
 
             continue;
@@ -797,9 +801,9 @@ export const dataSteps = [
 
           trackData.push(entry);
 
-          entry.dataSourceAlbumByRef = albumRef;
+          entry.dataSourceAlbum = albumRef;
 
-          currentTrackSection.tracksByRef.push(T.Thing.getReference(entry));
+          currentTrackSection.tracks.push(Thing.getReference(entry));
         }
 
         closeCurrentTrackSection();
@@ -823,12 +827,12 @@ export const dataSteps = [
       const artistData = results;
 
       const artistAliasData = results.flatMap((artist) => {
-        const origRef = T.Thing.getReference(artist);
+        const origRef = Thing.getReference(artist);
         return artist.aliasNames?.map((name) => {
           const alias = new T.Artist();
           alias.name = name;
           alias.isAlias = true;
-          alias.aliasedArtistRef = origRef;
+          alias.aliasedArtist = origRef;
           alias.artistData = artistData;
           return alias;
         }) ?? [];
@@ -852,7 +856,7 @@ export const dataSteps = [
 
     save(results) {
       let flashAct;
-      let flashesByRef = [];
+      let flashRefs = [];
 
       if (results[0] && !(results[0] instanceof T.FlashAct)) {
         throw new Error(`Expected an act at top of flash data file`);
@@ -861,18 +865,18 @@ export const dataSteps = [
       for (const thing of results) {
         if (thing instanceof T.FlashAct) {
           if (flashAct) {
-            Object.assign(flashAct, {flashesByRef});
+            Object.assign(flashAct, {flashes: flashRefs});
           }
 
           flashAct = thing;
-          flashesByRef = [];
+          flashRefs = [];
         } else {
-          flashesByRef.push(T.Thing.getReference(thing));
+          flashRefs.push(Thing.getReference(thing));
         }
       }
 
       if (flashAct) {
-        Object.assign(flashAct, {flashesByRef});
+        Object.assign(flashAct, {flashes: flashRefs});
       }
 
       const flashData = results.filter((x) => x instanceof T.Flash);
@@ -895,7 +899,7 @@ export const dataSteps = [
 
     save(results) {
       let groupCategory;
-      let groupsByRef = [];
+      let groupRefs = [];
 
       if (results[0] && !(results[0] instanceof T.GroupCategory)) {
         throw new Error(`Expected a category at top of group data file`);
@@ -904,18 +908,18 @@ export const dataSteps = [
       for (const thing of results) {
         if (thing instanceof T.GroupCategory) {
           if (groupCategory) {
-            Object.assign(groupCategory, {groupsByRef});
+            Object.assign(groupCategory, {groups: groupRefs});
           }
 
           groupCategory = thing;
-          groupsByRef = [];
+          groupRefs = [];
         } else {
-          groupsByRef.push(T.Thing.getReference(thing));
+          groupRefs.push(Thing.getReference(thing));
         }
       }
 
       if (groupCategory) {
-        Object.assign(groupCategory, {groupsByRef});
+        Object.assign(groupCategory, {groups: groupRefs});
       }
 
       const groupData = results.filter((x) => x instanceof T.Group);
@@ -1007,7 +1011,7 @@ export async function loadAndProcessDataDocuments({dataPath}) {
       } catch (error) {
         error.message +=
           (error.message.includes('\n') ? '\n' : ' ') +
-          `(file: ${color.bright(color.blue(path.relative(dataPath, x.file)))})`;
+          `(file: ${colors.bright(colors.blue(path.relative(dataPath, x.file)))})`;
         throw error;
       }
     };
@@ -1030,7 +1034,7 @@ export async function loadAndProcessDataDocuments({dataPath}) {
         // just without the callbacks. Thank you.
         const filterBlankDocuments = documents => {
           const aggregate = openAggregate({
-            message: `Found blank documents - check for extra '${color.cyan(`---`)}'`,
+            message: `Found blank documents - check for extra '${colors.cyan(`---`)}'`,
           });
 
           const filteredDocuments =
@@ -1074,10 +1078,10 @@ export async function loadAndProcessDataDocuments({dataPath}) {
 
               if (count === 1) {
                 const range = `#${start + 1}`;
-                parts.push(`${count} document (${color.yellow(range)}), `);
+                parts.push(`${count} document (${colors.yellow(range)}), `);
               } else {
                 const range = `#${start + 1}-${end + 1}`;
-                parts.push(`${count} documents (${color.yellow(range)}), `);
+                parts.push(`${count} documents (${colors.yellow(range)}), `);
               }
 
               if (previous === null) {
@@ -1087,7 +1091,7 @@ export async function loadAndProcessDataDocuments({dataPath}) {
               } else {
                 const previousDescription = Object.entries(previous).at(0).join(': ');
                 const nextDescription = Object.entries(next).at(0).join(': ');
-                parts.push(`between "${color.cyan(previousDescription)}" and "${color.cyan(nextDescription)}"`);
+                parts.push(`between "${colors.cyan(previousDescription)}" and "${colors.cyan(nextDescription)}"`);
               }
 
               aggregate.push(new Error(parts.join('')));
@@ -1318,13 +1322,27 @@ export async function loadAndProcessDataDocuments({dataPath}) {
 
 // Data linking! Basically, provide (portions of) wikiData to the Things which
 // require it - they'll expose dynamically computed properties as a result (many
-// of which are required for page HTML generation).
-export function linkWikiDataArrays(wikiData) {
+// of which are required for page HTML generation and other expected behavior).
+//
+// The XXX_decacheWikiData option should be used specifically to mark
+// points where you *aren't* replacing any of the arrays under wikiData with
+// new values, and are using linkWikiDataArrays to instead "decache" data
+// properties which depend on any of them. It's currently not possible for
+// a CacheableObject to depend directly on the value of a property exposed
+// on some other CacheableObject, so when those values change, you have to
+// manually decache before the object will realize its cache isn't valid
+// anymore.
+export function linkWikiDataArrays(wikiData, {
+  XXX_decacheWikiData = false,
+} = {}) {
   function assignWikiData(things, ...keys) {
+    if (things === undefined) return;
     for (let i = 0; i < things.length; i++) {
       const thing = things[i];
       for (let j = 0; j < keys.length; j++) {
         const key = keys[j];
+        if (!(key in wikiData)) continue;
+        if (XXX_decacheWikiData) thing[key] = [];
         thing[key] = wikiData[key];
       }
     }
@@ -1342,7 +1360,7 @@ export function linkWikiDataArrays(wikiData) {
   assignWikiData(WD.flashData, 'artistData', 'flashActData', 'trackData');
   assignWikiData(WD.flashActData, 'flashData');
   assignWikiData(WD.artTagData, 'albumData', 'trackData');
-  assignWikiData(WD.homepageLayout.rows, 'albumData', 'groupData');
+  assignWikiData(WD.homepageLayout?.rows, 'albumData', 'groupData');
 }
 
 export function sortWikiDataArrays(wikiData) {
@@ -1379,7 +1397,7 @@ export function filterDuplicateDirectories(wikiData) {
   const aggregate = openAggregate({message: `Duplicate directories found`});
   for (const thingDataProp of deduplicateSpec) {
     const thingData = wikiData[thingDataProp];
-    aggregate.nest({message: `Duplicate directories found in ${color.green('wikiData.' + thingDataProp)}`}, ({call}) => {
+    aggregate.nest({message: `Duplicate directories found in ${colors.green('wikiData.' + thingDataProp)}`}, ({call}) => {
       const directoryPlaces = Object.create(null);
       const duplicateDirectories = [];
 
@@ -1405,7 +1423,7 @@ export function filterDuplicateDirectories(wikiData) {
         const places = directoryPlaces[directory];
         call(() => {
           throw new Error(
-            `Duplicate directory ${color.green(directory)}:\n` +
+            `Duplicate directory ${colors.green(directory)}:\n` +
               places.map((thing) => ` - ` + inspect(thing)).join('\n')
           );
         });
@@ -1446,45 +1464,45 @@ export function filterDuplicateDirectories(wikiData) {
 export function filterReferenceErrors(wikiData) {
   const referenceSpec = [
     ['wikiInfo', processWikiInfoDocument, {
-      divideTrackListsByGroupsByRef: 'group',
+      divideTrackListsByGroups: 'group',
     }],
 
     ['albumData', processAlbumDocument, {
-      artistContribsByRef: '_contrib',
-      coverArtistContribsByRef: '_contrib',
-      trackCoverArtistContribsByRef: '_contrib',
-      wallpaperArtistContribsByRef: '_contrib',
-      bannerArtistContribsByRef: '_contrib',
-      groupsByRef: 'group',
-      artTagsByRef: 'artTag',
+      artistContribs: '_contrib',
+      coverArtistContribs: '_contrib',
+      trackCoverArtistContribs: '_contrib',
+      wallpaperArtistContribs: '_contrib',
+      bannerArtistContribs: '_contrib',
+      groups: 'group',
+      artTags: 'artTag',
     }],
 
     ['trackData', processTrackDocument, {
-      artistContribsByRef: '_contrib',
-      contributorContribsByRef: '_contrib',
-      coverArtistContribsByRef: '_contrib',
-      referencedTracksByRef: '_trackNotRerelease',
-      sampledTracksByRef: '_trackNotRerelease',
-      artTagsByRef: 'artTag',
-      originalReleaseTrackByRef: '_trackNotRerelease',
+      artistContribs: '_contrib',
+      contributorContribs: '_contrib',
+      coverArtistContribs: '_contrib',
+      referencedTracks: '_trackNotRerelease',
+      sampledTracks: '_trackNotRerelease',
+      artTags: 'artTag',
+      originalReleaseTrack: '_trackNotRerelease',
     }],
 
     ['groupCategoryData', processGroupCategoryDocument, {
-      groupsByRef: 'group',
+      groups: 'group',
     }],
 
     ['homepageLayout.rows', undefined, {
-      sourceGroupByRef: 'group',
-      sourceAlbumsByRef: 'album',
+      sourceGroup: '_homepageSourceGroup',
+      sourceAlbums: 'album',
     }],
 
     ['flashData', processFlashDocument, {
-      contributorContribsByRef: '_contrib',
-      featuredTracksByRef: 'track',
+      contributorContribs: '_contrib',
+      featuredTracks: 'track',
     }],
 
     ['flashActData', processFlashActDocument, {
-      flashesByRef: 'flash',
+      flashes: 'flash',
     }],
   ];
 
@@ -1500,7 +1518,7 @@ export function filterReferenceErrors(wikiData) {
   for (const [thingDataProp, providedProcessDocumentFn, propSpec] of referenceSpec) {
     const thingData = getNestedProp(wikiData, thingDataProp);
 
-    aggregate.nest({message: `Reference errors in ${color.green('wikiData.' + thingDataProp)}`}, ({nest}) => {
+    aggregate.nest({message: `Reference errors in ${colors.green('wikiData.' + thingDataProp)}`}, ({nest}) => {
       const things = Array.isArray(thingData) ? thingData : [thingData];
 
       for (const thing of things) {
@@ -1516,10 +1534,10 @@ export function filterReferenceErrors(wikiData) {
 
         nest({message: `Reference errors in ${inspect(thing)}`}, ({push, filter}) => {
           for (const [property, findFnKey] of Object.entries(propSpec)) {
-            const value = thing[property];
+            const value = CacheableObject.getUpdateValue(thing, property);
 
             if (value === undefined) {
-              push(new TypeError(`Property ${color.red(property)} isn't valid for ${color.green(thing.constructor.name)}`));
+              push(new TypeError(`Property ${colors.red(property)} isn't valid for ${colors.green(thing.constructor.name)}`));
               continue;
             }
 
@@ -1536,23 +1554,34 @@ export function filterReferenceErrors(wikiData) {
                   if (alias) {
                     // No need to check if the original exists here. Aliases are automatically
                     // created from a field on the original, so the original certainly exists.
-                    const original = find.artist(alias.aliasedArtistRef, wikiData.artistData, {mode: 'quiet'});
-                    throw new Error(`Reference ${color.red(contribRef.who)} is to an alias, should be ${color.green(original.name)}`);
+                    const original = alias.aliasedArtist;
+                    throw new Error(`Reference ${colors.red(contribRef.who)} is to an alias, should be ${colors.green(original.name)}`);
                   }
 
                   return boundFind.artist(contribRef.who);
                 };
                 break;
 
+              case '_homepageSourceGroup':
+                findFn = groupRef => {
+                  if (groupRef === 'new-additions' || groupRef === 'new-releases') {
+                    return true;
+                  }
+
+                  return boundFind.group(groupRef);
+                };
+                break;
+
               case '_trackNotRerelease':
                 findFn = trackRef => {
                   const track = find.track(trackRef, wikiData.trackData, {mode: 'error'});
+                  const originalRef = track && CacheableObject.getUpdateValue(track, 'originalReleaseTrack');
 
-                  if (track?.originalReleaseTrackByRef) {
+                  if (originalRef) {
                     // It's possible for the original to not actually exist, in this case.
                     // It should still be reported since the 'Originally Released As' field
                     // was present.
-                    const original = find.track(track.originalReleaseTrackByRef, wikiData.trackData, {mode: 'quiet'});
+                    const original = find.track(originalRef, wikiData.trackData, {mode: 'quiet'});
 
                     // Prefer references by name, but only if it's unambiguous.
                     const originalByName =
@@ -1562,12 +1591,12 @@ export function filterReferenceErrors(wikiData) {
 
                     const shouldBeMessage =
                       (originalByName
-                        ? color.green(original.name)
+                        ? colors.green(original.name)
                      : original
-                        ? color.green('track:' + original.directory)
-                        : color.green(track.originalReleaseTrackByRef));
+                        ? colors.green('track:' + original.directory)
+                        : colors.green(originalRef));
 
-                    throw new Error(`Reference ${color.red(trackRef)} is to a rerelease, should be ${shouldBeMessage}`);
+                    throw new Error(`Reference ${colors.red(trackRef)} is to a rerelease, should be ${shouldBeMessage}`);
                   }
 
                   return track;
@@ -1580,7 +1609,7 @@ export function filterReferenceErrors(wikiData) {
             }
 
             const suppress = fn => conditionallySuppressError(error => {
-              if (property === 'sampledTracksByRef') {
+              if (property === 'sampledTracks') {
                 // Suppress "didn't match anything" errors in particular, just for samples.
                 // In hsmusic-data we have a lot of "stub" sample data which don't have
                 // corresponding tracks yet, so it won't be useful to report such reference
@@ -1598,13 +1627,13 @@ export function filterReferenceErrors(wikiData) {
 
             const fieldPropertyMessage =
               (processDocumentFn?.propertyFieldMapping?.[property]
-                ? ` in field ${color.green(processDocumentFn.propertyFieldMapping[property])}`
-                : ` in property ${color.green(property)}`);
+                ? ` in field ${colors.green(processDocumentFn.propertyFieldMapping[property])}`
+                : ` in property ${colors.green(property)}`);
 
             const findFnMessage =
               (findFnKey.startsWith('_')
                 ? ``
-                : ` (${color.green('find.' + findFnKey)})`);
+                : ` (${colors.green('find.' + findFnKey)})`);
 
             const errorMessage =
               (Array.isArray(value)
diff --git a/src/find.js b/src/find.js
index 966629e3..c8edce98 100644
--- a/src/find.js
+++ b/src/find.js
@@ -1,6 +1,7 @@
 import {inspect} from 'node:util';
 
-import {color, logWarn} from '#cli';
+import {colors, logWarn} from '#cli';
+import {typeAppearance} from '#sugar';
 
 function warnOrThrow(mode, message) {
   if (mode === 'error') {
@@ -14,115 +15,159 @@ function warnOrThrow(mode, message) {
   return null;
 }
 
-function findHelper(keys, findFns = {}) {
+export function processAllAvailableMatches(data, {
+  getMatchableNames = thing =>
+    (Object.hasOwn(thing, 'name')
+      ? [thing.name]
+      : []),
+} = {}) {
+  const byName = Object.create(null);
+  const byDirectory = Object.create(null);
+  const multipleNameMatches = Object.create(null);
+
+  for (const thing of data) {
+    for (const name of getMatchableNames(thing)) {
+      if (typeof name !== 'string') {
+        logWarn`Unexpected ${typeAppearance(name)} returned in names for ${inspect(thing)}`;
+        continue;
+      }
+
+      const normalizedName = name.toLowerCase();
+      if (normalizedName in byName) {
+        byName[normalizedName] = null;
+        if (normalizedName in multipleNameMatches) {
+          multipleNameMatches[normalizedName].push(thing);
+        } else {
+          multipleNameMatches[normalizedName] = [thing];
+        }
+      } else {
+        byName[normalizedName] = thing;
+      }
+    }
+
+    byDirectory[thing.directory] = thing;
+  }
+
+  return {byName, byDirectory, multipleNameMatches};
+}
+
+function findHelper({
+  referenceTypes,
+
+  getMatchableNames = undefined,
+}) {
+  const keyRefRegex =
+    new RegExp(String.raw`^(?:(${referenceTypes.join('|')}):(?=\S))?(.*)$`);
+
   // Note: This cache explicitly *doesn't* support mutable data arrays. If the
   // data array is modified, make sure it's actually a new array object, not
   // the original, or the cache here will break and act as though the data
   // hasn't changed!
   const cache = new WeakMap();
 
-  const byDirectory = findFns.byDirectory || matchDirectory;
-  const byName = findFns.byName || matchName;
-
-  const keyRefRegex = new RegExp(String.raw`^(?:(${keys.join('|')}):(?=\S))?(.*)$`);
-
   // The mode argument here may be 'warn', 'error', or 'quiet'. 'error' throws
   // errors for null matches (with details about the error), while 'warn' and
   // 'quiet' both return null, with 'warn' logging details directly to the
   // console.
-  return (fullRef, data, {mode = 'warn'} = {}) => {
+  return (fullRef, data, {mode = 'warn'}) => {
     if (!fullRef) return null;
     if (typeof fullRef !== 'string') {
-      throw new Error(`Got a reference that is ${typeof fullRef}, not string: ${fullRef}`);
+      throw new TypeError(`Expected a string, got ${typeAppearance(fullRef)}`);
     }
 
     if (!data) {
-      throw new Error(`Expected data to be present`);
+      throw new TypeError(`Expected data to be present`);
     }
 
-    if (!Array.isArray(data) && data.wikiData) {
-      throw new Error(`Old {wikiData: {...}} format provided`);
-    }
+    let subcache = cache.get(data);
+    if (!subcache) {
+      subcache =
+        processAllAvailableMatches(data, {
+          getMatchableNames,
+        });
 
-    let cacheForThisData = cache.get(data);
-    const cachedValue = cacheForThisData?.[fullRef];
-    if (cachedValue) {
-      globalThis.NUM_CACHE = (globalThis.NUM_CACHE || 0) + 1;
-      return cachedValue;
-    }
-    if (!cacheForThisData) {
-      cacheForThisData = Object.create(null);
-      cache.set(data, cacheForThisData);
+      cache.set(data, subcache);
     }
 
-    const match = fullRef.match(keyRefRegex);
-    if (!match) {
-      return warnOrThrow(mode, `Malformed link reference: "${fullRef}"`);
+    const regexMatch = fullRef.match(keyRefRegex);
+    if (!regexMatch) {
+      warnOrThrow(mode, `Malformed link reference: "${fullRef}"`);
     }
 
-    const key = match[1];
-    const ref = match[2];
-
-    const found = key ? byDirectory(ref, data, mode) : byName(ref, data, mode);
-
-    if (!found) {
-      warnOrThrow(mode, `Didn't match anything for ${color.bright(fullRef)}`);
+    const typePart = regexMatch[1];
+    const refPart = regexMatch[2];
+
+    const match =
+      (typePart
+        ? subcache.byDirectory[refPart]
+        : subcache.byName[refPart.toLowerCase()]);
+
+    if (!match && !typePart) {
+      if (subcache.multipleNameMatches[refPart]) {
+        return warnOrThrow(mode,
+          `Multiple matches for reference "${fullRef}". Please resolve:\n` +
+          subcache.multipleNameMatches[refPart]
+            .map(match => `- ${inspect(match)}\n`)
+            .join('') +
+          `Returning null for this reference.`);
+      }
     }
 
-    cacheForThisData[fullRef] = found;
+    if (!match) {
+      warnOrThrow(mode, `Didn't match anything for ${colors.bright(fullRef)}`);
+      return null;
+    }
 
-    return found;
+    return match;
   };
 }
 
-function matchDirectory(ref, data) {
-  return data.find(({directory}) => directory === ref);
-}
-
-function matchName(ref, data, mode) {
-  const matches =
-    data
-      .filter(({name}) => name.toLowerCase() === ref.toLowerCase())
-      .filter(thing =>
-        (Object.hasOwn(thing, 'alwaysReferenceByDirectory')
-          ? !thing.alwaysReferenceByDirectory
-          : true));
-
-  if (matches.length > 1) {
-    return warnOrThrow(mode,
-      `Multiple matches for reference "${ref}". Please resolve:\n` +
-      matches.map(match => `- ${inspect(match)}\n`).join('') +
-      `Returning null for this reference.`);
-  }
-
-  if (matches.length === 0) {
-    return null;
-  }
-
-  const thing = matches[0];
-
-  if (ref !== thing.name) {
-    warnOrThrow(mode,
-      `Bad capitalization: ${color.red(ref)} -> ${color.green(thing.name)}`);
-  }
-
-  return thing;
-}
-
-function matchTagName(ref, data, quiet) {
-  return matchName(ref.startsWith('cw: ') ? ref.slice(4) : ref, data, quiet);
-}
-
 const find = {
-  album: findHelper(['album', 'album-commentary', 'album-gallery']),
-  artist: findHelper(['artist', 'artist-gallery']),
-  artTag: findHelper(['tag'], {byName: matchTagName}),
-  flash: findHelper(['flash']),
-  group: findHelper(['group', 'group-gallery']),
-  listing: findHelper(['listing']),
-  newsEntry: findHelper(['news-entry']),
-  staticPage: findHelper(['static']),
-  track: findHelper(['track']),
+  album: findHelper({
+    referenceTypes: ['album', 'album-commentary', 'album-gallery'],
+  }),
+
+  artist: findHelper({
+    referenceTypes: ['artist', 'artist-gallery'],
+  }),
+
+  artTag: findHelper({
+    referenceTypes: ['tag'],
+
+    getMatchableNames: tag =>
+      (tag.isContentWarning
+        ? [`cw: ${tag.name}`]
+        : [tag.name]),
+  }),
+
+  flash: findHelper({
+    referenceTypes: ['flash'],
+  }),
+
+  group: findHelper({
+    referenceTypes: ['group', 'group-gallery'],
+  }),
+
+  listing: findHelper({
+    referenceTypes: ['listing'],
+  }),
+
+  newsEntry: findHelper({
+    referenceTypes: ['news-entry'],
+  }),
+
+  staticPage: findHelper({
+    referenceTypes: ['static'],
+  }),
+
+  track: findHelper({
+    referenceTypes: ['track'],
+
+    getMatchableNames: track =>
+      (track.alwaysReferenceByDirectory
+        ? []
+        : [track.name]),
+  }),
 };
 
 export default find;
@@ -155,7 +200,9 @@ export function bindFind(wikiData, opts1) {
                 ? findFn(ref, thingData, {...opts1, ...opts2})
                 : findFn(ref, thingData, opts1)
           : (ref, opts2) =>
-              opts2 ? findFn(ref, thingData, opts2) : findFn(ref, thingData),
+              opts2
+                ? findFn(ref, thingData, opts2)
+                : findFn(ref, thingData),
       ];
     })
   );
diff --git a/src/gen-thumbs.js b/src/gen-thumbs.js
index 34eed9c1..3d441bc9 100644
--- a/src/gen-thumbs.js
+++ b/src/gen-thumbs.js
@@ -93,8 +93,11 @@ import * as path from 'node:path';
 
 import dimensionsOf from 'image-size';
 
+import {delay, empty, queue} from '#sugar';
+import {CacheableObject} from '#things';
+
 import {
-  color,
+  colors,
   fileIssue,
   logError,
   logInfo,
@@ -110,8 +113,6 @@ import {
   traverse,
 } from '#node-utils';
 
-import {delay, empty, queue} from '#sugar';
-
 export const defaultMagickThreads = 8;
 
 export function getThumbnailsAvailableForDimensions([width, height]) {
@@ -629,8 +630,8 @@ export function getExpectedImagePaths(mediaPath, {urls, wikiData}) {
     wikiData.albumData
       .flatMap(album => [
         album.hasCoverArt && fromRoot.to('media.albumCover', album.directory, album.coverArtFileExtension),
-        !empty(album.bannerArtistContribsByRef) && fromRoot.to('media.albumBanner', album.directory, album.bannerFileExtension),
-        !empty(album.wallpaperArtistContribsByRef) && fromRoot.to('media.albumWallpaper', album.directory, album.wallpaperFileExtension),
+        !empty(CacheableObject.getUpdateValue(album, 'bannerArtistContribs')) && fromRoot.to('media.albumBanner', album.directory, album.bannerFileExtension),
+        !empty(CacheableObject.getUpdateValue(album, 'wallpaperArtistContribs')) && fromRoot.to('media.albumWallpaper', album.directory, album.wallpaperFileExtension),
       ])
       .filter(Boolean),
 
@@ -683,14 +684,14 @@ export async function verifyImagePaths(mediaPath, {urls, wikiData}) {
   if (!empty(missing)) {
     logWarn`** Some image files are missing! (${missing.length + ' files'}) **`;
     for (const file of missing) {
-      console.warn(color.yellow(` - `) + file);
+      console.warn(colors.yellow(` - `) + file);
     }
   }
 
   if (!empty(misplaced)) {
     logWarn`** Some image files are misplaced! (${misplaced.length + ' files'}) **`;
     for (const file of misplaced) {
-      console.warn(color.yellow(` - `) + file);
+      console.warn(colors.yellow(` - `) + file);
     }
   }
 
diff --git a/src/listing-spec.js b/src/listing-spec.js
index fe36fc01..2b33744a 100644
--- a/src/listing-spec.js
+++ b/src/listing-spec.js
@@ -1,14 +1,4 @@
-import {accumulateSum, empty, showAggregate} from '#sugar';
-
-import {
-  chunkByProperties,
-  getArtistNumContributions,
-  getTotalDuration,
-  sortAlphabetically,
-  sortByDate,
-  sortChronologically,
-  sortFlashesChronologically,
-} from '#wiki-data';
+import {empty, showAggregate} from '#sugar';
 
 const listingSpec = [];
 
diff --git a/src/page/flash.js b/src/page/flash.js
index b9d27d0f..7df74158 100644
--- a/src/page/flash.js
+++ b/src/page/flash.js
@@ -1,5 +1,3 @@
-import {empty} from '#sugar';
-
 export const description = `flash & game pages`;
 
 export function condition({wikiData}) {
diff --git a/src/repl.js b/src/repl.js
index 9ab4ddf0..ead01567 100644
--- a/src/repl.js
+++ b/src/repl.js
@@ -11,7 +11,7 @@ import {generateURLs, urlSpec} from '#urls';
 import {quickLoadAllFromYAML} from '#yaml';
 
 import _find, {bindFind} from '#find';
-import thingConstructors from '#things';
+import thingConstructors, {CacheableObject} from '#things';
 import * as serialize from '#serialize';
 import * as sugar from '#sugar';
 import * as wikiDataUtils from '#wiki-data';
@@ -63,6 +63,7 @@ export async function getContextAssignments({
     WD: wikiData,
 
     ...thingConstructors,
+    CacheableObject,
     language,
 
     ...sugar,
diff --git a/src/upd8.js b/src/upd8.js
index df172e48..2cc8f554 100755
--- a/src/upd8.js
+++ b/src/upd8.js
@@ -38,6 +38,7 @@ import {fileURLToPath} from 'node:url';
 
 import wrap from 'word-wrap';
 
+import {displayCompositeCacheAnalysis} from '#composite';
 import {processLanguageFile} from '#language';
 import {isMain, traverse} from '#node-utils';
 import bootRepl from '#repl';
@@ -47,7 +48,7 @@ import {generateURLs, urlSpec} from '#urls';
 import {sortByName} from '#wiki-data';
 
 import {
-  color,
+  colors,
   decorateTime,
   logWarn,
   logInfo,
@@ -279,7 +280,7 @@ async function main() {
     const indentWrap = (spaces, str) => wrap(str, {width: 60 - spaces, indent: ' '.repeat(spaces)});
 
     const showOptions = (msg, options) => {
-      console.log(color.bright(msg));
+      console.log(colors.bright(msg));
 
       const entries = Object.entries(options);
       const sortedOptions = sortByName(entries
@@ -310,13 +311,13 @@ async function main() {
           console.log('');
         }
 
-        console.log(color.bright(` --` + name) +
+        console.log(colors.bright(` --` + name) +
           (aliases.length
-            ? ` (or: ${aliases.map(alias => color.bright(`--` + alias)).join(', ')})`
+            ? ` (or: ${aliases.map(alias => colors.bright(`--` + alias)).join(', ')})`
             : '') +
           (descriptor.help
             ? ''
-            : color.dim('  (no help provided)')));
+            : colors.dim('  (no help provided)')));
 
         if (wrappedHelp) {
           console.log(wrappedHelp);
@@ -336,7 +337,7 @@ async function main() {
     };
 
     console.log(
-      color.bright(`hsmusic (aka. Homestuck Music Wiki)\n`) +
+      colors.bright(`hsmusic (aka. Homestuck Music Wiki)\n`) +
       `static wiki software cataloguing collaborative creation\n`);
 
     console.log(indentWrap(0,
@@ -496,7 +497,7 @@ async function main() {
 
   {
     const logThings = (thingDataProp, label) =>
-      logInfo` - ${wikiData[thingDataProp]?.length ?? color.red('(Missing!)')} ${color.normal(color.dim(label))}`;
+      logInfo` - ${wikiData[thingDataProp]?.length ?? colors.red('(Missing!)')} ${colors.normal(colors.dim(label))}`;
     try {
       logInfo`Loaded data and processed objects:`;
       logThings('albumData', 'albums');
@@ -625,6 +626,11 @@ async function main() {
       .map(thing => () => CacheableObject.cacheAllExposedProperties(thing)));
   }
 
+  if (noBuild) {
+    displayCompositeCacheAnalysis();
+    if (precacheData) return;
+  }
+
   const internalDefaultLanguage = await processLanguageFile(
     path.join(__dirname, DEFAULT_STRINGS_FILE));
 
@@ -755,7 +761,9 @@ async function main() {
 
   logInfo`Done preloading filesizes!`;
 
-  if (noBuild) return;
+  if (noBuild) {
+    return;
+  }
 
   const developersComment =
     `<!--\n` + [
diff --git a/src/util/cli.js b/src/util/cli.js
index e8c8c79f..4c08c085 100644
--- a/src/util/cli.js
+++ b/src/util/cli.js
@@ -17,7 +17,7 @@ export const ENABLE_COLOR =
 const C = (n) =>
   ENABLE_COLOR ? (text) => `\x1b[${n}m${text}\x1b[0m` : (text) => text;
 
-export const color = {
+export const colors = {
   bright: C('1'),
   dim: C('2'),
   normal: C('22'),
@@ -335,8 +335,8 @@ export function fileIssue({
   topMessage = `This shouldn't happen.`,
 } = {}) {
   if (topMessage) {
-    console.error(color.red(`${topMessage} Please let the HSMusic developers know:`));
+    console.error(colors.red(`${topMessage} Please let the HSMusic developers know:`));
   }
-  console.error(color.red(`- https://hsmusic.wiki/feedback/`));
-  console.error(color.red(`- https://github.com/hsmusic/hsmusic-wiki/issues/`));
+  console.error(colors.red(`- https://hsmusic.wiki/feedback/`));
+  console.error(colors.red(`- https://github.com/hsmusic/hsmusic-wiki/issues/`));
 }
diff --git a/src/util/html.js b/src/util/html.js
index f0c7bfdf..282a52da 100644
--- a/src/util/html.js
+++ b/src/util/html.js
@@ -2,7 +2,7 @@
 
 import {inspect} from 'node:util';
 
-import {empty} from '#sugar';
+import {empty, typeAppearance} from '#sugar';
 import * as commonValidators from '#validators';
 
 // COMPREHENSIVE!
@@ -242,7 +242,7 @@ export class Tag {
       this.selfClosing &&
       !(value === null ||
         value === undefined ||
-        !Boolean(value) ||
+        !value ||
         Array.isArray(value) && value.filter(Boolean).length === 0)
     ) {
       throw new Error(`Tag <${this.tagName}> is self-closing but got content`);
@@ -633,7 +633,7 @@ export class Template {
 
   static validateDescription(description) {
     if (typeof description !== 'object') {
-      throw new TypeError(`Expected object, got ${typeof description}`);
+      throw new TypeError(`Expected object, got ${typeAppearance(description)}`);
     }
 
     if (description === null) {
diff --git a/src/util/replacer.js b/src/util/replacer.js
index c5289cc5..095ee060 100644
--- a/src/util/replacer.js
+++ b/src/util/replacer.js
@@ -5,9 +5,8 @@
 // function, which converts nodes parsed here into actual HTML, links, etc
 // for embedding in a wiki webpage.
 
-import {logError, logWarn} from '#cli';
 import * as html from '#html';
-import {escapeRegex} from '#sugar';
+import {escapeRegex, typeAppearance} from '#sugar';
 
 // Syntax literals.
 const tagBeginning = '[[';
@@ -408,7 +407,7 @@ export function postprocessHeadings(inputNodes) {
 
 export function parseInput(input) {
   if (typeof input !== 'string') {
-    throw new TypeError(`Expected input to be string, got ${input}`);
+    throw new TypeError(`Expected input to be string, got ${typeAppearance(input)}`);
   }
 
   try {
diff --git a/src/util/sugar.js b/src/util/sugar.js
index 5b1f3193..2e724bae 100644
--- a/src/util/sugar.js
+++ b/src/util/sugar.js
@@ -6,7 +6,7 @@
 // It will likely only do exactly what I want it to, and only in the cases I
 // decided were relevant enough to 8other handling.
 
-import {color} from './cli.js';
+import {colors} from './cli.js';
 
 // Apparently JavaScript doesn't come with a function to split an array into
 // chunks! Weird. Anyway, this is an awesome place to use a generator, even
@@ -82,7 +82,7 @@ export function stitchArrays(keyToArray) {
   for (const [key, value] of Object.entries(keyToArray)) {
     if (value === null) continue;
     if (Array.isArray(value)) continue;
-    errors.push(new TypeError(`(${key}) Expected array or null, got ${value}`));
+    errors.push(new TypeError(`(${key}) Expected array or null, got ${typeAppearance(value)}`));
   }
 
   if (!empty(errors)) {
@@ -168,12 +168,24 @@ export function setIntersection(set1, set2) {
   return intersection;
 }
 
-export function filterProperties(obj, properties) {
-  const set = new Set(properties);
-  return Object.fromEntries(
-    Object
-      .entries(obj)
-      .filter(([key]) => set.has(key)));
+export function filterProperties(object, properties) {
+  if (typeof object !== 'object' || object === null) {
+    throw new TypeError(`Expected object to be an object, got ${typeAppearance(object)}`);
+  }
+
+  if (!Array.isArray(properties)) {
+    throw new TypeError(`Expected properties to be an array, got ${typeAppearance(properties)}`);
+  }
+
+  const filteredObject = {};
+
+  for (const property of properties) {
+    if (Object.hasOwn(object, property)) {
+      filteredObject[property] = object[property];
+    }
+  }
+
+  return filteredObject;
 }
 
 export function queue(array, max = 50) {
@@ -218,6 +230,16 @@ export function escapeRegex(string) {
   return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
 }
 
+// Gets the "look" of some arbitrary value. It's like typeof, but smarter.
+// Don't use this for actually validating types - it's only suitable for
+// inclusion in error messages.
+export function typeAppearance(value) {
+  if (value === null) return 'null';
+  if (value === undefined) return 'undefined';
+  if (Array.isArray(value)) return 'array';
+  return typeof value;
+}
+
 // Binds default values for arguments in a {key: value} type function argument
 // (typically the second argument, but may be overridden by providing a
 // [bindOpts.bindIndex] argument). Typically useful for preparing a function for
@@ -532,15 +554,17 @@ export function showAggregate(topError, {
   print = true,
 } = {}) {
   const recursive = (error, {level}) => {
-    let header = showTraces
+    let headerPart = showTraces
       ? `[${error.constructor.name || 'unnamed'}] ${
           error.message || '(no message)'
         }`
       : error instanceof AggregateError
       ? `[${error.message || '(no message)'}]`
       : error.message || '(no message)';
+
     if (showTraces) {
       const stackLines = error.stack?.split('\n');
+
       const stackLine = stackLines?.find(
         (line) =>
           line.trim().startsWith('at') &&
@@ -548,38 +572,44 @@ export function showAggregate(topError, {
           !line.includes('node:') &&
           !line.includes('<anonymous>')
       );
+
       const tracePart = stackLine
         ? '- ' +
           stackLine
             .trim()
             .replace(/file:\/\/.*\.js/, (match) => pathToFileURL(match))
         : '(no stack trace)';
-      header += ` ${color.dim(tracePart)}`;
-    }
-    const bar = level % 2 === 0 ? '\u2502' : color.dim('\u254e');
-    const head = level % 2 === 0 ? '\u257f' : color.dim('\u257f');
-
-    if (error instanceof AggregateError) {
-      return (
-        header +
-        '\n' +
-        error.errors
-          .map((error) => recursive(error, {level: level + 1}))
-          .flatMap((str) => str.split('\n'))
-          .map((line, i) => i === 0 ? ` ${head} ${line}` : ` ${bar} ${line}`)
-          .join('\n')
-      );
-    } else {
-      return header;
+
+      headerPart += ` ${colors.dim(tracePart)}`;
     }
+
+    const head1 = level % 2 === 0 ? '\u21aa' : colors.dim('\u21aa');
+    const bar1 = ' ';
+
+    const causePart =
+      (error.cause
+        ? recursive(error.cause, {level: level + 1})
+            .split('\n')
+            .map((line, i) => i === 0 ? ` ${head1} ${line}` : ` ${bar1} ${line}`)
+            .join('\n')
+        : '');
+
+    const head2 = level % 2 === 0 ? '\u257f' : colors.dim('\u257f');
+    const bar2 = level % 2 === 0 ? '\u2502' : colors.dim('\u254e');
+
+    const aggregatePart =
+      (error instanceof AggregateError
+        ? error.errors
+            .map(error => recursive(error, {level: level + 1}))
+            .flatMap(str => str.split('\n'))
+            .map((line, i) => i === 0 ? ` ${head2} ${line}` : ` ${bar2} ${line}`)
+            .join('\n')
+        : '');
+
+    return [headerPart, causePart, aggregatePart].filter(Boolean).join('\n');
   };
 
-  const message =
-    (topError instanceof AggregateError
-      ? recursive(topError, {level: 0})
-      : (showTraces
-          ? topError.stack
-          : topError.toString()));
+  const message = recursive(topError, {level: 0});
 
   if (print) {
     console.error(message);
@@ -593,7 +623,8 @@ export function decorateErrorWithIndex(fn) {
     try {
       return fn(x, index, array);
     } catch (error) {
-      error.message = `(${color.yellow(`#${index + 1}`)}) ${error.message}`;
+      error.message = `(${colors.yellow(`#${index + 1}`)}) ${error.message}`;
+      error[Symbol.for('hsmusic.decorate.indexInSourceArray')] = index;
       throw error;
     }
   };
diff --git a/src/util/urls.js b/src/util/urls.js
index d2b303e9..11b9b8b0 100644
--- a/src/util/urls.js
+++ b/src/util/urls.js
@@ -237,27 +237,6 @@ export function getPagePathname({
     : to('localized.' + pagePath[0], ...pagePath.slice(1)));
 }
 
-export function getPagePathnameAcrossLanguages({
-  defaultLanguage,
-  languages,
-  pagePath,
-  urls,
-}) {
-  return withEntries(languages, entries => entries
-    .filter(([key, language]) => key !== 'default' && !language.hidden)
-    .map(([_key, language]) => [
-      language.code,
-      getPagePathname({
-        baseDirectory:
-          (language === defaultLanguage
-            ? ''
-            : language.code),
-        pagePath,
-        urls,
-      }),
-    ]));
-}
-
 // Needed for the rare path arguments which themselves contains one or more
 // slashes, e.g. for listings, with arguments like 'albums/by-name'.
 export function getPageSubdirectoryPrefix({
diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js
index ad2f82fb..ac652b27 100644
--- a/src/util/wiki-data.js
+++ b/src/util/wiki-data.js
@@ -1,6 +1,6 @@
 // Utility functions for interacting with wiki data.
 
-import {accumulateSum, empty, stitchArrays, unique} from './sugar.js';
+import {accumulateSum, empty, unique} from './sugar.js';
 
 // Generic value operations
 
@@ -874,3 +874,71 @@ export function filterItemsForCarousel(items) {
     .filter(item => item.artTags.every(tag => !tag.isContentWarning))
     .slice(0, maxCarouselLayoutItems + 1);
 }
+
+// Ridiculous caching support nonsense
+
+export class TupleMap {
+  static maxNestedTupleLength = 25;
+
+  #store = [undefined, null, null, null];
+
+  #lifetime(value) {
+    if (Array.isArray(value) && value.length <= TupleMap.maxNestedTupleLength) {
+      return 'tuple';
+    } else if (
+      typeof value === 'object' && value !== null ||
+      typeof value === 'function'
+    ) {
+      return 'weak';
+    } else {
+      return 'strong';
+    }
+  }
+
+  #getSubstoreShallow(value, store) {
+    const lifetime = this.#lifetime(value);
+    const mapIndex = {weak: 1, strong: 2, tuple: 3}[lifetime];
+
+    let map = store[mapIndex];
+    if (map === null) {
+      map = store[mapIndex] =
+        (lifetime === 'weak' ? new WeakMap()
+       : lifetime === 'strong' ? new Map()
+       : lifetime === 'tuple' ? new TupleMap()
+       : null);
+    }
+
+    if (map.has(value)) {
+      return map.get(value);
+    } else {
+      const substore = [undefined, null, null, null];
+      map.set(value, substore);
+      return substore;
+    }
+  }
+
+  #getSubstoreDeep(tuple, store = this.#store) {
+    if (tuple.length === 0) {
+      return store;
+    } else {
+      const [first, ...rest] = tuple;
+      return this.#getSubstoreDeep(rest, this.#getSubstoreShallow(first, store));
+    }
+  }
+
+  get(tuple) {
+    const store = this.#getSubstoreDeep(tuple);
+    return store[0];
+  }
+
+  has(tuple) {
+    const store = this.#getSubstoreDeep(tuple);
+    return store[0] !== undefined;
+  }
+
+  set(tuple, value) {
+    const store = this.#getSubstoreDeep(tuple);
+    store[0] = value;
+    return value;
+  }
+}
diff --git a/src/write/build-modes/live-dev-server.js b/src/write/build-modes/live-dev-server.js
index 730686db..d4efd177 100644
--- a/src/write/build-modes/live-dev-server.js
+++ b/src/write/build-modes/live-dev-server.js
@@ -10,11 +10,9 @@ import {quickEvaluate} from '#content-function';
 import * as html from '#html';
 import * as pageSpecs from '#page-specs';
 import {serializeThings} from '#serialize';
-import {empty} from '#sugar';
 
 import {
   getPagePathname,
-  getPagePathnameAcrossLanguages,
   getURLsFrom,
   getURLsFromRoot,
 } from '#urls';
@@ -44,8 +42,8 @@ export function getCLIOptions() {
       },
     },
 
-    'quiet-responses': {
-      help: `Disables outputting [200] and [404] responses in the server log`,
+    'loud-responses': {
+      help: `Enables outputting [200] and [404] responses in the server log, which are suppressed by default`,
       type: 'flag',
     },
   };
@@ -80,7 +78,7 @@ export async function go({
 
   const host = cliOptions['host'] ?? defaultHost;
   const port = parseInt(cliOptions['port'] ?? defaultPort);
-  const quietResponses = cliOptions['quiet-responses'] ?? false;
+  const loudResponses = cliOptions['loud-responses'] ?? false;
 
   const contentDependenciesWatcher = await watchContentDependencies();
   const {contentDependencies} = contentDependenciesWatcher;
@@ -162,7 +160,7 @@ export async function go({
         });
         response.writeHead(200, contentTypeJSON);
         response.end(json);
-        if (!quietResponses) console.log(`${requestHead} [200] /data.json`);
+        if (loudResponses) console.log(`${requestHead} [200] /data.json`);
       } catch (error) {
         response.writeHead(500, contentTypeJSON);
         response.end(`Internal error serializing wiki JSON`);
@@ -258,7 +256,7 @@ export async function go({
         await pipeline(
           createReadStream(filePath),
           response);
-        if (!quietResponses) console.log(`${requestHead} [200] ${pathname}`);
+        if (loudResponses) console.log(`${requestHead} [200] ${pathname}`);
       } catch (error) {
         response.writeHead(500, contentTypePlain);
         response.end(`Failed during file-to-response pipeline`);
@@ -276,7 +274,7 @@ export async function go({
     if (!Object.hasOwn(urlToPageMap, pathnameKey)) {
       response.writeHead(404, contentTypePlain);
       response.end(`No page found for: ${pathnameKey}\n`);
-      if (!quietResponses) console.log(`${requestHead} [404] ${pathname}`);
+      if (loudResponses) console.log(`${requestHead} [404] ${pathname}`);
       return;
     }
 
@@ -333,13 +331,6 @@ export async function go({
         return;
       }
 
-      const localizedPathnames = getPagePathnameAcrossLanguages({
-        defaultLanguage,
-        languages,
-        pagePath: servePath,
-        urls,
-      });
-
       const bound = bindUtilities({
         absoluteTo,
         cachebust,
@@ -367,7 +358,7 @@ export async function go({
 
       const {pageHTML} = html.resolve(topLevelResult);
 
-      if (!quietResponses) console.log(`${requestHead} [200] ${pathname}`);
+      if (loudResponses) console.log(`${requestHead} [200] ${pathname}`);
       response.writeHead(200, contentTypeHTML);
       response.end(pageHTML);
     } catch (error) {
@@ -397,8 +388,11 @@ export async function go({
   server.on('listening', () => {
     logInfo`${'All done!'} Listening at: ${address}`;
     logInfo`Press ^C here (control+C) to stop the server and exit.`;
-    if (quietResponses) {
-      logInfo`Suppressing [200] and [404] response logging.`;
+    if (loudResponses) {
+      logInfo`Printing [200] and [404] responses.`
+    } else {
+      logInfo`Suppressing [200] and [404] response logging.`
+      logInfo`(Pass --loud-responses to show these.)`;
     }
   });
 
diff --git a/src/write/build-modes/static-build.js b/src/write/build-modes/static-build.js
index 79c8defd..09316999 100644
--- a/src/write/build-modes/static-build.js
+++ b/src/write/build-modes/static-build.js
@@ -21,13 +21,11 @@ import {
   logError,
   logInfo,
   logWarn,
-  progressCallAll,
   progressPromiseAll,
 } from '#cli';
 
 import {
   getPagePathname,
-  getPagePathnameAcrossLanguages,
   getURLsFrom,
   getURLsFromRoot,
 } from '#urls';
@@ -94,7 +92,6 @@ export async function go({
   srcRootPath,
   thumbsCache,
   urls,
-  urlSpec,
   wikiData,
 
   cachebust,
@@ -271,13 +268,6 @@ export async function go({
       ...pageWrites.map(page => () => {
         const pagePath = page.path;
 
-        const localizedPathnames = getPagePathnameAcrossLanguages({
-          defaultLanguage,
-          languages,
-          pagePath,
-          urls,
-        });
-
         const pathname = getPagePathname({
           baseDirectory,
           pagePath,
@@ -480,14 +470,9 @@ async function writeFavicon({
 }
 
 async function writeSharedFilesAndPages({
-  language,
   outputPath,
-  urls,
-  wikiData,
   wikiDataJSON,
 }) {
-  const {groupData, wikiInfo} = wikiData;
-
   return progressPromiseAll(`Writing files & pages shared across languages.`, [
     wikiDataJSON &&
       writeFile(
diff --git a/tap-snapshots/test/snapshot/generateAdditionalFilesList.js.test.cjs b/tap-snapshots/test/snapshot/generateAdditionalFilesList.js.test.cjs
index 5ca63482..42a409ad 100644
--- a/tap-snapshots/test/snapshot/generateAdditionalFilesList.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateAdditionalFilesList.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateAdditionalFilesList.js TAP generateAdditionalFilesList (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/generateAdditionalFilesList.js > TAP > generateAdditionalFilesList (snapshot) > basic behavior 1`] = `
 <dl>
     <dt>SBURB Wallpaper</dt>
     <dd>
@@ -24,6 +24,6 @@ exports[`test/snapshot/generateAdditionalFilesList.js TAP generateAdditionalFile
 </dl>
 `
 
-exports[`test/snapshot/generateAdditionalFilesList.js TAP generateAdditionalFilesList (snapshot) > no additional files 1`] = `
+exports[`test/snapshot/generateAdditionalFilesList.js > TAP > generateAdditionalFilesList (snapshot) > no additional files 1`] = `
 
 `
diff --git a/tap-snapshots/test/snapshot/generateAdditionalFilesShortcut.js.test.cjs b/tap-snapshots/test/snapshot/generateAdditionalFilesShortcut.js.test.cjs
index c94371ca..e166140a 100644
--- a/tap-snapshots/test/snapshot/generateAdditionalFilesShortcut.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateAdditionalFilesShortcut.js.test.cjs
@@ -5,10 +5,10 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateAdditionalFilesShortcut.js TAP generateAdditionalFilesShortcut (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/generateAdditionalFilesShortcut.js > TAP > generateAdditionalFilesShortcut (snapshot) > basic behavior 1`] = `
 View <a href="#additional-files">additional files</a>: SBURB Wallpaper, Alternate Covers
 `
 
-exports[`test/snapshot/generateAdditionalFilesShortcut.js TAP generateAdditionalFilesShortcut (snapshot) > no additional files 1`] = `
+exports[`test/snapshot/generateAdditionalFilesShortcut.js > TAP > generateAdditionalFilesShortcut (snapshot) > no additional files 1`] = `
 
 `
diff --git a/tap-snapshots/test/snapshot/generateAlbumBanner.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumBanner.js.test.cjs
index 28eaf6d3..c900eb44 100644
--- a/tap-snapshots/test/snapshot/generateAlbumBanner.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateAlbumBanner.js.test.cjs
@@ -5,14 +5,14 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateAlbumBanner.js TAP generateAlbumBanner (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/generateAlbumBanner.js > TAP > generateAlbumBanner (snapshot) > basic behavior 1`] = `
 <div id="banner"><img src="media/album-art/cool-album/banner.png" alt="album banner" width="800" height="200"></div>
 `
 
-exports[`test/snapshot/generateAlbumBanner.js TAP generateAlbumBanner (snapshot) > no banner 1`] = `
+exports[`test/snapshot/generateAlbumBanner.js > TAP > generateAlbumBanner (snapshot) > no banner 1`] = `
 
 `
 
-exports[`test/snapshot/generateAlbumBanner.js TAP generateAlbumBanner (snapshot) > no dimensions 1`] = `
+exports[`test/snapshot/generateAlbumBanner.js > TAP > generateAlbumBanner (snapshot) > no dimensions 1`] = `
 <div id="banner"><img src="media/album-art/cool-album/banner.png" alt="album banner" width="1100" height="200"></div>
 `
diff --git a/tap-snapshots/test/snapshot/generateAlbumCoverArtwork.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumCoverArtwork.js.test.cjs
index 017ab0e4..2c679fc8 100644
--- a/tap-snapshots/test/snapshot/generateAlbumCoverArtwork.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateAlbumCoverArtwork.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateAlbumCoverArtwork.js TAP generateAlbumCoverArtwork (snapshot) > display: primary 1`] = `
+exports[`test/snapshot/generateAlbumCoverArtwork.js > TAP > generateAlbumCoverArtwork (snapshot) > display: primary 1`] = `
 <div id="cover-art-container">
     [mocked: image
      args: [
@@ -21,7 +21,7 @@ exports[`test/snapshot/generateAlbumCoverArtwork.js TAP generateAlbumCoverArtwor
 </div>
 `
 
-exports[`test/snapshot/generateAlbumCoverArtwork.js TAP generateAlbumCoverArtwork (snapshot) > display: thumbnail 1`] = `
+exports[`test/snapshot/generateAlbumCoverArtwork.js > TAP > generateAlbumCoverArtwork (snapshot) > display: thumbnail 1`] = `
 [mocked: image
  args: [
    [
diff --git a/tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs
index e769026f..9702cad8 100644
--- a/tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateAlbumReleaseInfo.js.test.cjs
@@ -5,11 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateAlbumReleaseInfo.js TAP generateAlbumReleaseInfo (snapshot) > URLs only 1`] = `
-<p>Listen on <a href="https://homestuck.bandcamp.com/foo" class="nowrap">Bandcamp</a> or <a href="https://soundcloud.com/bar" class="nowrap">SoundCloud</a>.</p>
-`
-
-exports[`test/snapshot/generateAlbumReleaseInfo.js TAP generateAlbumReleaseInfo (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/generateAlbumReleaseInfo.js > TAP > generateAlbumReleaseInfo (snapshot) > basic behavior 1`] = `
 <p>
     By <span class="nowrap"><a href="artist/toby-fox/">Toby Fox</a> (music probably)</span> and <span class="nowrap"><a href="artist/tensei/">Tensei</a> (hot jams) (<span class="icons"><a href="https://tenseimusic.bandcamp.com/" class="icon">
                 <svg>
@@ -33,10 +29,14 @@ exports[`test/snapshot/generateAlbumReleaseInfo.js TAP generateAlbumReleaseInfo
 <p>Listen on <a href="https://homestuck.bandcamp.com/album/alterniabound-with-alternia" class="nowrap">Bandcamp</a>, <a href="https://www.youtube.com/playlist?list=PLnVpmehyaOFZWO9QOZmD6A3TIK0wZ6xE2" class="nowrap">YouTube (playlist)</a>, or <a href="https://www.youtube.com/watch?v=HO5V2uogkYc" class="nowrap">YouTube (full album)</a>.</p>
 `
 
-exports[`test/snapshot/generateAlbumReleaseInfo.js TAP generateAlbumReleaseInfo (snapshot) > equal cover art date 1`] = `
+exports[`test/snapshot/generateAlbumReleaseInfo.js > TAP > generateAlbumReleaseInfo (snapshot) > equal cover art date 1`] = `
 <p>Released 4/12/2020.</p>
 `
 
-exports[`test/snapshot/generateAlbumReleaseInfo.js TAP generateAlbumReleaseInfo (snapshot) > reduced details 1`] = `
+exports[`test/snapshot/generateAlbumReleaseInfo.js > TAP > generateAlbumReleaseInfo (snapshot) > reduced details 1`] = `
 
 `
+
+exports[`test/snapshot/generateAlbumReleaseInfo.js > TAP > generateAlbumReleaseInfo (snapshot) > URLs only 1`] = `
+<p>Listen on <a href="https://homestuck.bandcamp.com/foo" class="nowrap">Bandcamp</a> or <a href="https://soundcloud.com/bar" class="nowrap">SoundCloud</a>.</p>
+`
diff --git a/tap-snapshots/test/snapshot/generateAlbumSecondaryNav.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumSecondaryNav.js.test.cjs
index 032fdc05..29d06283 100644
--- a/tap-snapshots/test/snapshot/generateAlbumSecondaryNav.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateAlbumSecondaryNav.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateAlbumSecondaryNav.js TAP generateAlbumSecondaryNav (snapshot) > basic behavior, mode: album 1`] = `
+exports[`test/snapshot/generateAlbumSecondaryNav.js > TAP > generateAlbumSecondaryNav (snapshot) > basic behavior, mode: album 1`] = `
 <nav id="secondary-nav" class="nav-links-groups">
     <span style="--primary-color: #abcdef; --dark-color: #21272e; --dim-color: #818181; --dim-ghost-color: #818181cc; --bg-color: #161616cc; --bg-black-color: #06090bcc; --shadow-color: #0d0d0dcc">
         <a href="group/vcg/">VCG</a>
@@ -18,14 +18,14 @@ exports[`test/snapshot/generateAlbumSecondaryNav.js TAP generateAlbumSecondaryNa
 </nav>
 `
 
-exports[`test/snapshot/generateAlbumSecondaryNav.js TAP generateAlbumSecondaryNav (snapshot) > basic behavior, mode: track 1`] = `
+exports[`test/snapshot/generateAlbumSecondaryNav.js > TAP > generateAlbumSecondaryNav (snapshot) > basic behavior, mode: track 1`] = `
 <nav id="secondary-nav" class="nav-links-groups">
     <a href="group/vcg/" style="--primary-color: #abcdef; --dim-color: #818181">VCG</a>
     <a href="group/bepis/" style="--primary-color: #123456; --dim-color: #000000">Bepis</a>
 </nav>
 `
 
-exports[`test/snapshot/generateAlbumSecondaryNav.js TAP generateAlbumSecondaryNav (snapshot) > dateless album in mixed group 1`] = `
+exports[`test/snapshot/generateAlbumSecondaryNav.js > TAP > generateAlbumSecondaryNav (snapshot) > dateless album in mixed group 1`] = `
 <nav id="secondary-nav" class="nav-links-groups">
     <a href="group/vcg/" style="--primary-color: #abcdef; --dim-color: #818181">VCG</a>
     <a href="group/bepis/" style="--primary-color: #123456; --dim-color: #000000">Bepis</a>
diff --git a/tap-snapshots/test/snapshot/generateAlbumSidebarGroupBox.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumSidebarGroupBox.js.test.cjs
index 3be84969..cd820cde 100644
--- a/tap-snapshots/test/snapshot/generateAlbumSidebarGroupBox.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateAlbumSidebarGroupBox.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateAlbumSidebarGroupBox.js TAP generateAlbumSidebarGroupBox (snapshot) > basic behavior, mode: album 1`] = `
+exports[`test/snapshot/generateAlbumSidebarGroupBox.js > TAP > generateAlbumSidebarGroupBox (snapshot) > basic behavior, mode: album 1`] = `
 <h1><a href="group/vcg/">VCG</a></h1>
 Very cool group.
 <p>Visit on <a href="https://vcg.bandcamp.com/" class="nowrap">Bandcamp</a> or <a href="https://youtube.com/@vcg" class="nowrap">YouTube</a>.</p>
@@ -13,12 +13,12 @@ Very cool group.
 <p class="group-chronology-link">Previous: <a href="album/first/">First</a></p>
 `
 
-exports[`test/snapshot/generateAlbumSidebarGroupBox.js TAP generateAlbumSidebarGroupBox (snapshot) > basic behavior, mode: track 1`] = `
+exports[`test/snapshot/generateAlbumSidebarGroupBox.js > TAP > generateAlbumSidebarGroupBox (snapshot) > basic behavior, mode: track 1`] = `
 <h1><a href="group/vcg/">VCG</a></h1>
 <p>Visit on <a href="https://vcg.bandcamp.com/" class="nowrap">Bandcamp</a> or <a href="https://youtube.com/@vcg" class="nowrap">YouTube</a>.</p>
 `
 
-exports[`test/snapshot/generateAlbumSidebarGroupBox.js TAP generateAlbumSidebarGroupBox (snapshot) > dateless album in mixed group 1`] = `
+exports[`test/snapshot/generateAlbumSidebarGroupBox.js > TAP > generateAlbumSidebarGroupBox (snapshot) > dateless album in mixed group 1`] = `
 <h1><a href="group/vcg/">VCG</a></h1>
 Very cool group.
 <p>Visit on <a href="https://vcg.bandcamp.com/" class="nowrap">Bandcamp</a> or <a href="https://youtube.com/@vcg" class="nowrap">YouTube</a>.</p>
diff --git a/tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs b/tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs
index 59eb4456..304717d1 100644
--- a/tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateAlbumTrackList.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateAlbumTrackList.js TAP generateAlbumTrackList (snapshot) > basic behavior, default track section 1`] = `
+exports[`test/snapshot/generateAlbumTrackList.js > TAP > generateAlbumTrackList (snapshot) > basic behavior, default track section 1`] = `
 <ul>
     <li>(0:20) <a href="track/t1/">Track 1</a></li>
     <li>(0:30) <a href="track/t2/">Track 2</a></li>
@@ -14,7 +14,7 @@ exports[`test/snapshot/generateAlbumTrackList.js TAP generateAlbumTrackList (sna
 </ul>
 `
 
-exports[`test/snapshot/generateAlbumTrackList.js TAP generateAlbumTrackList (snapshot) > basic behavior, with track sections 1`] = `
+exports[`test/snapshot/generateAlbumTrackList.js > TAP > generateAlbumTrackList (snapshot) > basic behavior, with track sections 1`] = `
 <dl class="album-group-list">
     <dt class="content-heading" tabindex="0">First section (~1:30):</dt>
     <dd>
diff --git a/tap-snapshots/test/snapshot/generateBanner.js.test.cjs b/tap-snapshots/test/snapshot/generateBanner.js.test.cjs
index 24e49603..bf2c03c3 100644
--- a/tap-snapshots/test/snapshot/generateBanner.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateBanner.js.test.cjs
@@ -5,10 +5,10 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateBanner.js TAP generateBanner (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/generateBanner.js > TAP > generateBanner (snapshot) > basic behavior 1`] = `
 <div id="banner"><img src="media/album-art/cool-album/banner.png" alt="Very cool banner art." width="800" height="200"></div>
 `
 
-exports[`test/snapshot/generateBanner.js TAP generateBanner (snapshot) > no dimensions 1`] = `
+exports[`test/snapshot/generateBanner.js > TAP > generateBanner (snapshot) > no dimensions 1`] = `
 <div id="banner"><img src="media/album-art/cool-album/banner.png" width="1100" height="200"></div>
 `
diff --git a/tap-snapshots/test/snapshot/generateCoverArtwork.js.test.cjs b/tap-snapshots/test/snapshot/generateCoverArtwork.js.test.cjs
index c1c880bc..bc1432de 100644
--- a/tap-snapshots/test/snapshot/generateCoverArtwork.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateCoverArtwork.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateCoverArtwork.js TAP generateCoverArtwork (snapshot) > display: primary 1`] = `
+exports[`test/snapshot/generateCoverArtwork.js > TAP > generateCoverArtwork (snapshot) > display: primary 1`] = `
 <div id="cover-art-container">
     [mocked: image
      args: [
@@ -21,7 +21,7 @@ exports[`test/snapshot/generateCoverArtwork.js TAP generateCoverArtwork (snapsho
 </div>
 `
 
-exports[`test/snapshot/generateCoverArtwork.js TAP generateCoverArtwork (snapshot) > display: thumbnail 1`] = `
+exports[`test/snapshot/generateCoverArtwork.js > TAP > generateCoverArtwork (snapshot) > display: thumbnail 1`] = `
 [mocked: image
  args: [
    [
diff --git a/tap-snapshots/test/snapshot/generatePreviousNextLinks.js.test.cjs b/tap-snapshots/test/snapshot/generatePreviousNextLinks.js.test.cjs
index 8171725b..ed7b882a 100644
--- a/tap-snapshots/test/snapshot/generatePreviousNextLinks.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generatePreviousNextLinks.js.test.cjs
@@ -5,24 +5,24 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generatePreviousNextLinks.js TAP generatePreviousNextLinks (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/generatePreviousNextLinks.js > TAP > generatePreviousNextLinks (snapshot) > basic behavior 1`] = `
 previous: { tooltip: true, color: false, attributes: { id: 'previous-button' }, content: Tag (no name, 1 items) }
 next: { tooltip: true, color: false, attributes: { id: 'next-button' }, content: Tag (no name, 1 items) }
 `
 
-exports[`test/snapshot/generatePreviousNextLinks.js TAP generatePreviousNextLinks (snapshot) > disable id 1`] = `
+exports[`test/snapshot/generatePreviousNextLinks.js > TAP > generatePreviousNextLinks (snapshot) > disable id 1`] = `
 previous: { tooltip: true, color: false, attributes: { id: false }, content: Tag (no name, 1 items) }
 next: { tooltip: true, color: false, attributes: { id: false }, content: Tag (no name, 1 items) }
 `
 
-exports[`test/snapshot/generatePreviousNextLinks.js TAP generatePreviousNextLinks (snapshot) > neither link present 1`] = `
+exports[`test/snapshot/generatePreviousNextLinks.js > TAP > generatePreviousNextLinks (snapshot) > neither link present 1`] = `
 
 `
 
-exports[`test/snapshot/generatePreviousNextLinks.js TAP generatePreviousNextLinks (snapshot) > next missing 1`] = `
+exports[`test/snapshot/generatePreviousNextLinks.js > TAP > generatePreviousNextLinks (snapshot) > next missing 1`] = `
 previous: { tooltip: true, color: false, attributes: { id: 'previous-button' }, content: Tag (no name, 1 items) }
 `
 
-exports[`test/snapshot/generatePreviousNextLinks.js TAP generatePreviousNextLinks (snapshot) > previous missing 1`] = `
+exports[`test/snapshot/generatePreviousNextLinks.js > TAP > generatePreviousNextLinks (snapshot) > previous missing 1`] = `
 next: { tooltip: true, color: false, attributes: { id: 'next-button' }, content: Tag (no name, 1 items) }
 `
diff --git a/tap-snapshots/test/snapshot/generateTrackCoverArtwork.js.test.cjs b/tap-snapshots/test/snapshot/generateTrackCoverArtwork.js.test.cjs
index 33b5d155..78063c48 100644
--- a/tap-snapshots/test/snapshot/generateTrackCoverArtwork.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateTrackCoverArtwork.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateTrackCoverArtwork.js TAP generateTrackCoverArtwork (snapshot) > display: primary - no unique art 1`] = `
+exports[`test/snapshot/generateTrackCoverArtwork.js > TAP > generateTrackCoverArtwork (snapshot) > display: primary - no unique art 1`] = `
 <div id="cover-art-container">
     [mocked: image
      args: [
@@ -21,7 +21,7 @@ exports[`test/snapshot/generateTrackCoverArtwork.js TAP generateTrackCoverArtwor
 </div>
 `
 
-exports[`test/snapshot/generateTrackCoverArtwork.js TAP generateTrackCoverArtwork (snapshot) > display: primary - unique art 1`] = `
+exports[`test/snapshot/generateTrackCoverArtwork.js > TAP > generateTrackCoverArtwork (snapshot) > display: primary - unique art 1`] = `
 <div id="cover-art-container">
     [mocked: image
      args: [ [ { name: 'Bees', directory: 'bees', isContentWarning: false } ] ]
@@ -30,7 +30,7 @@ exports[`test/snapshot/generateTrackCoverArtwork.js TAP generateTrackCoverArtwor
 </div>
 `
 
-exports[`test/snapshot/generateTrackCoverArtwork.js TAP generateTrackCoverArtwork (snapshot) > display: thumbnail - no unique art 1`] = `
+exports[`test/snapshot/generateTrackCoverArtwork.js > TAP > generateTrackCoverArtwork (snapshot) > display: thumbnail - no unique art 1`] = `
 [mocked: image
  args: [
    [
@@ -43,7 +43,7 @@ exports[`test/snapshot/generateTrackCoverArtwork.js TAP generateTrackCoverArtwor
  slots: { path: [ 'media.albumCover', 'bee-forus-seatbelt-safebee', 'png' ], thumb: 'small', reveal: false, link: false, square: true }]
 `
 
-exports[`test/snapshot/generateTrackCoverArtwork.js TAP generateTrackCoverArtwork (snapshot) > display: thumbnail - unique art 1`] = `
+exports[`test/snapshot/generateTrackCoverArtwork.js > TAP > generateTrackCoverArtwork (snapshot) > display: thumbnail - unique art 1`] = `
 [mocked: image
  args: [ [ { name: 'Bees', directory: 'bees', isContentWarning: false } ] ]
  slots: { path: [ 'media.trackCover', 'bee-forus-seatbelt-safebee', 'beesmp3', 'jpg' ], thumb: 'small', reveal: false, link: false, square: true }]
diff --git a/tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs b/tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs
index bfd7446a..2add28ed 100644
--- a/tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs
+++ b/tap-snapshots/test/snapshot/generateTrackReleaseInfo.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/generateTrackReleaseInfo.js TAP generateTrackReleaseInfo (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseInfo (snapshot) > basic behavior 1`] = `
 <p>
     By <a href="artist/toby-fox/">Toby Fox</a>.
     <br>
@@ -16,12 +16,12 @@ exports[`test/snapshot/generateTrackReleaseInfo.js TAP generateTrackReleaseInfo
 <p>Listen on <a href="https://soundcloud.com/foo" class="nowrap">SoundCloud</a> or <a href="https://youtube.com/watch?v=bar" class="nowrap">YouTube</a>.</p>
 `
 
-exports[`test/snapshot/generateTrackReleaseInfo.js TAP generateTrackReleaseInfo (snapshot) > cover artist contribs, non-unique 1`] = `
+exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseInfo (snapshot) > cover artist contribs, non-unique 1`] = `
 <p>By <a href="artist/toby-fox/">Toby Fox</a>.</p>
 <p>This wiki doesn't have any listening links for <i>Suspicious Track</i>.</p>
 `
 
-exports[`test/snapshot/generateTrackReleaseInfo.js TAP generateTrackReleaseInfo (snapshot) > cover artist contribs, unique 1`] = `
+exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseInfo (snapshot) > cover artist contribs, unique 1`] = `
 <p>
     By <a href="artist/toby-fox/">Toby Fox</a>.
     <br>
@@ -30,7 +30,7 @@ exports[`test/snapshot/generateTrackReleaseInfo.js TAP generateTrackReleaseInfo
 <p>This wiki doesn't have any listening links for <i>Suspicious Track</i>.</p>
 `
 
-exports[`test/snapshot/generateTrackReleaseInfo.js TAP generateTrackReleaseInfo (snapshot) > reduced details 1`] = `
+exports[`test/snapshot/generateTrackReleaseInfo.js > TAP > generateTrackReleaseInfo (snapshot) > reduced details 1`] = `
 <p>By <a href="artist/toby-fox/">Toby Fox</a>.</p>
 <p>This wiki doesn't have any listening links for <i>Suspicious Track</i>.</p>
 `
diff --git a/tap-snapshots/test/snapshot/image.js.test.cjs b/tap-snapshots/test/snapshot/image.js.test.cjs
index 3cd1a317..f88141d5 100644
--- a/tap-snapshots/test/snapshot/image.js.test.cjs
+++ b/tap-snapshots/test/snapshot/image.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/image.js TAP image (snapshot) > content warnings via tags 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > content warnings via tags 1`] = `
 <div class="reveal">
     <div class="image-container"><div class="image-inner-area"><img src="media/album-art/beyond-canon/cover.png"></div></div>
     <span class="reveal-text-container">
@@ -18,59 +18,59 @@ exports[`test/snapshot/image.js TAP image (snapshot) > content warnings via tags
 </div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > id with link 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > id with link 1`] = `
 <a id="banana" class="box image-link" href="foobar"><div class="image-container"><div class="image-inner-area"><img src="foobar"></div></div></a>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > id with square 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > id with square 1`] = `
 <div class="square"><div class="square-content"><div class="image-container"><div class="image-inner-area"><img id="banana" src="foobar"></div></div></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > id without link 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > id without link 1`] = `
 <div class="image-container"><div class="image-inner-area"><img id="banana" src="foobar"></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > lazy with square 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > lazy with square 1`] = `
 <noscript><div class="square"><div class="square-content"><div class="image-container"><div class="image-inner-area"><img src="foobar"></div></div></div></div></noscript>
 <div class="square js-hide"><div class="square-content"><div class="image-container"><div class="image-inner-area"><img class="lazy" data-original="foobar"></div></div></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > link with file size 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > link with file size 1`] = `
 <a class="box image-link" href="media/album-art/pingas/cover.png"><div class="image-container"><div class="image-inner-area"><img src="media/album-art/pingas/cover.png"></div></div></a>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > missing image path 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > missing image path 1`] = `
 <div class="image-container"><div class="image-inner-area"><div class="image-text-area">(This image file is missing)</div></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > missing image path w/ missingSourceContent 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > missing image path w/ missingSourceContent 1`] = `
 <div class="image-container"><div class="image-inner-area"><div class="image-text-area">Cover's missing, whoops</div></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > source missing 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > source missing 1`] = `
 <div class="image-container placeholder-image"><div class="image-inner-area"><div class="image-text-area">Example of missing source message.</div></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > source via path 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > source via path 1`] = `
 <div class="image-container"><div class="image-inner-area"><img src="media/album-art/beyond-canon/cover.png"></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > source via src 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > source via src 1`] = `
 <div class="image-container"><div class="image-inner-area"><img src="https://example.com/bananas.gif"></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > square 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > square 1`] = `
 <div class="square"><div class="square-content"><div class="image-container"><div class="image-inner-area"><img src="foobar"></div></div></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > thumb requested but source is gif 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > thumb requested but source is gif 1`] = `
 <div class="image-container"><div class="image-inner-area"><img src="media/flash-art/5426.gif"></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > thumbnail details 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > thumbnail details 1`] = `
 <div class="image-container"><div class="image-inner-area"><img data-original-length="1200" data-thumbs="voluminous:1200 middling:900 petite:20" src="media/album-art/beyond-canon/cover.voluminous.jpg"></div></div>
 `
 
-exports[`test/snapshot/image.js TAP image (snapshot) > width & height 1`] = `
+exports[`test/snapshot/image.js > TAP > image (snapshot) > width & height 1`] = `
 <div class="image-container"><div class="image-inner-area"><img width="600" height="400" src="foobar"></div></div>
 `
diff --git a/tap-snapshots/test/snapshot/linkArtist.js.test.cjs b/tap-snapshots/test/snapshot/linkArtist.js.test.cjs
index 77516f3f..6b532aef 100644
--- a/tap-snapshots/test/snapshot/linkArtist.js.test.cjs
+++ b/tap-snapshots/test/snapshot/linkArtist.js.test.cjs
@@ -5,10 +5,10 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/linkArtist.js TAP linkArtist (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/linkArtist.js > TAP > linkArtist (snapshot) > basic behavior 1`] = `
 <a href="artist/toby-fox/">Toby Fox</a>
 `
 
-exports[`test/snapshot/linkArtist.js TAP linkArtist (snapshot) > prefer short name 1`] = `
+exports[`test/snapshot/linkArtist.js > TAP > linkArtist (snapshot) > prefer short name 1`] = `
 <a href="artist/55gore/">55gore</a>
 `
diff --git a/tap-snapshots/test/snapshot/linkContribution.js.test.cjs b/tap-snapshots/test/snapshot/linkContribution.js.test.cjs
index b845a336..75b9d273 100644
--- a/tap-snapshots/test/snapshot/linkContribution.js.test.cjs
+++ b/tap-snapshots/test/snapshot/linkContribution.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > loads of links 1`] = `
+exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > loads of links 1`] = `
 <span class="nowrap"><a href="artist/lorem-ipsum-lover/">Lorem Ipsum Lover</a> (<span class="icons"><a href="https://loremipsum.io" class="icon">
             <svg>
                 <title>External (loremipsum.io)</title>
@@ -29,13 +29,13 @@ exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > loa
         </a></span>)</span>
 `
 
-exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > no accents 1`] = `
+exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > no accents 1`] = `
 <a href="artist/clark-powell/">Clark Powell</a>
 <a href="artist/the-big-baddies/">Grounder &amp; Scratch</a>
 <a href="artist/toby-fox/">Toby Fox</a>
 `
 
-exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > no preventWrapping 1`] = `
+exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > no preventWrapping 1`] = `
 <a href="artist/clark-powell/">Clark Powell</a> (<span class="icons"><a href="https://soundcloud.com/plazmataz" class="icon">
         <svg>
             <title>SoundCloud</title>
@@ -56,13 +56,13 @@ exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > no
     </a></span>)
 `
 
-exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > only showContribution 1`] = `
+exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > only showContribution 1`] = `
 <a href="artist/clark-powell/">Clark Powell</a>
 <span class="nowrap"><a href="artist/the-big-baddies/">Grounder &amp; Scratch</a> (Snooping)</span>
 <span class="nowrap"><a href="artist/toby-fox/">Toby Fox</a> (Arrangement)</span>
 `
 
-exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > only showIcons 1`] = `
+exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > only showIcons 1`] = `
 <span class="nowrap"><a href="artist/clark-powell/">Clark Powell</a> (<span class="icons"><a href="https://soundcloud.com/plazmataz" class="icon">
             <svg>
                 <title>SoundCloud</title>
@@ -83,7 +83,7 @@ exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > onl
         </a></span>)</span>
 `
 
-exports[`test/snapshot/linkContribution.js TAP linkContribution (snapshot) > showContribution & showIcons 1`] = `
+exports[`test/snapshot/linkContribution.js > TAP > linkContribution (snapshot) > showContribution & showIcons 1`] = `
 <span class="nowrap"><a href="artist/clark-powell/">Clark Powell</a> (<span class="icons"><a href="https://soundcloud.com/plazmataz" class="icon">
             <svg>
                 <title>SoundCloud</title>
diff --git a/tap-snapshots/test/snapshot/linkExternal.js.test.cjs b/tap-snapshots/test/snapshot/linkExternal.js.test.cjs
index 156b7f90..cd6dca76 100644
--- a/tap-snapshots/test/snapshot/linkExternal.js.test.cjs
+++ b/tap-snapshots/test/snapshot/linkExternal.js.test.cjs
@@ -5,7 +5,7 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > basic domain matches 1`] = `
+exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > basic domain matches 1`] = `
 <a href="https://homestuck.bandcamp.com/" class="nowrap">Bandcamp</a>
 <a href="https://soundcloud.com/plazmataz" class="nowrap">SoundCloud</a>
 <a href="https://aeritus.tumblr.com/" class="nowrap">Tumblr</a>
@@ -19,21 +19,21 @@ exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > basic domai
 <a href="https://buzinkai.newgrounds.com/" class="nowrap">Newgrounds</a>
 `
 
-exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > custom domains for common platforms 1`] = `
+exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > custom domains for common platforms 1`] = `
 <a href="https://music.solatrus.com/" class="nowrap">music.solatrus.com</a>
 <a href="https://types.pl/" class="nowrap">Mastodon (types.pl)</a>
 `
 
-exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > custom matches - album 1`] = `
+exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > custom matches - album 1`] = `
 <a href="https://youtu.be/abc" class="nowrap">YouTube (full album)</a>
 <a href="https://youtube.com/watch?v=abc" class="nowrap">YouTube (full album)</a>
 <a href="https://youtube.com/Playlist?list=kweh" class="nowrap">YouTube (playlist)</a>
 `
 
-exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > missing domain (arbitrary local path) 1`] = `
+exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > missing domain (arbitrary local path) 1`] = `
 <a href="/foo/bar/baz.mp3" class="nowrap">Wiki Archive (local upload)</a>
 `
 
-exports[`test/snapshot/linkExternal.js TAP linkExternal (snapshot) > unknown domain (arbitrary world wide web path) 1`] = `
+exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > unknown domain (arbitrary world wide web path) 1`] = `
 <a href="https://snoo.ping.as/usual/i/see/" class="nowrap">snoo.ping.as</a>
 `
diff --git a/tap-snapshots/test/snapshot/linkExternalFlash.js.test.cjs b/tap-snapshots/test/snapshot/linkExternalFlash.js.test.cjs
index d7f6c1c3..d29d0dde 100644
--- a/tap-snapshots/test/snapshot/linkExternalFlash.js.test.cjs
+++ b/tap-snapshots/test/snapshot/linkExternalFlash.js.test.cjs
@@ -5,14 +5,14 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/linkExternalFlash.js TAP linkExternalFlash (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/linkExternalFlash.js > TAP > linkExternalFlash (snapshot) > basic behavior 1`] = `
 <span class="nowrap"><a href="https://homestuck.com/story/4109/" class="nowrap">homestuck.com</a> (page 4109)</span>
 <span class="nowrap"><a href="https://youtu.be/FDt-SLyEcjI" class="nowrap">YouTube</a> (on any device)</span>
 <span class="nowrap"><a href="https://www.bgreco.net/hsflash/006009.html" class="nowrap">www.bgreco.net</a> (HQ Audio)</span>
 <span class="nowrap"><a href="https://www.newgrounds.com/portal/view/582345" class="nowrap">Newgrounds</a></span>
 `
 
-exports[`test/snapshot/linkExternalFlash.js TAP linkExternalFlash (snapshot) > secret page 1`] = `
+exports[`test/snapshot/linkExternalFlash.js > TAP > linkExternalFlash (snapshot) > secret page 1`] = `
 <span class="nowrap"><a href="https://homestuck.com/story/pony/" class="nowrap">homestuck.com</a> (secret page)</span>
 <span class="nowrap"><a href="https://youtu.be/USB1pj6hAjU" class="nowrap">YouTube</a> (on any device)</span>
 `
diff --git a/tap-snapshots/test/snapshot/linkTemplate.js.test.cjs b/tap-snapshots/test/snapshot/linkTemplate.js.test.cjs
index 45a9e616..0d9ef77b 100644
--- a/tap-snapshots/test/snapshot/linkTemplate.js.test.cjs
+++ b/tap-snapshots/test/snapshot/linkTemplate.js.test.cjs
@@ -5,15 +5,15 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/linkTemplate.js TAP linkTemplate (snapshot) > fill many slots 1`] = `
+exports[`test/snapshot/linkTemplate.js > TAP > linkTemplate (snapshot) > fill many slots 1`] = `
 <a class="dog" id="cat1" href="https://hsmusic.wiki/media/cool%20file.pdf#fooey" style="--primary-color: #123456ff; --dim-color: #12345677">My Cool Link</a>
 `
 
-exports[`test/snapshot/linkTemplate.js TAP linkTemplate (snapshot) > fill path slot & provide appendIndexHTML 1`] = `
+exports[`test/snapshot/linkTemplate.js > TAP > linkTemplate (snapshot) > fill path slot & provide appendIndexHTML 1`] = `
 <a href="/c*lzone/myCoolPath/ham/pineapple/tomato/index.html">delish</a>
 `
 
-exports[`test/snapshot/linkTemplate.js TAP linkTemplate (snapshot) > link in content 1`] = `
+exports[`test/snapshot/linkTemplate.js > TAP > linkTemplate (snapshot) > link in content 1`] = `
 <a href="#the-more-ye-know">
     Oh geez oh heck
     There's a link in here!!
@@ -23,10 +23,10 @@ exports[`test/snapshot/linkTemplate.js TAP linkTemplate (snapshot) > link in con
 </a>
 `
 
-exports[`test/snapshot/linkTemplate.js TAP linkTemplate (snapshot) > missing content 1`] = `
+exports[`test/snapshot/linkTemplate.js > TAP > linkTemplate (snapshot) > missing content 1`] = `
 <a href="banana">(Missing link content)</a>
 `
 
-exports[`test/snapshot/linkTemplate.js TAP linkTemplate (snapshot) > special characters in path argument 1`] = `
+exports[`test/snapshot/linkTemplate.js > TAP > linkTemplate (snapshot) > special characters in path argument 1`] = `
 <a href="media/album-additional/homestuck-vol-1/Showtime%20(Piano%20Refrain)%20-%20%23xXxAwesomeSheetMusick%3FrxXx%23.pdf">Damn, that's some good sheet music</a>
 `
diff --git a/tap-snapshots/test/snapshot/linkThing.js.test.cjs b/tap-snapshots/test/snapshot/linkThing.js.test.cjs
index 1a98cfdd..5a5b2516 100644
--- a/tap-snapshots/test/snapshot/linkThing.js.test.cjs
+++ b/tap-snapshots/test/snapshot/linkThing.js.test.cjs
@@ -5,32 +5,32 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > basic behavior 1`] = `
+exports[`test/snapshot/linkThing.js > TAP > linkThing (snapshot) > basic behavior 1`] = `
 <a href="track/foo/" style="--primary-color: #abcdef; --dim-color: #818181">Cool track!</a>
 `
 
-exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > color 1`] = `
+exports[`test/snapshot/linkThing.js > TAP > linkThing (snapshot) > color 1`] = `
 <a href="track/showtime-piano-refrain/">Showtime (Piano Refrain)</a>
 <a href="track/showtime-piano-refrain/" style="--primary-color: #38f43d; --dim-color: #389f33">Showtime (Piano Refrain)</a>
 <a href="track/showtime-piano-refrain/" style="--primary-color: #aaccff; --dim-color: #828282">Showtime (Piano Refrain)</a>
 `
 
-exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > nested links in content stripped 1`] = `
+exports[`test/snapshot/linkThing.js > TAP > linkThing (snapshot) > nested links in content stripped 1`] = `
 <a href="foo/"><b>Oooo! Very spooky.</b></a>
 `
 
-exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > preferShortName 1`] = `
+exports[`test/snapshot/linkThing.js > TAP > linkThing (snapshot) > preferShortName 1`] = `
 <a href="tag/five-oceanfalls/">Five</a>
 `
 
-exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > tags in name escaped 1`] = `
+exports[`test/snapshot/linkThing.js > TAP > linkThing (snapshot) > tags in name escaped 1`] = `
 <a href="track/foo/">&lt;a href=&quot;SNOOPING&quot;&gt;AS USUAL&lt;/a&gt; I SEE</a>
 <a href="track/bar/">&lt;b&gt;boldface&lt;/b&gt;</a>
 <a href="album/exile/">&gt;Exile&lt;</a>
 <a href="track/heart/">&lt;3</a>
 `
 
-exports[`test/snapshot/linkThing.js TAP linkThing (snapshot) > tooltip & content 1`] = `
+exports[`test/snapshot/linkThing.js > TAP > linkThing (snapshot) > tooltip & content 1`] = `
 <a href="album/beyond-canon/">Beyond Canon</a>
 <a href="album/beyond-canon/" title="Beyond Canon">Beyond Canon</a>
 <a href="album/beyond-canon/" title="Beyond Canon">Next</a>
diff --git a/tap-snapshots/test/snapshot/transformContent.js.test.cjs b/tap-snapshots/test/snapshot/transformContent.js.test.cjs
index 4af6b147..85ee740f 100644
--- a/tap-snapshots/test/snapshot/transformContent.js.test.cjs
+++ b/tap-snapshots/test/snapshot/transformContent.js.test.cjs
@@ -5,12 +5,12 @@
  * Make sure to inspect the output below.  Do not ignore changes!
  */
 'use strict'
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > dates 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > dates 1`] = `
 <p><time datetime="Thu, 13 Apr 2023 00:00:00 GMT">4/12/2023</time> Yep!</p>
 <p>Very nice: <time datetime="Fri, 25 Oct 2413 03:00:00 GMT">10/25/2413</time></p>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > inline images 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > inline images 1`] = `
 <p><img src="snooping.png"> as USUAL...</p>
 <p>What do you know? <img src="cowabunga.png" width="24" height="32"></p>
 <p><a href="to-localized.album/cool-album" style="--primary-color: #123456; --dim-color: #000000">I'm on the left.</a><img src="im-on-the-right.jpg"></p>
@@ -20,12 +20,12 @@ exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > inl
 <p>And... all done! <img src="end-of-source.png"></p>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > links to a thing 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > links to a thing 1`] = `
 <p>This is <a href="to-localized.album/cool-album" style="--primary-color: #123456; --dim-color: #000000">my favorite album</a>.</p>
 <p>That&#39;s right, <a href="to-localized.album/cool-album" style="--primary-color: #123456; --dim-color: #000000">Cool Album</a>!</p>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > lyrics - basic line breaks 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > lyrics - basic line breaks 1`] = `
 <p>Hey, ho<br>
 And away we go<br>
 Truly, music</p>
@@ -33,7 +33,7 @@ Truly, music</p>
 (That&#39;s right)</p>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > lyrics - line breaks around tags 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > lyrics - line breaks around tags 1`] = `
 <p>The date be <time datetime="Tue, 13 Apr 2004 03:00:00 GMT">4/13/2004</time><br>
 I say, the date be <time datetime="Tue, 13 Apr 2004 03:00:00 GMT">4/13/2004</time><br>
 <time datetime="Tue, 13 Apr 2004 03:00:00 GMT">4/13/2004</time><br>
@@ -46,31 +46,31 @@ I say, the date be <time datetime="Tue, 13 Apr 2004 03:00:00 GMT">4/13/2004</tim
 <time datetime="Tue, 13 Apr 2004 03:00:00 GMT">4/13/2004</time>, and don&#39;t ye forget it</p>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > lyrics - repeated and edge line breaks 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > lyrics - repeated and edge line breaks 1`] = `
 <p>Well, you know<br>
 How it goes</p>
 <p>Yessiree</p>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > non-inline image #1 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > non-inline image #1 1`] = `
 <div class="content-image">[mocked: image - slots: { src: 'spark.png', link: true, thumb: 'large' }]</div>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > non-inline image #2 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > non-inline image #2 1`] = `
 <p>Rad.</p>
 <div class="content-image">[mocked: image - slots: { src: 'spark.png', link: true, thumb: 'large' }]</div>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > non-inline image #3 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > non-inline image #3 1`] = `
 <div class="content-image">[mocked: image - slots: { src: 'spark.png', link: true, thumb: 'large' }]</div>
 <p>Baller.</p>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > super basic string 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > super basic string 1`] = `
 <p>Neat listing: Albums - by Date</p>
 `
 
-exports[`test/snapshot/transformContent.js TAP transformContent (snapshot) > two text paragraphs 1`] = `
+exports[`test/snapshot/transformContent.js > TAP > transformContent (snapshot) > two text paragraphs 1`] = `
 <p>Hello, world!</p>
 <p>Wow, this is very cool.</p>
 `
diff --git a/test/lib/index.js b/test/lib/index.js
index b9cc82f8..5fb5bf78 100644
--- a/test/lib/index.js
+++ b/test/lib/index.js
@@ -1,3 +1,6 @@
+Error.stackTraceLimit = Infinity;
+
 export * from './content-function.js';
 export * from './generic-mock.js';
+export * from './wiki-data.js';
 export * from './strict-match-error.js';
diff --git a/test/lib/wiki-data.js b/test/lib/wiki-data.js
new file mode 100644
index 00000000..c4083a56
--- /dev/null
+++ b/test/lib/wiki-data.js
@@ -0,0 +1,24 @@
+import {linkWikiDataArrays} from '#yaml';
+
+export function linkAndBindWikiData(wikiData) {
+  linkWikiDataArrays(wikiData);
+
+  return {
+    // Mutate to make the below functions aware of new data objects, or of
+    // reordering the existing ones. Don't mutate arrays such as trackData
+    // in-place; assign completely new arrays to this wikiData object instead.
+    wikiData,
+
+    // Use this after you've mutated wikiData to assign new data arrays.
+    // It'll automatically relink everything on wikiData so all the objects
+    // are caught up to date.
+    linkWikiDataArrays:
+      linkWikiDataArrays.bind(null, wikiData),
+
+    // Use this if you HAVEN'T mutated wikiData and just need to decache
+    // indirect dependencies on exposed properties of other data objects.
+    // See documentation on linkWikiDataArarys (in yaml.js) for more info.
+    XXX_decacheWikiData:
+      linkWikiDataArrays.bind(null, wikiData, {XXX_decacheWikiData: true}),
+  };
+}
diff --git a/test/unit/data/things/cacheable-object.js b/test/unit/data/cacheable-object.js
index 2e82af08..2e82af08 100644
--- a/test/unit/data/things/cacheable-object.js
+++ b/test/unit/data/cacheable-object.js
diff --git a/test/unit/data/composite/control-flow/exposeConstant.js b/test/unit/data/composite/control-flow/exposeConstant.js
new file mode 100644
index 00000000..0c75894b
--- /dev/null
+++ b/test/unit/data/composite/control-flow/exposeConstant.js
@@ -0,0 +1,42 @@
+import t from 'tap';
+
+import {compositeFrom, continuationSymbol, input} from '#composite';
+import {exposeConstant} from '#composite/control-flow';
+
+t.test(`exposeConstant: basic behavior`, t => {
+  t.plan(2);
+
+  const composite1 = compositeFrom({
+    compose: false,
+
+    steps: [
+      exposeConstant({
+        value: input.value('foo'),
+      }),
+    ],
+  });
+
+  t.match(composite1, {
+    expose: {
+      dependencies: [],
+    },
+  });
+
+  t.equal(composite1.expose.compute(), 'foo');
+});
+
+t.test(`exposeConstant: validate inputs`, t => {
+  t.plan(2);
+
+  t.throws(
+    () => exposeConstant({}),
+    {message: `Errors in input options passed to exposeConstant`, errors: [
+      {message: `Required these inputs: value`},
+    ]});
+
+  t.throws(
+    () => exposeConstant({value: 'some dependency'}),
+    {message: `Errors in input options passed to exposeConstant`, errors: [
+      {message: `value: Expected input.value() call, got dependency name`},
+    ]});
+});
diff --git a/test/unit/data/composite/control-flow/exposeDependency.js b/test/unit/data/composite/control-flow/exposeDependency.js
new file mode 100644
index 00000000..8f6bfd01
--- /dev/null
+++ b/test/unit/data/composite/control-flow/exposeDependency.js
@@ -0,0 +1,64 @@
+import t from 'tap';
+
+import {compositeFrom, continuationSymbol, input} from '#composite';
+import {exposeDependency} from '#composite/control-flow';
+
+t.test(`exposeDependency: basic behavior`, t => {
+  t.plan(4);
+
+  const composite1 = compositeFrom({
+    compose: false,
+
+    steps: [
+      exposeDependency({dependency: 'foo'}),
+    ],
+  });
+
+  t.match(composite1, {
+    expose: {
+      dependencies: ['foo'],
+    },
+  });
+
+  t.equal(composite1.expose.compute({foo: 'bar'}), 'bar');
+
+  const composite2 = compositeFrom({
+    compose: false,
+
+    steps: [
+      {
+        dependencies: ['foo'],
+        compute: (continuation, {foo}) =>
+          continuation({'#bar': foo.toUpperCase()}),
+      },
+
+      exposeDependency({dependency: '#bar'}),
+    ],
+  });
+
+  t.match(composite2, {
+    expose: {
+      dependencies: ['foo'],
+    },
+  });
+
+  t.equal(composite2.expose.compute({foo: 'bar'}), 'BAR');
+});
+
+t.test(`exposeDependency: validate inputs`, t => {
+  t.plan(2);
+
+  t.throws(
+    () => exposeDependency({}),
+    {message: `Errors in input options passed to exposeDependency`, errors: [
+      {message: `Required these inputs: dependency`},
+    ]});
+
+  t.throws(
+    () => exposeDependency({
+      dependency: input.value('some static value'),
+    }),
+    {message: `Errors in input options passed to exposeDependency`, errors: [
+      {message: `dependency: Expected dependency name, got input.value() call`},
+    ]});
+});
diff --git a/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js b/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js
new file mode 100644
index 00000000..4c4be04a
--- /dev/null
+++ b/test/unit/data/composite/control-flow/withResultOfAvailabilityCheck.js
@@ -0,0 +1,173 @@
+import t from 'tap';
+
+import {compositeFrom, continuationSymbol, input} from '#composite';
+import {withResultOfAvailabilityCheck} from '#composite/control-flow';
+
+const composite = compositeFrom({
+  compose: false,
+
+  steps: [
+    withResultOfAvailabilityCheck({
+      from: 'from',
+      mode: 'mode',
+    }).outputs({
+      ['#availability']: '#result',
+    }),
+
+    {
+      dependencies: ['#result'],
+      compute: ({'#result': result}) => result,
+    },
+  ],
+});
+
+t.test(`withResultOfAvailabilityCheck: basic behavior`, t => {
+  t.plan(1);
+
+  t.match(composite, {
+    expose: {
+      dependencies: ['from', 'mode'],
+    },
+  });
+});
+
+const quickCompare = (t, expect, {from, mode}) =>
+  t.equal(composite.expose.compute({from, mode}), expect);
+
+const quickThrows = (t, {from, mode}) =>
+  t.throws(() => composite.expose.compute({from, mode}));
+
+t.test(`withResultOfAvailabilityCheck: mode = null`, t => {
+  t.plan(10);
+
+  quickCompare(t, true,  {mode: 'null', from: 'truthy string'});
+  quickCompare(t, true,  {mode: 'null', from: 123});
+  quickCompare(t, true,  {mode: 'null', from: true});
+
+  quickCompare(t, true,  {mode: 'null', from: ''});
+  quickCompare(t, true,  {mode: 'null', from: 0});
+  quickCompare(t, true,  {mode: 'null', from: false});
+
+  quickCompare(t, true,  {mode: 'null', from: [1, 2, 3]});
+  quickCompare(t, true,  {mode: 'null', from: []});
+
+  quickCompare(t, false, {mode: 'null', from: null});
+  quickCompare(t, false, {mode: 'null', from: undefined});
+});
+
+t.test(`withResultOfAvailabilityCheck: mode = empty`, t => {
+  t.plan(10);
+
+  quickThrows(t, {mode: 'empty', from: 'truthy string'});
+  quickThrows(t, {mode: 'empty', from: 123});
+  quickThrows(t, {mode: 'empty', from: true});
+
+  quickThrows(t, {mode: 'empty', from: ''});
+  quickThrows(t, {mode: 'empty', from: 0});
+  quickThrows(t, {mode: 'empty', from: false});
+
+  quickCompare(t, true,  {mode: 'empty', from: [1, 2, 3]});
+  quickCompare(t, false, {mode: 'empty', from: []});
+
+  quickCompare(t, false, {mode: 'empty', from: null});
+  quickCompare(t, false, {mode: 'empty', from: undefined});
+});
+
+t.test(`withResultOfAvailabilityCheck: mode = falsy`, t => {
+  t.plan(10);
+
+  quickCompare(t, true,  {mode: 'falsy', from: 'truthy string'});
+  quickCompare(t, true,  {mode: 'falsy', from: 123});
+  quickCompare(t, true,  {mode: 'falsy', from: true});
+
+  quickCompare(t, false, {mode: 'falsy', from: ''});
+  quickCompare(t, false, {mode: 'falsy', from: 0});
+  quickCompare(t, false, {mode: 'falsy', from: false});
+
+  quickCompare(t, true,  {mode: 'falsy', from: [1, 2, 3]});
+  quickCompare(t, false, {mode: 'falsy', from: []});
+
+  quickCompare(t, false, {mode: 'falsy', from: null});
+  quickCompare(t, false, {mode: 'falsy', from: undefined});
+});
+
+t.test(`withResultOfAvailabilityCheck: default mode`, t => {
+  t.plan(1);
+
+  const template = withResultOfAvailabilityCheck({
+    from: 'foo',
+  });
+
+  t.match(template.toDescription(), {
+    inputMapping: {
+      from: input.dependency('foo'),
+      mode: input.value('null'),
+    },
+  });
+});
+
+t.test(`withResultOfAvailabilityCheck: validate static inputs`, t => {
+  t.plan(5);
+
+  t.throws(
+    () => withResultOfAvailabilityCheck({}),
+    {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [
+      {message: `Required these inputs: from`},
+    ]});
+
+  t.doesNotThrow(() =>
+    withResultOfAvailabilityCheck({
+      from: 'dependency1',
+      mode: 'dependency2',
+    }));
+
+  t.doesNotThrow(() =>
+    withResultOfAvailabilityCheck({
+      from: input.value('some static value'),
+      mode: input.value('null'),
+    }));
+
+  t.throws(
+    () => withResultOfAvailabilityCheck({
+      from: 'foo',
+      mode: input.value('invalid'),
+    }),
+    {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [
+      {message: `mode: Expected one of null empty falsy, got invalid`},
+    ]});
+
+  t.throws(() =>
+    withResultOfAvailabilityCheck({
+      from: input.value(null),
+      mode: input.value(null),
+    }),
+    {message: `Errors in input options passed to withResultOfAvailabilityCheck`, errors: [
+      {message: `mode: Expected a value, got null`},
+    ]});
+});
+
+t.test(`withResultOfAvailabilityCheck: validate dynamic inputs`, t => {
+  t.plan(2);
+
+  t.throws(
+    () => composite.expose.compute({
+      from: 'apple',
+      mode: 'banana',
+    }),
+    {message: `Error computing composition`, cause:
+      {message: `Error computing composition withResultOfAvailabilityCheck`, cause:
+        {message: `Errors in input values provided to withResultOfAvailabilityCheck`, errors: [
+          {message: `mode: Expected one of null empty falsy, got banana`},
+        ]}}});
+
+  t.throws(
+    () => composite.expose.compute({
+      from: null,
+      mode: null,
+    }),
+    {message: `Error computing composition`, cause:
+      {message: `Error computing composition withResultOfAvailabilityCheck`, cause:
+        {message: `Errors in input values provided to withResultOfAvailabilityCheck`, errors: [
+          {message: `mode: Expected a value, got null`},
+        ]}}});
+});
diff --git a/test/unit/data/composite/data/withPropertiesFromObject.js b/test/unit/data/composite/data/withPropertiesFromObject.js
new file mode 100644
index 00000000..ead1b9b2
--- /dev/null
+++ b/test/unit/data/composite/data/withPropertiesFromObject.js
@@ -0,0 +1,248 @@
+import t from 'tap';
+
+import {compositeFrom, input} from '#composite';
+import {exposeDependency} from '#composite/control-flow';
+import {withPropertiesFromObject} from '#composite/data';
+
+const composite = compositeFrom({
+  compose: false,
+
+  steps: [
+    withPropertiesFromObject({
+      object: 'object',
+      properties: 'properties',
+    }),
+
+    exposeDependency({dependency: '#object'}),
+  ],
+});
+
+t.test(`withPropertiesFromObject: basic behavior`, t => {
+  t.plan(4);
+
+  t.match(composite, {
+    expose: {
+      dependencies: ['object', 'properties'],
+    },
+  });
+
+  t.same(
+    composite.expose.compute({
+      object: {foo: 'bar', bim: 'BOOM', bam: 'baz'},
+      properties: ['foo', 'bim'],
+    }),
+    {foo: 'bar', bim: 'BOOM'});
+
+  t.same(
+    composite.expose.compute({
+      object: {value1: 'uwah', value2: 'arah'},
+      properties: ['value1', 'value3'],
+    }),
+    {value1: 'uwah', value3: null});
+
+  t.same(
+    composite.expose.compute({
+      object: null,
+      properties: ['ohMe', 'ohMy', 'ohDear'],
+    }),
+    {ohMe: null, ohMy: null, ohDear: null});
+});
+
+t.test(`withPropertiesFromObject: output shapes & values`, t => {
+  t.plan(2 * 2 * 3 ** 2);
+
+  const dependencies = {
+    ['object_dependency']:
+      {foo: 'apple', bar: 'banana', baz: 'orange'},
+    [input('object_neither')]:
+      {foo: 'koala', bar: 'okapi', baz: 'mongoose'},
+    ['properties_dependency']:
+      ['foo', 'bar', 'missing1'],
+    [input('properties_neither')]:
+      ['foo', 'baz', 'missing3'],
+  };
+
+  const mapLevel1 = [
+    [input.value('prefix_value'), [
+      ['object_dependency', [
+        ['properties_dependency', {
+          '#object': {foo: 'apple', bar: 'banana', missing1: null},
+        }],
+        [input.value(['bar', 'baz', 'missing2']), {
+          '#prefix_value.bar': 'banana',
+          '#prefix_value.baz': 'orange',
+          '#prefix_value.missing2': null,
+        }],
+        [input('properties_neither'), {
+          '#object': {foo: 'apple', baz: 'orange', missing3: null},
+        }]]],
+
+      [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [
+        ['properties_dependency', {
+          '#object': {foo: 'ouh', bar: 'rah', missing1: null},
+        }],
+        [input.value(['bar', 'baz', 'missing2']), {
+          '#prefix_value.bar': 'rah',
+          '#prefix_value.baz': 'nyu',
+          '#prefix_value.missing2': null,
+        }],
+        [input('properties_neither'), {
+          '#object': {foo: 'ouh', baz: 'nyu', missing3: null},
+        }]]],
+
+      [input('object_neither'), [
+        ['properties_dependency', {
+          '#object': {foo: 'koala', bar: 'okapi', missing1: null},
+        }],
+        [input.value(['bar', 'baz', 'missing2']), {
+          '#prefix_value.bar': 'okapi',
+          '#prefix_value.baz': 'mongoose',
+          '#prefix_value.missing2': null,
+        }],
+        [input('properties_neither'), {
+          '#object': {foo: 'koala', baz: 'mongoose', missing3: null},
+        }]]]]],
+
+    [input.value(null), [
+      ['object_dependency', [
+        ['properties_dependency', {
+          '#object': {foo: 'apple', bar: 'banana', missing1: null},
+        }],
+        [input.value(['bar', 'baz', 'missing2']), {
+          '#object_dependency.bar': 'banana',
+          '#object_dependency.baz': 'orange',
+          '#object_dependency.missing2': null,
+        }],
+        [input('properties_neither'), {
+          '#object': {foo: 'apple', baz: 'orange', missing3: null},
+        }]]],
+
+      [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [
+        ['properties_dependency', {
+          '#object': {foo: 'ouh', bar: 'rah', missing1: null},
+        }],
+        [input.value(['bar', 'baz', 'missing2']), {
+          '#object.bar': 'rah',
+          '#object.baz': 'nyu',
+          '#object.missing2': null,
+        }],
+        [input('properties_neither'), {
+          '#object': {foo: 'ouh', baz: 'nyu', missing3: null},
+        }]]],
+
+      [input('object_neither'), [
+        ['properties_dependency', {
+          '#object': {foo: 'koala', bar: 'okapi', missing1: null},
+        }],
+        [input.value(['bar', 'baz', 'missing2']), {
+          '#object.bar': 'okapi',
+          '#object.baz': 'mongoose',
+          '#object.missing2': null,
+        }],
+        [input('properties_neither'), {
+          '#object': {foo: 'koala', baz: 'mongoose', missing3: null},
+        }]]]]],
+  ];
+
+  for (const [prefixInput, mapLevel2] of mapLevel1) {
+    for (const [objectInput, mapLevel3] of mapLevel2) {
+      for (const [propertiesInput, outputDict] of mapLevel3) {
+        const step = withPropertiesFromObject({
+          prefix: prefixInput,
+          object: objectInput,
+          properties: propertiesInput,
+        });
+
+        quickCheckOutputs(step, outputDict);
+      }
+    }
+  }
+
+  function quickCheckOutputs(step, outputDict) {
+    t.same(
+      Object.keys(step.toDescription().outputs),
+      Object.keys(outputDict));
+
+    const composite = compositeFrom({
+      compose: false,
+      steps: [step, {
+        dependencies: Object.keys(outputDict),
+        compute: dependencies => dependencies,
+      }],
+    });
+
+    t.same(
+      composite.expose.compute(dependencies),
+      outputDict);
+  }
+});
+
+t.test(`withPropertiesFromObject: validate static inputs`, t => {
+  t.plan(3);
+
+  t.throws(
+    () => withPropertiesFromObject({}),
+    {message: `Errors in input options passed to withPropertiesFromObject`, errors: [
+      {message: `Required these inputs: object, properties`},
+    ]});
+
+  t.throws(
+    () => withPropertiesFromObject({
+      object: input.value('intriguing'),
+      properties: input.value('very'),
+      prefix: input.value({yes: 'yup'}),
+    }),
+    {message: `Errors in input options passed to withPropertiesFromObject`, errors: [
+      {message: `object: Expected an object, got string`},
+      {message: `properties: Expected an array, got string`},
+      {message: `prefix: Expected a string, got object`},
+    ]});
+
+  t.throws(
+    () => withPropertiesFromObject({
+      object: input.value([['abc', 1], ['def', 2], [123, 3]]),
+      properties: input.value(['abc', 'def', 123]),
+    }),
+    {message: `Errors in input options passed to withPropertiesFromObject`, errors: [
+      {message: `object: Expected an object, got array`},
+      {message: `properties: Errors validating array items`, errors: [
+        {
+          [Symbol.for('hsmusic.decorate.indexInSourceArray')]: 2,
+          message: /Expected a string, got number/,
+        },
+      ]},
+    ]});
+});
+
+t.test(`withPropertiesFromObject: validate dynamic inputs`, t => {
+  t.plan(2);
+
+  t.throws(
+    () => composite.expose.compute({
+      object: 'intriguing',
+      properties: 'onceMore',
+    }),
+    {message: `Error computing composition`, cause:
+      {message: `Error computing composition withPropertiesFromObject`, cause:
+        {message: `Errors in input values provided to withPropertiesFromObject`, errors: [
+          {message: `object: Expected an object, got string`},
+          {message: `properties: Expected an array, got string`},
+        ]}}});
+
+  t.throws(
+    () => composite.expose.compute({
+      object: [['abc', 1], ['def', 2], [123, 3]],
+      properties: ['abc', 'def', 123],
+    }),
+    {message: `Error computing composition`, cause:
+      {message: `Error computing composition withPropertiesFromObject`, cause:
+        {message: `Errors in input values provided to withPropertiesFromObject`, errors: [
+          {message: `object: Expected an object, got array`},
+          {message: `properties: Errors validating array items`, errors: [
+            {
+              [Symbol.for('hsmusic.decorate.indexInSourceArray')]: 2,
+              message: /Expected a string, got number/,
+            },
+          ]},
+        ]}}});
+});
diff --git a/test/unit/data/composite/data/withPropertyFromObject.js b/test/unit/data/composite/data/withPropertyFromObject.js
new file mode 100644
index 00000000..6a772c36
--- /dev/null
+++ b/test/unit/data/composite/data/withPropertyFromObject.js
@@ -0,0 +1,122 @@
+import t from 'tap';
+
+import {compositeFrom, input} from '#composite';
+import {exposeDependency} from '#composite/control-flow';
+import {withPropertyFromObject} from '#composite/data';
+
+t.test(`withPropertyFromObject: basic behavior`, t => {
+  t.plan(4);
+
+  const composite = compositeFrom({
+    compose: false,
+
+    steps: [
+      withPropertyFromObject({
+        object: 'object',
+        property: 'property',
+      }),
+
+      exposeDependency({dependency: '#value'}),
+    ],
+  });
+
+  t.match(composite, {
+    expose: {
+      dependencies: ['object', 'property'],
+    },
+  });
+
+  t.equal(composite.expose.compute({
+    object: {foo: 'bar', bim: 'BOOM'},
+    property: 'bim',
+  }), 'BOOM');
+
+  t.equal(composite.expose.compute({
+    object: {value1: 'uwah'},
+    property: 'value2',
+  }), null);
+
+  t.equal(composite.expose.compute({
+    object: null,
+    property: 'oml where did me object go',
+  }), null);
+});
+
+t.test(`withPropertyFromObject: output shapes & values`, t => {
+  t.plan(2 * 3 ** 2);
+
+  const dependencies = {
+    ['object_dependency']:
+      {foo: 'apple', bar: 'banana', baz: 'orange'},
+    [input('object_neither')]:
+      {foo: 'koala', bar: 'okapi', baz: 'mongoose'},
+    ['property_dependency']:
+      'foo',
+    [input('property_neither')]:
+      'baz',
+  };
+
+  const mapLevel1 = [
+    ['object_dependency', [
+      ['property_dependency', {
+        '#value': 'apple',
+      }],
+      [input.value('bar'), {
+        '#object_dependency.bar': 'banana',
+      }],
+      [input('property_neither'), {
+        '#value': 'orange',
+      }]]],
+
+    [input.value({foo: 'ouh', bar: 'rah', baz: 'nyu'}), [
+      ['property_dependency', {
+        '#value': 'ouh',
+      }],
+      [input.value('bar'), {
+        '#value': 'rah',
+      }],
+      [input('property_neither'), {
+        '#value': 'nyu',
+      }]]],
+
+    [input('object_neither'), [
+      ['property_dependency', {
+        '#value': 'koala',
+      }],
+      [input.value('bar'), {
+        '#value': 'okapi',
+      }],
+      [input('property_neither'), {
+        '#value': 'mongoose',
+      }]]],
+  ];
+
+  for (const [objectInput, mapLevel2] of mapLevel1) {
+    for (const [propertyInput, outputDict] of mapLevel2) {
+      const step = withPropertyFromObject({
+        object: objectInput,
+        property: propertyInput,
+      });
+
+      quickCheckOutputs(step, outputDict);
+    }
+  }
+
+  function quickCheckOutputs(step, outputDict) {
+    t.same(
+      Object.keys(step.toDescription().outputs),
+      Object.keys(outputDict));
+
+    const composite = compositeFrom({
+      compose: false,
+      steps: [step, {
+        dependencies: Object.keys(outputDict),
+        compute: dependencies => dependencies,
+      }],
+    });
+
+    t.same(
+      composite.expose.compute(dependencies),
+      outputDict);
+  }
+});
diff --git a/test/unit/data/compositeFrom.js b/test/unit/data/compositeFrom.js
new file mode 100644
index 00000000..00296675
--- /dev/null
+++ b/test/unit/data/compositeFrom.js
@@ -0,0 +1,345 @@
+import t from 'tap';
+
+import {compositeFrom, continuationSymbol, input} from '#composite';
+import {isString} from '#validators';
+
+t.test(`compositeFrom: basic behavior`, t => {
+  t.plan(2);
+
+  const composite = compositeFrom({
+    annotation: `myComposite`,
+    compose: false,
+
+    steps: [
+      {
+        dependencies: ['foo'],
+        compute: (continuation, {foo}) =>
+          continuation({'#bar': foo * 2}),
+      },
+
+      {
+        dependencies: ['#bar', 'baz', 'suffix'],
+        compute: ({'#bar': bar, baz, suffix}) =>
+          baz.repeat(bar) + suffix,
+      },
+    ],
+  });
+
+  t.match(composite, {
+    annotation: `myComposite`,
+
+    flags: {expose: true, compose: false, update: false},
+
+    expose: {
+      dependencies: ['foo', 'baz', 'suffix'],
+      compute: Function,
+      transform: null,
+    },
+
+    update: null,
+  });
+
+  t.equal(
+    composite.expose.compute({
+      foo: 3,
+      baz: 'ba',
+      suffix: 'BOOM',
+    }),
+    'babababababaBOOM');
+});
+
+t.test(`compositeFrom: input-shaped step dependencies`, t => {
+  t.plan(2);
+
+  const composite = compositeFrom({
+    compose: false,
+    steps: [
+      {
+        dependencies: [
+          input.myself(),
+          input.updateValue(),
+        ],
+
+        transform: (updateValue1, {
+          [input.myself()]: me,
+          [input.updateValue()]: updateValue2,
+        }) => ({me, updateValue1, updateValue2}),
+      },
+    ],
+  });
+
+  t.match(composite, {
+    expose: {
+      dependencies: ['this'],
+      transform: Function,
+      compute: null,
+    },
+  });
+
+  const myself = {foo: 'bar'};
+
+  t.same(
+    composite.expose.transform('banana', {
+      this: myself,
+      pomelo: 'delicious',
+    }),
+    {
+      me: myself,
+      updateValue1: 'banana',
+      updateValue2: 'banana',
+    });
+});
+
+t.test(`compositeFrom: dependencies from inputs`, t => {
+  t.plan(3);
+
+  const composite = compositeFrom({
+    annotation: `myComposite`,
+
+    compose: true,
+
+    inputMapping: {
+      foo:      input('bar'),
+      pomelo:   input.value('delicious'),
+      humorous: input.dependency('#mammal'),
+      data:     input.dependency('albumData'),
+      ref:      input.updateValue(),
+    },
+
+    inputDescriptions: {
+      foo:      input(),
+      pomelo:   input(),
+      humorous: input(),
+      data:     input(),
+      ref:      input(),
+    },
+
+    steps: [
+      {
+        dependencies: [
+          input('foo'),
+          input('pomelo'),
+          input('humorous'),
+          input('data'),
+          input('ref'),
+        ],
+
+        compute: (continuation, {
+          [input('foo')]: foo,
+          [input('pomelo')]: pomelo,
+          [input('humorous')]: humorous,
+          [input('data')]: data,
+          [input('ref')]: ref,
+        }) => continuation.exit({foo, pomelo, humorous, data, ref}),
+      },
+    ],
+  });
+
+  t.match(composite, {
+    expose: {
+      dependencies: [
+        input('bar'),
+        '#mammal',
+        'albumData',
+      ],
+
+      transform: Function,
+      compute: null,
+    },
+  });
+
+  const exitData = {};
+  const continuation = {
+    exit(value) {
+      Object.assign(exitData, value);
+      return continuationSymbol;
+    },
+  };
+
+  t.equal(
+    composite.expose.transform('album:bepis', continuation, {
+      [input('bar')]: 'squid time',
+      '#mammal': 'fox',
+      'albumData': ['album1', 'album2'],
+    }),
+    continuationSymbol);
+
+  t.same(exitData, {
+    foo: 'squid time',
+    pomelo: 'delicious',
+    humorous: 'fox',
+    data: ['album1', 'album2'],
+    ref: 'album:bepis',
+  });
+});
+
+t.test(`compositeFrom: update from various sources`, t => {
+  t.plan(3);
+
+  const match = {
+    flags: {update: true, expose: true, compose: false},
+
+    update: {
+      validate: isString,
+      default: 'foo',
+    },
+
+    expose: {
+      transform: Function,
+      compute: null,
+    },
+  };
+
+  t.test(`compositeFrom: update from composition description`, t => {
+    t.plan(2);
+
+    const composite = compositeFrom({
+      compose: false,
+
+      update: {
+        validate: isString,
+        default: 'foo',
+      },
+
+      steps: [
+        {transform: (value, continuation) => continuation(value.repeat(2))},
+        {transform: (value) => `Xx_${value}_xX`},
+      ],
+    });
+
+    t.match(composite, match);
+    t.equal(composite.expose.transform('foo'), `Xx_foofoo_xX`);
+  });
+
+  t.test(`compositeFrom: update from step dependencies`, t => {
+    t.plan(2);
+
+    const composite = compositeFrom({
+      compose: false,
+
+      steps: [
+        {
+          dependencies: [
+            input.updateValue({
+              validate: isString,
+              default: 'foo',
+            }),
+          ],
+
+          compute: ({
+            [input.updateValue()]: value,
+          }) => `Xx_${value.repeat(2)}_xX`,
+        },
+      ],
+    });
+
+    t.match(composite, match);
+    t.equal(composite.expose.transform('foo'), 'Xx_foofoo_xX');
+  });
+
+  t.test(`compositeFrom: update from inputs`, t => {
+    t.plan(3);
+
+    const composite = compositeFrom({
+      inputMapping: {
+        myInput: input.updateValue({
+          validate: isString,
+          default: 'foo',
+        }),
+      },
+
+      inputDescriptions: {
+        myInput: input(),
+      },
+
+      steps: [
+        {
+          dependencies: [input('myInput')],
+          compute: (continuation, {
+            [input('myInput')]: value,
+          }) => continuation({
+            '#value': `Xx_${value.repeat(2)}_xX`,
+          }),
+        },
+
+        {
+          dependencies: ['#value'],
+          transform: (_value, continuation, {'#value': value}) =>
+            continuation(value),
+        },
+      ],
+    });
+
+    let continuationValue = null;
+    const continuation = value => {
+      continuationValue = value;
+      return continuationSymbol;
+    };
+
+    t.match(composite, {
+      ...match,
+
+      flags: {update: true, expose: true, compose: true},
+    });
+
+    t.equal(
+      composite.expose.transform('foo', continuation),
+      continuationSymbol);
+
+    t.equal(continuationValue, 'Xx_foofoo_xX');
+  });
+});
+
+t.test(`compositeFrom: dynamic input validation from type`, t => {
+  t.plan(2);
+
+  const composite = compositeFrom({
+    inputMapping: {
+      string:   input('string'),
+      number:   input('number'),
+      boolean:  input('boolean'),
+      function: input('function'),
+      object:   input('object'),
+      array:    input('array'),
+    },
+
+    inputDescriptions: {
+      string:   input({null: true, type: 'string'}),
+      number:   input({null: true, type: 'number'}),
+      boolean:  input({null: true, type: 'boolean'}),
+      function: input({null: true, type: 'function'}),
+      object:   input({null: true, type: 'object'}),
+      array:    input({null: true, type: 'array'}),
+    },
+
+    outputs: {'#result': '#result'},
+
+    steps: [
+      {compute: continuation => continuation({'#result': 'OK'})},
+    ],
+  });
+
+  const notCalledSymbol = Symbol('continuation not called');
+
+  let continuationValue;
+  const continuation = value => {
+    continuationValue = value;
+    return continuationSymbol;
+  };
+
+  let thrownError;
+
+  try {
+    continuationValue = notCalledSymbol;
+    thrownError = null;
+    composite.expose.compute(continuation, {
+      [input('string')]: 123,
+    });
+  } catch (error) {
+    thrownError = error;
+  }
+
+  t.equal(continuationValue, notCalledSymbol);
+  t.match(thrownError, {
+  });
+});
diff --git a/test/unit/data/templateCompositeFrom.js b/test/unit/data/templateCompositeFrom.js
new file mode 100644
index 00000000..2de18730
--- /dev/null
+++ b/test/unit/data/templateCompositeFrom.js
@@ -0,0 +1,209 @@
+import t from 'tap';
+
+import {isString} from '#validators';
+
+import {
+  compositeFrom,
+  continuationSymbol,
+  input,
+  templateCompositeFrom,
+} from '#composite';
+
+t.test(`templateCompositeFrom: basic behavior`, t => {
+  t.plan(1);
+
+  const myCoolUtility = templateCompositeFrom({
+    annotation: `myCoolUtility`,
+
+    inputs: {
+      foo: input(),
+    },
+
+    outputs: ['#bar'],
+
+    steps: () => [
+      {
+        dependencies: [input('foo')],
+        compute: (continuation, {
+          [input('foo')]: foo,
+        }) => continuation({
+          ['#bar']: (typeof foo).toUpperCase()
+        }),
+      },
+    ],
+  });
+
+  const instantiatedTemplate = myCoolUtility({
+    foo: 'color',
+  });
+
+  t.match(instantiatedTemplate.toDescription(), {
+    annotation: `myCoolUtility`,
+
+    inputMapping: {
+      foo: input.dependency('color'),
+    },
+
+    inputDescriptions: {
+      foo: input(),
+    },
+
+    outputs: {
+      '#bar': '#bar',
+    },
+
+    steps: Function,
+  });
+});
+
+t.test(`templateCompositeFrom: validate static input values`, t => {
+  t.plan(3);
+
+  const stub = {
+    annotation: 'stubComposite',
+    outputs: ['#result'],
+    steps: () => [{compute: continuation => continuation({'#result': 'OK'})}],
+  };
+
+  const quickThrows = (t, composite, inputOptions, ...errorMessages) =>
+    t.throws(
+      () => composite(inputOptions),
+      {
+        message: `Errors in input options passed to stubComposite`,
+        errors: errorMessages.map(message => ({message})),
+      });
+
+  t.test(`templateCompositeFrom: validate input token shapes`, t => {
+    t.plan(15);
+
+    const template1 = templateCompositeFrom({
+      ...stub, inputs: {
+        foo: input(),
+      },
+    });
+
+    t.doesNotThrow(
+      () => template1({foo: 'dependency'}));
+
+    t.doesNotThrow(
+      () => template1({foo: input.dependency('dependency')}));
+
+    t.doesNotThrow(
+      () => template1({foo: input.value('static value')}));
+
+    t.doesNotThrow(
+      () => template1({foo: input('outerInput')}));
+
+    t.doesNotThrow(
+      () => template1({foo: input.updateValue()}));
+
+    t.doesNotThrow(
+      () => template1({foo: input.myself()}));
+
+    quickThrows(t, template1,
+      {foo: input.staticValue()},
+      `foo: Expected dependency name or value-providing input() call, got input.staticValue`);
+
+    quickThrows(t, template1,
+      {foo: input.staticDependency()},
+      `foo: Expected dependency name or value-providing input() call, got input.staticDependency`);
+
+    const template2 = templateCompositeFrom({
+      ...stub, inputs: {
+        bar: input.staticDependency(),
+      },
+    });
+
+    t.doesNotThrow(
+      () => template2({bar: 'dependency'}));
+
+    t.doesNotThrow(
+      () => template2({bar: input.dependency('dependency')}));
+
+    quickThrows(t, template2,
+      {bar: input.value(123)},
+      `bar: Expected dependency name, got input.value`);
+
+    quickThrows(t, template2,
+      {bar: input('outOfPlace')},
+      `bar: Expected dependency name, got input`);
+
+    const template3 = templateCompositeFrom({
+      ...stub, inputs: {
+        baz: input.staticValue(),
+      },
+    });
+
+    t.doesNotThrow(
+      () => template3({baz: input.value(1025)}));
+
+    quickThrows(t, template3,
+      {baz: 'dependency'},
+      `baz: Expected input.value() call, got dependency name`);
+
+    quickThrows(t, template3,
+      {baz: input('outOfPlace')},
+      `baz: Expected input.value() call, got input() call`);
+  });
+
+  t.test(`templateCompositeFrom: validate missing / misplaced inputs`, t => {
+    t.plan(1);
+
+    const template = templateCompositeFrom({
+      ...stub, inputs: {
+        foo: input(),
+        bar: input(),
+      },
+    });
+
+    t.throws(
+      () => template({
+        baz: 'aeiou',
+        raz: input.value(123),
+      }),
+      {message: `Errors in input options passed to stubComposite`, errors: [
+        {message: `Unexpected input names: baz, raz`},
+        {message: `Required these inputs: foo, bar`},
+      ]});
+  });
+
+  t.test(`templateCompositeFrom: validate acceptsNull / defaultValue: null`, t => {
+    t.plan(3);
+
+    const template1 = templateCompositeFrom({
+      ...stub, inputs: {
+        foo: input(),
+      },
+    });
+
+    t.throws(
+      () => template1({}),
+      {message: `Errors in input options passed to stubComposite`, errors: [
+        {message: `Required these inputs: foo`},
+      ]},
+      `throws if input missing and not marked specially`);
+
+    const template2 = templateCompositeFrom({
+      ...stub, inputs: {
+        bar: input({acceptsNull: true}),
+      },
+    });
+
+    t.throws(
+      () => template2({}),
+      {message: `Errors in input options passed to stubComposite`, errors: [
+        {message: `Required these inputs: bar`},
+      ]},
+      `throws if input missing even if marked {acceptsNull}`);
+
+    const template3 = templateCompositeFrom({
+      ...stub, inputs: {
+        baz: input({defaultValue: null}),
+      },
+    });
+
+    t.doesNotThrow(
+      () => template3({}),
+      `does not throw if input missing if marked {defaultValue: null}`);
+  });
+});
diff --git a/test/unit/data/things/album.js b/test/unit/data/things/album.js
new file mode 100644
index 00000000..76a2b90f
--- /dev/null
+++ b/test/unit/data/things/album.js
@@ -0,0 +1,411 @@
+import t from 'tap';
+
+import {linkAndBindWikiData} from '#test-lib';
+import thingConstructors from '#things';
+
+const {
+  Album,
+  Artist,
+  Track,
+} = thingConstructors;
+
+function stubArtistAndContribs() {
+  const artist = new Artist();
+  artist.name = `Test Artist`;
+
+  const contribs = [{who: `Test Artist`, what: null}];
+  const badContribs = [{who: `Figment of Your Imagination`, what: null}];
+
+  return {artist, contribs, badContribs};
+}
+
+function stubTrack(directory = 'foo') {
+  const track = new Track();
+  track.directory = directory;
+
+  return track;
+}
+
+t.test(`Album.bannerDimensions`, t => {
+  t.plan(4);
+
+  const album = new Album();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+  });
+
+  t.equal(album.bannerDimensions, null,
+    `Album.bannerDimensions #1: defaults to null`);
+
+  album.bannerDimensions = [1200, 275];
+
+  t.equal(album.bannerDimensions, null,
+    `Album.bannerDimensions #2: is null if bannerArtistContribs empty`);
+
+  album.bannerArtistContribs = badContribs;
+
+  t.equal(album.bannerDimensions, null,
+    `Album.bannerDimensions #3: is null if bannerArtistContribs resolves empty`);
+
+  album.bannerArtistContribs = contribs;
+
+  t.same(album.bannerDimensions, [1200, 275],
+    `Album.bannerDimensions #4: is own value`);
+});
+
+t.test(`Album.bannerFileExtension`, t => {
+  t.plan(5);
+
+  const album = new Album();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+  });
+
+  t.equal(album.bannerFileExtension, null,
+    `Album.bannerFileExtension #1: defaults to null`);
+
+  album.bannerFileExtension = 'png';
+
+  t.equal(album.bannerFileExtension, null,
+    `Album.bannerFileExtension #2: is null if bannerArtistContribs empty`);
+
+  album.bannerArtistContribs = badContribs;
+
+  t.equal(album.bannerFileExtension, null,
+    `Album.bannerFileExtension #3: is null if bannerArtistContribs resolves empty`);
+
+  album.bannerArtistContribs = contribs;
+
+  t.equal(album.bannerFileExtension, 'png',
+    `Album.bannerFileExtension #4: is own value`);
+
+  album.bannerFileExtension = null;
+
+  t.equal(album.bannerFileExtension, 'jpg',
+    `Album.bannerFileExtension #5: defaults to jpg`);
+});
+
+t.test(`Album.bannerStyle`, t => {
+  t.plan(4);
+
+  const album = new Album();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+  });
+
+  t.equal(album.bannerStyle, null,
+    `Album.bannerStyle #1: defaults to null`);
+
+  album.bannerStyle = `opacity: 0.5`;
+
+  t.equal(album.bannerStyle, null,
+    `Album.bannerStyle #2: is null if bannerArtistContribs empty`);
+
+  album.bannerArtistContribs = badContribs;
+
+  t.equal(album.bannerStyle, null,
+    `Album.bannerStyle #3: is null if bannerArtistContribs resolves empty`);
+
+  album.bannerArtistContribs = contribs;
+
+  t.equal(album.bannerStyle, `opacity: 0.5`,
+    `Album.bannerStyle #4: is own value`);
+});
+
+t.test(`Album.coverArtDate`, t => {
+  t.plan(6);
+
+  const album = new Album();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+  });
+
+  t.equal(album.coverArtDate, null,
+    `Album.coverArtDate #1: defaults to null`);
+
+  album.date = new Date('2012-10-25');
+
+  t.equal(album.coverArtDate, null,
+    `Album.coverArtDate #2: is null if coverArtistContribs empty (1/2)`);
+
+  album.coverArtDate = new Date('2011-04-13');
+
+  t.equal(album.coverArtDate, null,
+    `Album.coverArtDate #3: is null if coverArtistContribs empty (2/2)`);
+
+  album.coverArtistContribs = contribs;
+
+  t.same(album.coverArtDate, new Date('2011-04-13'),
+    `Album.coverArtDate #4: is own value`);
+
+  album.coverArtDate = null;
+
+  t.same(album.coverArtDate, new Date(`2012-10-25`),
+    `Album.coverArtDate #5: inherits album release date`);
+
+  album.coverArtistContribs = badContribs;
+
+  t.equal(album.coverArtDate, null,
+    `Album.coverArtDate #6: is null if coverArtistContribs resolves empty`);
+});
+
+t.test(`Album.coverArtFileExtension`, t => {
+  t.plan(5);
+
+  const album = new Album();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+  });
+
+  t.equal(album.coverArtFileExtension, null,
+    `Album.coverArtFileExtension #1: is null if coverArtistContribs empty (1/2)`);
+
+  album.coverArtFileExtension = 'png';
+
+  t.equal(album.coverArtFileExtension, null,
+    `Album.coverArtFileExtension #2: is null if coverArtistContribs empty (2/2)`);
+
+  album.coverArtFileExtension = null;
+  album.coverArtistContribs = contribs;
+
+  t.equal(album.coverArtFileExtension, 'jpg',
+    `Album.coverArtFileExtension #3: defaults to jpg`);
+
+  album.coverArtFileExtension = 'png';
+
+  t.equal(album.coverArtFileExtension, 'png',
+    `Album.coverArtFileExtension #4: is own value`);
+
+  album.coverArtistContribs = badContribs;
+
+  t.equal(album.coverArtFileExtension, null,
+    `Album.coverArtFileExtension #5: is null if coverArtistContribs resolves empty`);
+});
+
+t.test(`Album.tracks`, t => {
+  t.plan(5);
+
+  const album = new Album();
+  const track1 = stubTrack('track1');
+  const track2 = stubTrack('track2');
+  const track3 = stubTrack('track3');
+
+  linkAndBindWikiData({
+    albumData: [album],
+    trackData: [track1, track2, track3],
+  });
+
+  t.same(album.tracks, [],
+    `Album.tracks #1: defaults to empty array`);
+
+  album.trackSections = [
+    {tracks: ['track:track1', 'track:track2', 'track:track3']},
+  ];
+
+  t.same(album.tracks, [track1, track2, track3],
+    `Album.tracks #2: pulls tracks from one track section`);
+
+  album.trackSections = [
+    {tracks: ['track:track1']},
+    {tracks: ['track:track2', 'track:track3']},
+  ];
+
+  t.same(album.tracks, [track1, track2, track3],
+    `Album.tracks #3: pulls tracks from multiple track sections`);
+
+  album.trackSections = [
+    {tracks: ['track:track1', 'track:does-not-exist']},
+    {tracks: ['track:this-one-neither', 'track:track2']},
+    {tracks: ['track:effectively-empty-section']},
+    {tracks: ['track:track3']},
+  ];
+
+  t.same(album.tracks, [track1, track2, track3],
+    `Album.tracks #4: filters out references without matches`);
+
+  album.trackSections = [
+    {tracks: ['track:track1']},
+    {},
+    {tracks: ['track:track2']},
+    {},
+    {},
+    {tracks: ['track:track3']},
+  ];
+
+  t.same(album.tracks, [track1, track2, track3],
+    `Album.tracks #5: skips missing tracks property`);
+});
+
+t.test(`Album.trackSections`, t => {
+  t.plan(7);
+
+  const album = new Album();
+  const track1 = stubTrack('track1');
+  const track2 = stubTrack('track2');
+  const track3 = stubTrack('track3');
+  const track4 = stubTrack('track4');
+
+  linkAndBindWikiData({
+    albumData: [album],
+    trackData: [track1, track2, track3, track4],
+  });
+
+  album.trackSections = [
+    {tracks: ['track:track1', 'track:track2']},
+    {tracks: ['track:track3', 'track:track4']},
+  ];
+
+  t.match(album.trackSections, [
+    {tracks: [track1, track2]},
+    {tracks: [track3, track4]},
+  ], `Album.trackSections #1: exposes tracks`);
+
+  t.match(album.trackSections, [
+    {tracks: [track1, track2], startIndex: 0},
+    {tracks: [track3, track4], startIndex: 2},
+  ], `Album.trackSections #2: exposes startIndex`);
+
+  album.trackSections = [
+    {name: 'First section', tracks: ['track:track1']},
+    {name: 'Second section', tracks: ['track:track2']},
+    {tracks: ['track:track3']},
+  ];
+
+  t.match(album.trackSections, [
+    {name: 'First section', tracks: [track1]},
+    {name: 'Second section', tracks: [track2]},
+    {name: 'Unnamed Track Section', tracks: [track3]},
+  ], `Album.trackSections #3: exposes name, with fallback value`);
+
+  album.color = '#123456';
+
+  album.trackSections = [
+    {tracks: ['track:track1'], color: null},
+    {tracks: ['track:track2'], color: '#abcdef'},
+    {tracks: ['track:track3'], color: null},
+  ];
+
+  t.match(album.trackSections, [
+    {tracks: [track1], color: '#123456'},
+    {tracks: [track2], color: '#abcdef'},
+    {tracks: [track3], color: '#123456'},
+  ], `Album.trackSections #4: exposes color, inherited from album`);
+
+  album.trackSections = [
+    {tracks: ['track:track1'], dateOriginallyReleased: null},
+    {tracks: ['track:track2'], dateOriginallyReleased: new Date('2009-04-11')},
+    {tracks: ['track:track3'], dateOriginallyReleased: null},
+  ];
+
+  t.match(album.trackSections, [
+    {tracks: [track1], dateOriginallyReleased: null},
+    {tracks: [track2], dateOriginallyReleased: new Date('2009-04-11')},
+    {tracks: [track3], dateOriginallyReleased: null},
+  ], `Album.trackSections #5: exposes dateOriginallyReleased, if present`);
+
+  album.trackSections = [
+    {tracks: ['track:track1'], isDefaultTrackSection: true},
+    {tracks: ['track:track2'], isDefaultTrackSection: false},
+    {tracks: ['track:track3'], isDefaultTrackSection: null},
+  ];
+
+  t.match(album.trackSections, [
+    {tracks: [track1], isDefaultTrackSection: true},
+    {tracks: [track2], isDefaultTrackSection: false},
+    {tracks: [track3], isDefaultTrackSection: false},
+  ], `Album.trackSections #6: exposes isDefaultTrackSection, defaults to false`);
+
+  album.trackSections = [
+    {tracks: ['track:track1', 'track:track2', 'track:snooping'], color: '#112233'},
+    {tracks: ['track:track3', 'track:as-usual'],                 color: '#334455'},
+    {tracks: [],                                                 color: '#bbbbba'},
+    {tracks: ['track:icy', 'track:chilly', 'track:frigid'],      color: '#556677'},
+    {tracks: ['track:track4'],                                   color: '#778899'},
+  ];
+
+  t.match(album.trackSections, [
+    {tracks: [track1, track2], color: '#112233'},
+    {tracks: [track3],         color: '#334455'},
+    {tracks: [track4],         color: '#778899'},
+  ], `Album.trackSections #7: filters out references without matches & empty sections`);
+});
+
+t.test(`Album.wallpaperFileExtension`, t => {
+  t.plan(5);
+
+  const album = new Album();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+  });
+
+  t.equal(album.wallpaperFileExtension, null,
+    `Album.wallpaperFileExtension #1: defaults to null`);
+
+  album.wallpaperFileExtension = 'png';
+
+  t.equal(album.wallpaperFileExtension, null,
+    `Album.wallpaperFileExtension #2: is null if wallpaperArtistContribs empty`);
+
+  album.wallpaperArtistContribs = contribs;
+
+  t.equal(album.wallpaperFileExtension, 'png',
+    `Album.wallpaperFileExtension #3: is own value`);
+
+  album.wallpaperFileExtension = null;
+
+  t.equal(album.wallpaperFileExtension, 'jpg',
+    `Album.wallpaperFileExtension #4: defaults to jpg`);
+
+  album.wallpaperArtistContribs = badContribs;
+
+  t.equal(album.wallpaperFileExtension, null,
+    `Album.wallpaperFileExtension #5: is null if wallpaperArtistContribs resolves empty`);
+});
+
+t.test(`Album.wallpaperStyle`, t => {
+  t.plan(4);
+
+  const album = new Album();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+  });
+
+  t.equal(album.wallpaperStyle, null,
+    `Album.wallpaperStyle #1: defaults to null`);
+
+  album.wallpaperStyle = `opacity: 0.5`;
+
+  t.equal(album.wallpaperStyle, null,
+    `Album.wallpaperStyle #2: is null if wallpaperArtistContribs empty`);
+
+  album.wallpaperArtistContribs = badContribs;
+
+  t.equal(album.wallpaperStyle, null,
+    `Album.wallpaperStyle #3: is null if wallpaperArtistContribs resolves empty`);
+
+  album.wallpaperArtistContribs = contribs;
+
+  t.equal(album.wallpaperStyle, `opacity: 0.5`,
+    `Album.wallpaperStyle #4: is own value`);
+});
diff --git a/test/unit/data/things/art-tag.js b/test/unit/data/things/art-tag.js
new file mode 100644
index 00000000..561c93ef
--- /dev/null
+++ b/test/unit/data/things/art-tag.js
@@ -0,0 +1,71 @@
+import t from 'tap';
+
+import {linkAndBindWikiData} from '#test-lib';
+import thingConstructors from '#things';
+
+const {
+  Album,
+  Artist,
+  ArtTag,
+  Track,
+} = thingConstructors;
+
+function stubAlbum(tracks, directory = 'bar') {
+  const album = new Album();
+  album.directory = directory;
+
+  const trackRefs = tracks.map(t => Thing.getReference(t));
+  album.trackSections = [{tracks: trackRefs}];
+
+  return album;
+}
+
+function stubTrack(directory = 'foo') {
+  const track = new Track();
+  track.directory = directory;
+
+  return track;
+}
+
+function stubTrackAndAlbum(trackDirectory = 'foo', albumDirectory = 'bar') {
+  const track = stubTrack(trackDirectory);
+  const album = stubAlbum([track], albumDirectory);
+
+  return {track, album};
+}
+
+function stubArtist(artistName = `Test Artist`) {
+  const artist = new Artist();
+  artist.name = artistName;
+
+  return artist;
+}
+
+function stubArtistAndContribs(artistName = `Test Artist`) {
+  const artist = stubArtist(artistName);
+  const contribs = [{who: artistName, what: null}];
+  const badContribs = [{who: `Figment of Your Imagination`, what: null}];
+
+  return {artist, contribs, badContribs};
+}
+
+t.test(`ArtTag.nameShort`, t => {
+  t.plan(3);
+
+  const artTag = new ArtTag();
+
+  artTag.name = `Dave Strider`;
+
+  t.equal(artTag.nameShort, `Dave Strider`,
+    `ArtTag #1: defaults to name`);
+
+  artTag.name = `Dave Strider (Homestuck)`;
+
+  t.equal(artTag.nameShort, `Dave Strider`,
+    `ArtTag #2: trims parenthical part at end`);
+
+  artTag.name = `This (And) That (Then)`;
+
+  t.equal(artTag.nameShort, `This (And) That`,
+    `ArtTag #2: doesn't trim midlde parenthical part`);
+});
diff --git a/test/unit/data/things/track.js b/test/unit/data/things/track.js
index 383e3e3f..f68510c6 100644
--- a/test/unit/data/things/track.js
+++ b/test/unit/data/things/track.js
@@ -1,74 +1,587 @@
 import t from 'tap';
+
+import {showAggregate} from '#sugar';
+import {linkAndBindWikiData} from '#test-lib';
 import thingConstructors from '#things';
 
 const {
   Album,
+  Artist,
+  Flash,
+  FlashAct,
   Thing,
   Track,
-  TrackGroup,
 } = thingConstructors;
 
-function stubAlbum(tracks) {
+function stubAlbum(tracks, directory = 'bar') {
   const album = new Album();
-  album.trackSections = [
-    {
-      tracksByRef: tracks.map(t => Thing.getReference(t)),
-    },
-  ];
-  album.trackData = tracks;
+  album.directory = directory;
+
+  const trackRefs = tracks.map(t => Thing.getReference(t));
+  album.trackSections = [{tracks: trackRefs}];
+
   return album;
 }
 
-t.test(`Track.coverArtDate`, t => {
+function stubTrack(directory = 'foo') {
+  const track = new Track();
+  track.directory = directory;
+
+  return track;
+}
+
+function stubTrackAndAlbum(trackDirectory = 'foo', albumDirectory = 'bar') {
+  const track = stubTrack(trackDirectory);
+  const album = stubAlbum([track], albumDirectory);
+
+  return {track, album};
+}
+
+function stubArtist(artistName = `Test Artist`) {
+  const artist = new Artist();
+  artist.name = artistName;
+
+  return artist;
+}
+
+function stubArtistAndContribs(artistName = `Test Artist`) {
+  const artist = stubArtist(artistName);
+  const contribs = [{who: artistName, what: null}];
+  const badContribs = [{who: `Figment of Your Imagination`, what: null}];
+
+  return {artist, contribs, badContribs};
+}
+
+function stubFlashAndAct(directory = 'zam') {
+  const flash = new Flash();
+  flash.directory = directory;
+
+  const flashAct = new FlashAct();
+  flashAct.flashes = [Thing.getReference(flash)];
+
+  return {flash, flashAct};
+}
+
+t.test(`Track.album`, t => {
+  t.plan(6);
+
+  // Note: These asserts use manual albumData/trackData relationships
+  // to illustrate more specifically the properties which are expected to
+  // be relevant for this case. Other properties use the same underlying
+  // get-album behavior as Track.album so aren't tested as aggressively.
+
+  const track1 = stubTrack('track1');
+  const track2 = stubTrack('track2');
+  const album1 = new Album();
+  const album2 = new Album();
+
+  t.equal(track1.album, null,
+    `album #1: defaults to null`);
+
+  track1.albumData = [album1, album2];
+  track2.albumData = [album1, album2];
+  album1.trackData = [track1, track2];
+  album2.trackData = [track1, track2];
+  album1.trackSections = [{tracks: ['track:track1']}];
+  album2.trackSections = [{tracks: ['track:track2']}];
+
+  t.equal(track1.album, album1,
+    `album #2: is album when album's trackSections matches track`);
+
+  track1.albumData = [album2, album1];
+
+  t.equal(track1.album, album1,
+    `album #3: is album when albumData is in different order`);
+
+  track1.albumData = [];
+
+  t.equal(track1.album, null,
+    `album #4: is null when track missing albumData`);
+
+  album1.trackData = [];
+  track1.albumData = [album1, album2];
+
+  t.equal(track1.album, null,
+    `album #5: is null when album missing trackData`);
+
+  album1.trackData = [track1, track2];
+  album1.trackSections = [{tracks: ['track:track2']}];
+
+  // XXX_decacheWikiData
+  track1.albumData = [];
+  track1.albumData = [album1, album2];
+
+  t.equal(track1.album, null,
+    `album #6: is null when album's trackSections don't match track`);
+});
+
+t.test(`Track.color`, t => {
   t.plan(5);
 
-  // Priority order is as follows, with the last (trackCoverArtDate) being
-  // greatest priority.
-  const albumDate = new Date('2010-10-10');
-  const albumTrackArtDate = new Date('2012-12-12');
-  const trackDateFirstReleased = new Date('2008-08-08');
-  const trackCoverArtDate = new Date('2009-09-09');
+  const {track, album} = stubTrackAndAlbum();
+
+  const {wikiData, linkWikiDataArrays, XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album],
+    trackData: [track],
+  });
+
+  t.equal(track.color, null,
+    `color #1: defaults to null`);
+
+  album.color = '#abcdef';
+  album.trackSections = [{
+    color: '#beeeef',
+    tracks: [Thing.getReference(track)],
+  }];
+  XXX_decacheWikiData();
+
+  t.equal(track.color, '#beeeef',
+    `color #2: inherits from track section before album`);
+
+  // Replace the album with a completely fake one. This isn't realistic, since
+  // in correct data, Album.tracks depends on Albums.trackSections and so the
+  // track's album will always have a corresponding track section. But if that
+  // connection breaks for some future reason (with the album still present),
+  // Track.color should still inherit directly from the album.
+  wikiData.albumData = [
+    new Proxy({
+      color: '#abcdef',
+      tracks: [track],
+      trackSections: [
+        {color: '#baaaad', tracks: []},
+      ],
+    }, {getPrototypeOf: () => Album.prototype}),
+  ];
+
+  linkWikiDataArrays();
+
+  t.equal(track.color, '#abcdef',
+    `color #3: inherits from album without matching track section`);
+
+  track.color = '#123456';
+
+  t.equal(track.color, '#123456',
+    `color #4: is own value`);
+
+  t.throws(() => { track.color = '#aeiouw'; }, TypeError,
+    `color #5: must be set to valid color`);
+});
+
+t.test(`Track.commentatorArtists`, t => {
+  t.plan(6);
 
   const track = new Track();
-  track.directory = 'foo';
+  const artist1 = stubArtist(`SnooPING`);
+  const artist2 = stubArtist(`ASUsual`);
+  const artist3 = stubArtist(`Icy`);
+
+  linkAndBindWikiData({
+    trackData: [track],
+    artistData: [artist1, artist2, artist3],
+  });
+
+  track.commentary =
+    `<i>SnooPING:</i>\n` +
+    `Wow.\n`;
+
+  t.same(track.commentatorArtists, [artist1],
+    `Track.commentatorArtists #1: works with one commentator`);
+
+  track.commentary +=
+    `<i>ASUsual:</i>\n` +
+    `Yes!\n`;
+
+  t.same(track.commentatorArtists, [artist1, artist2],
+    `Track.commentatorArtists #2: works with two commentators`);
+
+  track.commentary +=
+    `<i><b>Icy:</b></i>\n` +
+    `Incredible.\n`;
+
+  t.same(track.commentatorArtists, [artist1, artist2, artist3],
+    `Track.commentatorArtists #3: works with boldface name`);
+
+  track.commentary =
+    `<i>Icy:</i> (project manager)\n` +
+    `Very good track.\n`;
+
+  t.same(track.commentatorArtists, [artist3],
+    `Track.commentatorArtists #4: works with parenthical accent`);
+
+  track.commentary +=
+    `<i>SNooPING ASUsual Icy:</i>\n` +
+    `WITH ALL THREE POWERS COMBINED...`;
+
+  t.same(track.commentatorArtists, [artist3],
+    `Track.commentatorArtists #5: ignores artist names not found`);
+
+  track.commentary +=
+    `<i>Icy:</i>\n` +
+    `I'm back!\n`;
+
+  t.same(track.commentatorArtists, [artist3],
+    `Track.commentatorArtists #6: ignores duplicate artist`);
+});
+
+t.test(`Track.coverArtDate`, t => {
+  t.plan(8);
+
+  const {track, album} = stubTrackAndAlbum();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  const {XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+    trackData: [track],
+  });
+
+  track.coverArtistContribs = contribs;
+
+  t.equal(track.coverArtDate, null,
+    `coverArtDate #1: defaults to null`);
+
+  album.trackArtDate = new Date('2012-12-12');
+
+  XXX_decacheWikiData();
+
+  t.same(track.coverArtDate, new Date('2012-12-12'),
+    `coverArtDate #2: inherits album trackArtDate`);
+
+  track.coverArtDate = new Date('2009-09-09');
+
+  t.same(track.coverArtDate, new Date('2009-09-09'),
+    `coverArtDate #3: is own value`);
+
+  track.coverArtistContribs = [];
+
+  t.equal(track.coverArtDate, null,
+    `coverArtDate #4: is null if track coverArtistContribs empty`);
+
+  album.trackCoverArtistContribs = contribs;
+
+  XXX_decacheWikiData();
+
+  t.same(track.coverArtDate, new Date('2009-09-09'),
+    `coverArtDate #5: is not null if album trackCoverArtistContribs specified`);
+
+  album.trackCoverArtistContribs = badContribs;
+
+  XXX_decacheWikiData();
+
+  t.equal(track.coverArtDate, null,
+    `coverArtDate #6: is null if album trackCoverArtistContribs resolves empty`);
+
+  track.coverArtistContribs = badContribs;
+
+  t.equal(track.coverArtDate, null,
+    `coverArtDate #7: is null if track coverArtistContribs resolves empty`);
 
-  const album = stubAlbum([track]);
+  track.coverArtistContribs = contribs;
+  track.disableUniqueCoverArt = true;
 
-  track.albumData = [album];
+  t.equal(track.coverArtDate, null,
+    `coverArtDate #8: is null if track disables unique cover artwork`);
+});
+
+t.test(`Track.coverArtFileExtension`, t => {
+  t.plan(8);
+
+  const {track, album} = stubTrackAndAlbum();
+  const {artist, contribs} = stubArtistAndContribs();
+
+  const {XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+    trackData: [track],
+  });
+
+  t.equal(track.coverArtFileExtension, null,
+    `coverArtFileExtension #1: defaults to null`);
+
+  track.coverArtistContribs = contribs;
+
+  t.equal(track.coverArtFileExtension, 'jpg',
+    `coverArtFileExtension #2: is jpg if has cover art and not further specified`);
+
+  track.coverArtistContribs = [];
+
+  album.coverArtistContribs = contribs;
+  XXX_decacheWikiData();
+
+  t.equal(track.coverArtFileExtension, null,
+    `coverArtFileExtension #3: only has value for unique cover art`);
+
+  track.coverArtistContribs = contribs;
+
+  album.trackCoverArtFileExtension = 'png';
+  XXX_decacheWikiData();
+
+  t.equal(track.coverArtFileExtension, 'png',
+    `coverArtFileExtension #4: inherits album trackCoverArtFileExtension (1/2)`);
+
+  track.coverArtFileExtension = 'gif';
+
+  t.equal(track.coverArtFileExtension, 'gif',
+    `coverArtFileExtension #5: is own value (1/2)`);
+
+  track.coverArtistContribs = [];
+
+  album.trackCoverArtistContribs = contribs;
+  XXX_decacheWikiData();
+
+  t.equal(track.coverArtFileExtension, 'gif',
+    `coverArtFileExtension #6: is own value (2/2)`);
+
+  track.coverArtFileExtension = null;
+
+  t.equal(track.coverArtFileExtension, 'png',
+    `coverArtFileExtension #7: inherits album trackCoverArtFileExtension (2/2)`);
+
+  track.disableUniqueCoverArt = true;
+
+  t.equal(track.coverArtFileExtension, null,
+    `coverArtFileExtension #8: is null if track disables unique cover art`);
+});
+
+t.test(`Track.date`, t => {
+  t.plan(3);
+
+  const {track, album} = stubTrackAndAlbum();
+
+  const {XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album],
+    trackData: [track],
+  });
+
+  t.equal(track.date, null,
+    `date #1: defaults to null`);
+
+  album.date = new Date('2012-12-12');
+  XXX_decacheWikiData();
+
+  t.same(track.date, album.date,
+    `date #2: inherits from album`);
+
+  track.dateFirstReleased = new Date('2009-09-09');
+
+  t.same(track.date, new Date('2009-09-09'),
+    `date #3: is own dateFirstReleased`);
+});
+
+t.test(`Track.featuredInFlashes`, t => {
+  t.plan(2);
+
+  const {track, album} = stubTrackAndAlbum('track1');
+
+  const {flash: flash1, flashAct: flashAct1} = stubFlashAndAct('flash1');
+  const {flash: flash2, flashAct: flashAct2} = stubFlashAndAct('flash2');
+
+  const {XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album],
+    trackData: [track],
+    flashData: [flash1, flash2],
+    flashActData: [flashAct1, flashAct2],
+  });
+
+  t.same(track.featuredInFlashes, [],
+    `featuredInFlashes #1: defaults to empty array`);
+
+  flash1.featuredTracks = ['track:track1'];
+  flash2.featuredTracks = ['track:track1'];
+  XXX_decacheWikiData();
+
+  t.same(track.featuredInFlashes, [flash1, flash2],
+    `featuredInFlashes #2: matches flashes' featuredTracks`);
+});
+
+t.test(`Track.hasUniqueCoverArt`, t => {
+  t.plan(7);
+
+  const {track, album} = stubTrackAndAlbum();
+  const {artist, contribs, badContribs} = stubArtistAndContribs();
+
+  const {XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album],
+    artistData: [artist],
+    trackData: [track],
+  });
 
-  // 1. coverArtDate defaults to null
+  t.equal(track.hasUniqueCoverArt, false,
+    `hasUniqueCoverArt #1: defaults to false`);
 
-  t.equal(track.coverArtDate, null);
+  album.trackCoverArtistContribs = contribs;
+  XXX_decacheWikiData();
 
-  // 2. coverArtDate inherits album release date
+  t.equal(track.hasUniqueCoverArt, true,
+    `hasUniqueCoverArt #2: is true if album specifies trackCoverArtistContribs`);
 
-  album.date = albumDate;
+  track.disableUniqueCoverArt = true;
 
-  // XXX clear cache so change in album's property is reflected
-  track.albumData = [];
-  track.albumData = [album];
+  t.equal(track.hasUniqueCoverArt, false,
+    `hasUniqueCoverArt #3: is false if disableUniqueCoverArt is true (1/2)`);
 
-  t.equal(track.coverArtDate, albumDate);
+  track.disableUniqueCoverArt = false;
+
+  album.trackCoverArtistContribs = badContribs;
+  XXX_decacheWikiData();
+
+  t.equal(track.hasUniqueCoverArt, false,
+    `hasUniqueCoverArt #4: is false if album's trackCoverArtistContribs resolve empty`);
+
+  track.coverArtistContribs = contribs;
+
+  t.equal(track.hasUniqueCoverArt, true,
+    `hasUniqueCoverArt #5: is true if track specifies coverArtistContribs`);
+
+  track.disableUniqueCoverArt = true;
+
+  t.equal(track.hasUniqueCoverArt, false,
+    `hasUniqueCoverArt #6: is false if disableUniqueCoverArt is true (2/2)`);
+
+  track.disableUniqueCoverArt = false;
+
+  track.coverArtistContribs = badContribs;
+
+  t.equal(track.hasUniqueCoverArt, false,
+    `hasUniqueCoverArt #7: is false if track's coverArtistContribs resolve empty`);
+});
+
+t.test(`Track.originalReleaseTrack`, t => {
+  t.plan(3);
+
+  const {track: track1, album: album1} = stubTrackAndAlbum('track1');
+  const {track: track2, album: album2} = stubTrackAndAlbum('track2');
+
+  const {wikiData, linkWikiDataArrays, XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album1, album2],
+    trackData: [track1, track2],
+  });
+
+  t.equal(track2.originalReleaseTrack, null,
+    `originalReleaseTrack #1: defaults to null`);
+
+  track2.originalReleaseTrack = 'track:track1';
+
+  t.equal(track2.originalReleaseTrack, track1,
+    `originalReleaseTrack #2: is resolved from own value`);
+
+  track2.trackData = [];
+
+  t.equal(track2.originalReleaseTrack, null,
+    `originalReleaseTrack #3: is null when track missing trackData`);
+});
+
+t.test(`Track.otherReleases`, t => {
+  t.plan(6);
+
+  const {track: track1, album: album1} = stubTrackAndAlbum('track1');
+  const {track: track2, album: album2} = stubTrackAndAlbum('track2');
+  const {track: track3, album: album3} = stubTrackAndAlbum('track3');
+  const {track: track4, album: album4} = stubTrackAndAlbum('track4');
+
+  const {wikiData, linkWikiDataArrays, XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album1, album2, album3, album4],
+    trackData: [track1, track2, track3, track4],
+  });
+
+  t.same(track1.otherReleases, [],
+    `otherReleases #1: defaults to empty array`);
+
+  track2.originalReleaseTrack = 'track:track1';
+  track3.originalReleaseTrack = 'track:track1';
+  track4.originalReleaseTrack = 'track:track1';
+  XXX_decacheWikiData();
+
+  t.same(track1.otherReleases, [track2, track3, track4],
+    `otherReleases #2: otherReleases of original release are its rereleases`);
+
+  wikiData.trackData = [track1, track3, track2, track4];
+  linkWikiDataArrays();
+
+  t.same(track1.otherReleases, [track3, track2, track4],
+    `otherReleases #3: otherReleases matches trackData order`);
+
+  wikiData.trackData = [track3, track2, track1, track4];
+  linkWikiDataArrays();
+
+  t.same(track2.otherReleases, [track1, track3, track4],
+    `otherReleases #4: otherReleases of rerelease are original track then other rereleases (1/3)`);
+
+  t.same(track3.otherReleases, [track1, track2, track4],
+    `otherReleases #5: otherReleases of rerelease are original track then other rereleases (2/3)`);
+
+  t.same(track4.otherReleases, [track1, track3, track2],
+    `otherReleases #6: otherReleases of rerelease are original track then other rereleases (3/3)`);
+});
+
+t.test(`Track.referencedByTracks`, t => {
+  t.plan(4);
+
+  const {track: track1, album: album1} = stubTrackAndAlbum('track1');
+  const {track: track2, album: album2} = stubTrackAndAlbum('track2');
+  const {track: track3, album: album3} = stubTrackAndAlbum('track3');
+  const {track: track4, album: album4} = stubTrackAndAlbum('track4');
+
+  const {XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album1, album2, album3, album4],
+    trackData: [track1, track2, track3, track4],
+  });
+
+  t.same(track1.referencedByTracks, [],
+    `referencedByTracks #1: defaults to empty array`);
+
+  track2.referencedTracks = ['track:track1'];
+  track3.referencedTracks = ['track:track1'];
+  XXX_decacheWikiData();
+
+  t.same(track1.referencedByTracks, [track2, track3],
+    `referencedByTracks #2: matches tracks' referencedTracks`);
+
+  track4.sampledTracks = ['track:track1'];
+  XXX_decacheWikiData();
+
+  t.same(track1.referencedByTracks, [track2, track3],
+    `referencedByTracks #3: doesn't match tracks' sampledTracks`);
+
+  track3.originalReleaseTrack = 'track:track2';
+  XXX_decacheWikiData();
+
+  t.same(track1.referencedByTracks, [track2],
+    `referencedByTracks #4: doesn't include re-releases`);
+});
 
-  // 3. coverArtDate inherits album trackArtDate
+t.test(`Track.sampledByTracks`, t => {
+  t.plan(4);
 
-  album.trackArtDate = albumTrackArtDate;
+  const {track: track1, album: album1} = stubTrackAndAlbum('track1');
+  const {track: track2, album: album2} = stubTrackAndAlbum('track2');
+  const {track: track3, album: album3} = stubTrackAndAlbum('track3');
+  const {track: track4, album: album4} = stubTrackAndAlbum('track4');
 
-  // XXX clear cache again
-  track.albumData = [];
-  track.albumData = [album];
+  const {XXX_decacheWikiData} = linkAndBindWikiData({
+    albumData: [album1, album2, album3, album4],
+    trackData: [track1, track2, track3, track4],
+  });
 
-  t.equal(track.coverArtDate, albumTrackArtDate);
+  t.same(track1.sampledByTracks, [],
+    `sampledByTracks #1: defaults to empty array`);
 
-  // 4. coverArtDate is overridden dateFirstReleased
+  track2.sampledTracks = ['track:track1'];
+  track3.sampledTracks = ['track:track1'];
+  XXX_decacheWikiData();
 
-  track.dateFirstReleased = trackDateFirstReleased;
+  t.same(track1.sampledByTracks, [track2, track3],
+    `sampledByTracks #2: matches tracks' sampledTracks`);
 
-  t.equal(track.coverArtDate, trackDateFirstReleased);
+  track4.referencedTracks = ['track:track1'];
+  XXX_decacheWikiData();
 
-  // 5. coverArtDate is overridden coverArtDate
+  t.same(track1.sampledByTracks, [track2, track3],
+    `sampledByTracks #3: doesn't match tracks' referencedTracks`);
 
-  track.coverArtDate = trackCoverArtDate;
+  track3.originalReleaseTrack = 'track:track2';
+  XXX_decacheWikiData();
 
-  t.equal(track.coverArtDate, trackCoverArtDate);
+  t.same(track1.sampledByTracks, [track2],
+    `sampledByTracks #4: doesn't include re-releases`);
 });