From 9e35d2d0727b398d5a01a00b4cee1624125d8651 Mon Sep 17 00:00:00 2001 From: Juan Cruz Fortunatti Date: Mon, 5 Oct 2026 23:15:05 +0200 Subject: [PATCH] refactor(site): write generated site outputs into site/prepared --- .../references/implementation-map.md | 2 +- .github/scripts/architecture/graph.mts | 2 +- .github/scripts/ci/prepare-typecheck.mts | 2 +- .github/site-refactor.json | 30 ++++++++++++-- .gitignore | 40 +++++++++---------- docs/moon-catalogues.md | 2 +- docs/object-discovery.md | 2 +- docs/provenance/CONTRACT.md | 2 +- docs/reader-text.md | 2 +- docs/site-architecture/moves.json | 23 ----------- docs/site-architecture/tiers.json | 38 +++++++++--------- docs/sources-catalogue.md | 4 +- eslint.config.mts | 2 +- labs/performance/README.md | 2 +- packages/bake/AGENTS.md | 2 +- packages/bake/README.md | 2 +- .../bake/src/navigation/prepare-navigation.ts | 2 +- .../site-assets/prepare-dataset-billboards.ts | 4 +- .../prepare-search-thumbnails.test.mts | 12 +++--- .../site-assets/prepare-search-thumbnails.ts | 4 +- .../src/sources/facility-artwork-refresh.ts | 8 ++-- packages/objects/README.md | 2 +- .../src/node/prepared-registry.test.ts | 2 +- .../objects/src/node/prepared-registry.ts | 2 +- .../source/format-reader-ledger.ts | 2 +- .../objects/src/provenance/source-credits.ts | 2 +- packages/telescope-cli/src/query.mts | 2 +- ...on-world-context.characterization.test.mts | 2 +- site/application-world-context.mts | 2 +- ...-world-resources.characterization.test.mts | 2 +- site/application-world-resources.mts | 4 +- site/build/content/prepare.ts | 2 +- site/build/prepare/body-additions.test.mts | 2 +- site/build/prepare/prepare-catalog.mts | 31 +++++++------- site/build/prepare/prepare-facilities.mts | 8 ++-- site/build/prepare/prepare-feature-index.mts | 2 +- site/build/prepare/prepare-moon-labels.mts | 4 +- site/build/prepare/prepare-shell-icons.mts | 2 +- site/build/prepare/prepare-shell-titles.mts | 2 +- site/build/prepare/prepare-text.mts | 2 +- .../prepare/prepare-world-presentation.mts | 8 ++-- site/build/prepare/prepared-icons.test.mts | 4 +- site/build/prepare/refresh-source-icons.mts | 2 +- ...ogue-moon-labels.characterization.test.mts | 2 +- site/catalogue-moon-labels.mts | 2 +- site/components/ObjectFactsheet.astro | 2 +- site/components/ObjectNavigationMarker.astro | 2 +- site/components/ObjectShell.astro | 2 +- ...t-catalogue-data.characterization.test.mts | 2 +- site/dot-catalogue-data.mts | 4 +- site/exploration-catalog.mts | 2 +- site/hosted-banks.characterization.test.mts | 4 +- site/hosted-banks.mts | 4 +- site/layouts/ObjectLayout.astro | 2 +- site/object-entry.mts | 2 +- site/objects.mts | 2 +- site/pages/catalogues/dots.bin.ts | 2 +- site/pages/objects/[id]/entry.json.ts | 2 +- site/pages/world/context-assets/[id].json.ts | 2 +- ...rld-presentation.characterization.test.mts | 4 +- site/prepared-world-presentation.mts | 4 +- site/{ => prepared}/prepared-catalogue.d.mts | 0 .../prepared-navigation-markers.d.mts | 0 .../{ => prepared}/prepared-shell-icons.d.mts | 0 .../prepared-shell-titles.d.mts | 0 site/search/prepared-catalogue-index.mts | 4 +- site/server/feature-pin.mts | 2 +- site/server/search-server.mts | 2 +- site/source-documentation.mts | 2 +- site/stellar-extents.mts | 2 +- site/system-framing.mts | 10 ++--- site/test/hosted-banks.test.mts | 2 +- site/world/navigation-preparation.test.mts | 6 +-- site/world/object-navigation.test.mts | 2 +- 74 files changed, 179 insertions(+), 177 deletions(-) rename site/{ => prepared}/prepared-catalogue.d.mts (100%) rename site/{ => prepared}/prepared-navigation-markers.d.mts (100%) rename site/{ => prepared}/prepared-shell-icons.d.mts (100%) rename site/{ => prepared}/prepared-shell-titles.d.mts (100%) diff --git a/.agents/skills/celestial-skill/references/implementation-map.md b/.agents/skills/celestial-skill/references/implementation-map.md index b46384fab0a..1b684d0f485 100644 --- a/.agents/skills/celestial-skill/references/implementation-map.md +++ b/.agents/skills/celestial-skill/references/implementation-map.md @@ -86,7 +86,7 @@ Navigation marker appearance comes from each authored package's `source/preparation/navigation.json`, which names its source image by path; the pins and attribution are the source manifest's record. `packages/bake/src/navigation/prepare-navigation.ts` generates individual `public/navigation/body-.webp` images and their 2x counterparts. -Builds assemble the ignored `site/prepared-navigation-markers.mjs` from those +Builds assemble the ignored `site/prepared/prepared-navigation-markers.mjs` from those images and recipes; `ObjectNavigationMarker.astro` consumes it. Follow the [registration steps](../../../../src/objects/README.md#register-a-body-without-editing-shared-lists) instead of editing a shared list or atlas position. diff --git a/.github/scripts/architecture/graph.mts b/.github/scripts/architecture/graph.mts index d13b2266484..e6c05903b9c 100644 --- a/.github/scripts/architecture/graph.mts +++ b/.github/scripts/architecture/graph.mts @@ -57,7 +57,7 @@ export function decodeCruiseResult(value: unknown): CruisedModule[] { } /** A generated module (untracked) is counted as its tracked declaration, so the graph is the same whether or - * not the checkout has run its preparation steps: `site/prepared-shell-titles.mjs` becomes `….d.mts`. */ + * not the checkout has run its preparation steps: `site/prepared/prepared-shell-titles.mjs` becomes `….d.mts`. */ export function trackedStandIn(path: string, tracked: ReadonlySet): string | undefined { if (tracked.has(path)) return path; const match = /^(.*)\.([cm]?)[jt]s$/u.exec(path); diff --git a/.github/scripts/ci/prepare-typecheck.mts b/.github/scripts/ci/prepare-typecheck.mts index 0ee1c8409f7..694d53c03bb 100644 --- a/.github/scripts/ci/prepare-typecheck.mts +++ b/.github/scripts/ci/prepare-typecheck.mts @@ -45,7 +45,7 @@ export function preparedJsonImports(file: string, source: string, root: string): /** Generated modules that import prepared JSON found by rule, so that no authored file names its object: the application's * context objects import the galaxy catalogue's display sample (site/build/prepare/prepare-catalog.mts contextObjectModule). * The preparation build writes them before this scan; git ignores them, so the listing below does not find them. */ -const GENERATED_IMPORTERS = ['site/prepared-context-objects.mts']; +const GENERATED_IMPORTERS = ['site/prepared/prepared-context-objects.mts']; /** Scan maintained source, including test imports. Generated declarations are not an alternate data contract. * All authored TypeScript/Astro is a conservative superset of the application and test compiler programs, so a diff --git a/.github/site-refactor.json b/.github/site-refactor.json index 5a71099b04b..2cf6ec8af20 100644 --- a/.github/site-refactor.json +++ b/.github/site-refactor.json @@ -1,7 +1,31 @@ { "mode": "semantic", - "moves": {}, + "moves": { + "site/prepared-catalogue.d.mts": "site/prepared/prepared-catalogue.d.mts", + "site/prepared-navigation-markers.d.mts": "site/prepared/prepared-navigation-markers.d.mts", + "site/prepared-shell-icons.d.mts": "site/prepared/prepared-shell-icons.d.mts", + "site/prepared-shell-titles.d.mts": "site/prepared/prepared-shell-titles.d.mts", + "site/moon-labels.prepared.json": "site/prepared/moon-labels.prepared.json", + "site/prepared-catalogue.mjs": "site/prepared/prepared-catalogue.mjs", + "site/prepared-context-bank-assets.json": "site/prepared/prepared-context-bank-assets.json", + "site/prepared-context-json.mts": "site/prepared/prepared-context-json.mts", + "site/prepared-context-objects.mts": "site/prepared/prepared-context-objects.mts", + "site/prepared-dataset-billboards.json": "site/prepared/prepared-dataset-billboards.json", + "site/prepared-dataset-volumes.json": "site/prepared/prepared-dataset-volumes.json", + "site/prepared-dot-catalogues.json": "site/prepared/prepared-dot-catalogues.json", + "site/prepared-facilities.json": "site/prepared/prepared-facilities.json", + "site/prepared-feature-index.json": "site/prepared/prepared-feature-index.json", + "site/prepared-fit-boxes.json": "site/prepared/prepared-fit-boxes.json", + "site/prepared-hosted-banks.json": "site/prepared/prepared-hosted-banks.json", + "site/prepared-navigation-markers.mjs": "site/prepared/prepared-navigation-markers.mjs", + "site/prepared-shell-icons.mjs": "site/prepared/prepared-shell-icons.mjs", + "site/prepared-shell-titles.mjs": "site/prepared/prepared-shell-titles.mjs", + "site/prepared-source-credits.json": "site/prepared/prepared-source-credits.json", + "site/prepared-sources.json": "site/prepared/prepared-sources.json", + "site/prepared-stellar-extents.json": "site/prepared/prepared-stellar-extents.json", + "site/prepared-world-presentation.json": "site/prepared/prepared-world-presentation.json" + }, "outputs": [], - "layout": "changes", - "change": "S3-7 loader owners" + "layout": "none", + "change": "generated site outputs into site/prepared" } diff --git a/.gitignore b/.gitignore index b486ade986a..246bd55a6f0 100644 --- a/.gitignore +++ b/.gitignore @@ -270,35 +270,35 @@ src/objects/*/source/**/*.bpc !src/objects/moon/source/science/diviner-ghrm/tbol_m-compact.tif # Combined catalogues and navigation are rebuilt from individual body records. -/site/prepared-sources.json -/site/prepared-source-credits.json -/site/prepared-facilities.json -/site/prepared-catalogue.mjs -/site/prepared-overview-objects.json -/site/prepared-dataset-volumes.json -/site/prepared-dot-catalogues.json -/site/prepared-stellar-extents.json -/site/prepared-fit-boxes.json -/site/prepared-context-objects.mts -/site/prepared-context-bank-assets.json -/site/prepared-hosted-banks.json -/site/prepared-context-json.mts -/site/prepared-feature-index.json -/site/prepared-dataset-billboards.json +/site/prepared/prepared-sources.json +/site/prepared/prepared-source-credits.json +/site/prepared/prepared-facilities.json +/site/prepared/prepared-catalogue.mjs +/site/prepared/prepared-overview-objects.json +/site/prepared/prepared-dataset-volumes.json +/site/prepared/prepared-dot-catalogues.json +/site/prepared/prepared-stellar-extents.json +/site/prepared/prepared-fit-boxes.json +/site/prepared/prepared-context-objects.mts +/site/prepared/prepared-context-bank-assets.json +/site/prepared/prepared-hosted-banks.json +/site/prepared/prepared-context-json.mts +/site/prepared/prepared-feature-index.json +/site/prepared/prepared-dataset-billboards.json /public/navigation/dataset-billboards/ /public/navigation/dataset-sprites/ /public/features/ /packages/astronomy/src/data/generated/ -/site/prepared-navigation-markers.mjs +/site/prepared/prepared-navigation-markers.mjs /src/platform/solar-geometry.mts -/site/moon-labels.prepared.json +/site/prepared/moon-labels.prepared.json # AKARI AcuA v1: restored and verified from each body source manifest. /src/objects/*/source/reference/AcuA_V1.txt.gz # Shared shell outputs, replayed by pnpm prepare:shell from pinned fonts and vectors. -/site/prepared-shell-icons.mjs -/site/prepared-shell-titles.mjs +/site/prepared/prepared-shell-icons.mjs +/site/prepared/prepared-shell-titles.mjs # Large source inputs with pinned archive downloads; `pnpm prepare:checkout` (or # `node packages/bake/cli/object-operations.mts acquire `) restores and verifies them. @@ -355,7 +355,7 @@ src/objects/*/source/**/*.bpc # Small expected-output receipt supports offline source recovery. -/site/prepared-world-presentation.json +/site/prepared/prepared-world-presentation.json # Solar System source products restored from their manifest origins or source-cache. /src/objects/mars/source/geology/SIM3292_Global_Geology.shp diff --git a/docs/moon-catalogues.md b/docs/moon-catalogues.md index 1ee6d1f0513..7f70be9978b 100644 --- a/docs/moon-catalogues.md +++ b/docs/moon-catalogues.md @@ -75,7 +75,7 @@ as a nominal sphere (Anthe). Those pages were retired; each is now a dot with it ([when a body gets a scene](../.agents/skills/celestial-skill/references/scientific-faithfulness.md#a-scene-needs-a-measured-shape)). Run `node site/build/prepare/prepare-moon-labels.mts` to reproduce the ignored -`site/moon-labels.prepared.json` from the pinned source archive and current world +`site/prepared/moon-labels.prepared.json` from the pinned source archive and current world context. `pnpm prepare:world-context` runs this step after preparing the world. The browser only projects these fixed positions; it performs no ephemeris work. diff --git a/docs/object-discovery.md b/docs/object-discovery.md index 30a9363eb88..48dd589be59 100644 --- a/docs/object-discovery.md +++ b/docs/object-discovery.md @@ -98,7 +98,7 @@ part of the package's source interpretation update. `pnpm prepare:catalog` derives discovery from exposed `prepared/controls.json` datasets and their raster recipes. It writes each body's discovery beside its descriptor and distance in the ignored prepared catalogue -(`site/prepared-catalogue.mjs`) that the single `OBJECTS` registry reads. Runtime reads this prepared metadata; it does not inspect source images or +(`site/prepared/prepared-catalogue.mjs`) that the single `OBJECTS` registry reads. Runtime reads this prepared metadata; it does not inspect source images or generate assets. The controls come from R2, not Git, so the dev, build and deploy chains restore prepared assets before the catalogue runs. Without them, a clean checkout finds no imagery for any body, and moons and small bodies read **Shape only**. diff --git a/docs/provenance/CONTRACT.md b/docs/provenance/CONTRACT.md index 0d8233a90c0..3d152fe3f71 100644 --- a/docs/provenance/CONTRACT.md +++ b/docs/provenance/CONTRACT.md @@ -49,7 +49,7 @@ PDS4 archive compliance nor assessed ISO conformity, and does not require PDS XM | `prepared/object.json`, `prepared/page.json` | Transport and page metadata built from the installed runtime when read; never files, inventoried or committed | | Other delivery files under `prepared/` | Baked output. Published to R2 through `inventory.json`, restored by `setup:assets`, never committed; audit-only terrain reports and source-index rasters are excluded | | `inventory.json` at the body root | Generated inventory of every baked file (public textures and `prepared/*`) used by installation and publication | -| `site/prepared-sources.json`, `site/prepared-source-credits.json` and `site/prepared-facilities.json` | Ignored source usage, page credit and mission attribution outputs; prepare together | +| `site/prepared/prepared-sources.json`, `site/prepared/prepared-source-credits.json` and `site/prepared/prepared-facilities.json` | Ignored source usage, page credit and mission attribution outputs; prepare together | | Shared guides and illustrations under `docs/` | Maintained explanations used across bodies | | Unit tests, their fixtures and helpers beside the code they test; integration tests that span several owners in `integration/`; do not add a `tools/` or `tests/` directory; preparation code under `packages/bake/` (`src/`, `cli/`, `authoring/`), telescope code under `packages/telescope-cli/`, site build steps under `site/build/`, CI checks under `.github/scripts/` | Inputs and implementation used by executable checks and preparation | | Root README and [body contributor guide](../../src/objects/README.md) | Shared installation, controls, commands and contribution workflow | diff --git a/docs/reader-text.md b/docs/reader-text.md index 7514ea0a9de..e4c014610c6 100644 --- a/docs/reader-text.md +++ b/docs/reader-text.md @@ -130,7 +130,7 @@ Warnings are for the reviewer and never block: - a card or introduction that another body shares; - repetition between blocks shown together: the introduction, one dataset summary and the mission, facility and note cards beside it. This check reads - `site/prepared-facilities.json`, so run + `site/prepared/prepared-facilities.json`, so run `node site/build/prepare/prepare-facilities.mts --catalog-only` first. `site/build/prepare/prepare-text.test.mts` runs the check on every registered body. diff --git a/docs/site-architecture/moves.json b/docs/site-architecture/moves.json index 7bb6a0fde59..c6a103e9e2b 100644 --- a/docs/site-architecture/moves.json +++ b/docs/site-architecture/moves.json @@ -53,10 +53,6 @@ "site/components/ObjectSwatchStyles.astro": "site/layouts/ObjectSwatchStyles.astro", "site/diagnostic-recorder.mts": "site/shell/diagnostic-recorder.mts", "site/env.d.ts": "site/env.d.ts", - "site/prepared-catalogue.d.mts": "site/prepared/prepared-catalogue.d.mts", - "site/prepared-navigation-markers.d.mts": "site/prepared/prepared-navigation-markers.d.mts", - "site/prepared-shell-icons.d.mts": "site/prepared/prepared-shell-icons.d.mts", - "site/prepared-shell-titles.d.mts": "site/prepared/prepared-shell-titles.d.mts", "site/social-images.mts": "site/server/social-images.mts", "site/build/source-maps.test.mts": "site/build/source-maps.test.mts", "site/navigation/evidence/star-click/one-click.jpg": "site/navigation/evidence/star-click/one-click.jpg", @@ -81,25 +77,6 @@ "site/test/system-framing.test.mts": "site/world/system-framing.test.mts", "site/test/view-readout.test.mts": "site/shell/view-readout.test.mts", "site/test/zoom-scope.test.mts": "site/world/zoom-scope.test.mts", - "site/moon-labels.prepared.json": "site/prepared/moon-labels.prepared.json", - "site/prepared-catalogue.mjs": "site/prepared/prepared-catalogue.mjs", - "site/prepared-context-bank-assets.json": "site/prepared/prepared-context-bank-assets.json", - "site/prepared-context-json.mts": "site/prepared/prepared-context-json.mts", - "site/prepared-context-objects.mts": "site/prepared/prepared-context-objects.mts", - "site/prepared-dataset-billboards.json": "site/prepared/prepared-dataset-billboards.json", - "site/prepared-dataset-volumes.json": "site/prepared/prepared-dataset-volumes.json", - "site/prepared-dot-catalogues.json": "site/prepared/prepared-dot-catalogues.json", - "site/prepared-facilities.json": "site/prepared/prepared-facilities.json", - "site/prepared-feature-index.json": "site/prepared/prepared-feature-index.json", - "site/prepared-fit-boxes.json": "site/prepared/prepared-fit-boxes.json", - "site/prepared-hosted-banks.json": "site/prepared/prepared-hosted-banks.json", - "site/prepared-navigation-markers.mjs": "site/prepared/prepared-navigation-markers.mjs", - "site/prepared-shell-icons.mjs": "site/prepared/prepared-shell-icons.mjs", - "site/prepared-shell-titles.mjs": "site/prepared/prepared-shell-titles.mjs", - "site/prepared-source-credits.json": "site/prepared/prepared-source-credits.json", - "site/prepared-sources.json": "site/prepared/prepared-sources.json", - "site/prepared-stellar-extents.json": "site/prepared/prepared-stellar-extents.json", - "site/prepared-world-presentation.json": "site/prepared/prepared-world-presentation.json", "site/object-shell.css": "site/layouts/object-shell.css", "site/destination-browser.characterization.test.mts": "site/shell/destination-browser.characterization.test.mts", "site/diagnostic-recorder.characterization.test.mts": "site/shell/diagnostic-recorder.characterization.test.mts", diff --git a/docs/site-architecture/tiers.json b/docs/site-architecture/tiers.json index 18291227d03..a5f6fe0fbba 100644 --- a/docs/site-architecture/tiers.json +++ b/docs/site-architecture/tiers.json @@ -358,28 +358,28 @@ ], "assets": [ "site/layouts/cubic-sky.css", - "site/moon-labels.prepared.json", + "site/prepared/moon-labels.prepared.json", "site/navigation/evidence/star-click/one-click.jpg", "site/layouts/object-navigation-marker.css", "site/object-shell.css", - "site/prepared-catalogue.mjs", - "site/prepared-context-bank-assets.json", - "site/prepared-context-json.mts", - "site/prepared-context-objects.mts", - "site/prepared-dataset-billboards.json", - "site/prepared-dataset-volumes.json", - "site/prepared-dot-catalogues.json", - "site/prepared-facilities.json", - "site/prepared-feature-index.json", - "site/prepared-fit-boxes.json", - "site/prepared-hosted-banks.json", - "site/prepared-navigation-markers.mjs", - "site/prepared-shell-icons.mjs", - "site/prepared-shell-titles.mjs", - "site/prepared-source-credits.json", - "site/prepared-sources.json", - "site/prepared-stellar-extents.json", - "site/prepared-world-presentation.json", + "site/prepared/prepared-catalogue.mjs", + "site/prepared/prepared-context-bank-assets.json", + "site/prepared/prepared-context-json.mts", + "site/prepared/prepared-context-objects.mts", + "site/prepared/prepared-dataset-billboards.json", + "site/prepared/prepared-dataset-volumes.json", + "site/prepared/prepared-dot-catalogues.json", + "site/prepared/prepared-facilities.json", + "site/prepared/prepared-feature-index.json", + "site/prepared/prepared-fit-boxes.json", + "site/prepared/prepared-hosted-banks.json", + "site/prepared/prepared-navigation-markers.mjs", + "site/prepared/prepared-shell-icons.mjs", + "site/prepared/prepared-shell-titles.mjs", + "site/prepared/prepared-source-credits.json", + "site/prepared/prepared-sources.json", + "site/prepared/prepared-stellar-extents.json", + "site/prepared/prepared-world-presentation.json", "site/shell/maps-shell.css", "site/shell/settings-panel.css", "site/shell/shell-layout.css", diff --git a/docs/sources-catalogue.md b/docs/sources-catalogue.md index 522cedbdae7..a80d1d0c979 100644 --- a/docs/sources-catalogue.md +++ b/docs/sources-catalogue.md @@ -25,9 +25,9 @@ and ESA's own images are share-alike. Each render-library entry carries its own `license`, so the obligation stays attached to the file it covers. Reusing that file elsewhere carries the share-alike terms with it. -`site/prepared-sources.json` and `site/prepared-facilities.json` are generated +`site/prepared/prepared-sources.json` and `site/prepared/prepared-facilities.json` are generated from these records and the existing product lineage, with -`site/prepared-source-credits.json`: the short provider list each page's +`site/prepared/prepared-source-credits.json`: the short provider list each page's "Sources" link shows, and the rows of the body card's Sources tab. A published work is one row: its title, kind, publisher and landing page. Files that have no published title are one row per credit line, with their count and the datasets diff --git a/eslint.config.mts b/eslint.config.mts index 97a0738e1cf..8bc889ce0c6 100644 --- a/eslint.config.mts +++ b/eslint.config.mts @@ -25,7 +25,7 @@ export default [ // Generated output and the open-ended registries. `src/platform/solar-geometry.mts` alone is // 26,968 generated lines; `src/objects` is 586 authored body packages, not modules. '**/prepared/**', '**/generated/**', 'src/objects/**/*', '!src/objects/**/', '!src/objects/**/*.test.mts', 'src/sources/**/*', '!src/sources/**/*.test.mts', - 'src/platform/solar-geometry.mts', 'site/prepared-context-objects.mts'] }, + 'src/platform/solar-geometry.mts', 'site/prepared/prepared-context-objects.mts'] }, { // `src` and `site` (and once `tools`) — roughly 232,000 authored lines — had no ESLint at all, so the // size and boundary rules below governed only the two smallest trees. Warnings, not errors: diff --git a/labs/performance/README.md b/labs/performance/README.md index 4e60d42dc47..cd88d2f6daa 100644 --- a/labs/performance/README.md +++ b/labs/performance/README.md @@ -113,7 +113,7 @@ untracked files. Before these changes it spent about 9 s of every capture on sle A cold dev load of Venus used to take 7.3 s on the iPad. Every page imported all 2,071 prepared files of the 29 context objects as one `?url` module each. Safari needed about 4 s to resolve them, and no frame rendered until it had. -`site/prepared-context-objects.mts` now lists those files from each object's inventory, as the asset-origin build +`site/prepared/prepared-context-objects.mts` now lists those files from each object's inventory, as the asset-origin build always did. The load takes 3.3 s with 263 requests instead of 2,335. ### Counting late frames: the page times itself diff --git a/packages/bake/AGENTS.md b/packages/bake/AGENTS.md index df1b4692f40..b13301db102 100644 --- a/packages/bake/AGENTS.md +++ b/packages/bake/AGENTS.md @@ -109,7 +109,7 @@ cycle. Share anything else through `@cssearth/core` or another package. Prepared the catalogue and search destinations are built with, and the marker recipes whose source bytes are checked and drawn into navigation marker sprites, and the navigation preparation (`prepare-navigation.ts`, with the Sun, black-hole, supernova and action marker sources in `marker-descriptors.ts`) that writes the marker atlases, action markers and - `site/prepared-navigation-markers.mjs`, and the flat neutral disc (`neutral-disc-marker.ts`) that marks an unresolved, + `site/prepared/prepared-navigation-markers.mjs`, and the flat neutral disc (`neutral-disc-marker.ts`) that marks an unresolved, self-luminous surface in generated and authored packages. It reads the registry on first use, not at import, and validates marker presentation with the shared rules in `@cssearth/objects` (`marker-presentation.ts`, which the shell also uses to draw them). It imports `raster`, `delivery`, `sources`, `astronomy`, and `objects/raster` (loaded only when a marker is diff --git a/packages/bake/README.md b/packages/bake/README.md index c39cd0e9538..c95f586df75 100644 --- a/packages/bake/README.md +++ b/packages/bake/README.md @@ -130,7 +130,7 @@ renderer enforces. Every other bank is a bake input written to the ignored `outp inventoried, never published, and never imported as a module (`packages/bake/src/volume/node/catalogue-banks.ts`). Vite serves an imported JSON file as an array literal, which Safari cannot compile past about a hundred thousand elements, and the site's build reads only `datasets` and `presentation` from a context object's `prepared/`, plus its source manifest -(`site/prepared-context-json.mts`). +(`site/prepared/prepared-context-json.mts`). Presentation compilers import `PREPARED_PRESENTATION_SCHEMA`, prepared material/selection records and the leaf-box data shape from `@cssearth/objects`. The sky/Sun-dependent presentation-envelope validator remains in bake. diff --git a/packages/bake/src/navigation/prepare-navigation.ts b/packages/bake/src/navigation/prepare-navigation.ts index d70d11ce7de..5716adb8bad 100644 --- a/packages/bake/src/navigation/prepare-navigation.ts +++ b/packages/bake/src/navigation/prepare-navigation.ts @@ -77,7 +77,7 @@ export async function prepareNavigation({ projectRoot = ROOT, outputRoot = resolve(projectRoot, "public/navigation"), planets = PLANET_MARKER_PLANETS(), - presentationPath = resolve(projectRoot, "site/prepared-navigation-markers.mjs"), + presentationPath = resolve(projectRoot, "site/prepared/prepared-navigation-markers.mjs"), moveFile = moveNavigationFile, objectIds, catalogOnly = false, diff --git a/packages/bake/src/site-assets/prepare-dataset-billboards.ts b/packages/bake/src/site-assets/prepare-dataset-billboards.ts index 32efc949def..469f5a9cfcd 100644 --- a/packages/bake/src/site-assets/prepare-dataset-billboards.ts +++ b/packages/bake/src/site-assets/prepare-dataset-billboards.ts @@ -25,7 +25,7 @@ import { isRecord, requireArray, requireRecord, requireString } from '@cssearth/ import { readInventory, readVolumeDatasetBank } from '@cssearth/objects/node'; /** The images are served from the site's own `public/` tree, beside the other generated navigation images. */ -const OUTPUT = { metadata: 'site/prepared-dataset-billboards.json', images: 'public/navigation/dataset-billboards' }; +const OUTPUT = { metadata: 'site/prepared/prepared-dataset-billboards.json', images: 'public/navigation/dataset-billboards' }; /** Prepared impostor views are 256 px squares; billboards keep them at their prepared size. */ const CELL_PX = 256; @@ -214,6 +214,6 @@ export async function prepareDatasetBillboards(projectRoot = checkoutProjectRoot datasets: bank.datasets.map(dataset => ({ id: dataset.id, radiusUnits: dataset.radiusUnits, ...dataset.view })) } : {}) })) }; await writeIfChanged(resolve(projectRoot, OUTPUT.metadata), `${JSON.stringify(metadata)}\n`); // The atlas an earlier checkout wrote here is no longer read. - await rm(resolve(projectRoot, 'site/prepared-dataset-billboards.webp'), { force: true }); + await rm(resolve(projectRoot, 'site/prepared/prepared-dataset-billboards.webp'), { force: true }); return { banks: banks.length, billboards: drawn.length, imageBytes }; } diff --git a/packages/bake/src/site-assets/prepare-search-thumbnails.test.mts b/packages/bake/src/site-assets/prepare-search-thumbnails.test.mts index e45ce0c81ef..f0e0a06ca3d 100644 --- a/packages/bake/src/site-assets/prepare-search-thumbnails.test.mts +++ b/packages/bake/src/site-assets/prepare-search-thumbnails.test.mts @@ -14,16 +14,16 @@ const picture = (red: number, green: number, blue: number) => sharp({ create: { test('featured-star previews reuse arrival pixels, including an older restored arrival, while other markers stay unchanged', async () => { const root = await mkdtemp(join(tmpdir(), 'cssearth-search-preview-')); try { - await Promise.all(['site', 'public/navigation', 'public/scenes/star'].map(path => mkdir(join(root, path), { recursive: true }))); + await Promise.all(['site/prepared', 'public/navigation', 'public/scenes/star'].map(path => mkdir(join(root, path), { recursive: true }))); const context = await picture(240, 80, 40), arrival = join(root, 'public/scenes/star/arrival.webp'); await Promise.all([ writeFile(join(root, 'public/navigation/star-context.webp'), context), writeFile(join(root, 'public/navigation/other-context.webp'), context), writeFile(arrival, await picture(120, 120, 120)), - writeFile(join(root, 'site/prepared-navigation-markers.mjs'), `export const PREPARED_NAVIGATION_MARKERS = ${JSON.stringify({ + writeFile(join(root, 'site/prepared/prepared-navigation-markers.mjs'), `export const PREPARED_NAVIGATION_MARKERS = ${JSON.stringify({ star: { context: { url: '/navigation/star-context.webp' } }, other: { context: { url: '/navigation/other-context.webp' } }, })};`), - writeFile(join(root, 'site/prepared-catalogue.mjs'), `export const CATALOGUE_ENTRIES = ${JSON.stringify([ + writeFile(join(root, 'site/prepared/prepared-catalogue.mjs'), `export const CATALOGUE_ENTRIES = ${JSON.stringify([ { kind: 'scene', descriptor: { id: 'star', properties: { catalog: { classification: 'star' } } }, discovery: { featured: true, imagery: false, illustration: false, arrival: { defaultDataset: 'color', datasetIds: ['color'], rotation, billboard: { url: '/scenes/star/arrival.webp', size: 80, focalPixels: 50, distanceM: 100, dataset: 'color', rotation } } } }, @@ -51,7 +51,7 @@ test('featured-star previews reuse arrival pixels, including an older restored a test('an opaque sprite previews its complete marker picture, faded out before the frame', async () => { const root = await mkdtemp(join(tmpdir(), 'cssearth-search-preview-')); try { - await Promise.all(['site', 'public/navigation', 'src/objects/galaxy/source/preparation', 'src/objects/galaxy/source/presentation'] + await Promise.all(['site/prepared', 'public/navigation', 'src/objects/galaxy/source/preparation', 'src/objects/galaxy/source/presentation'] .map(path => mkdir(join(root, path), { recursive: true }))); // A wide photograph that fills its frame, and the square sprite the marker cuts from its middle. const photograph = (width: number, height: number) => sharp({ create: { width, height, channels: 3, background: { r: 200, g: 180, b: 160 } } }); @@ -59,9 +59,9 @@ test('an opaque sprite previews its complete marker picture, faded out before th photograph(600, 200).png().toFile(join(root, 'src/objects/galaxy/source/presentation/context.png')), photograph(200, 200).webp({ lossless: true }).toFile(join(root, 'public/navigation/galaxy-context.webp')), writeFile(join(root, 'src/objects/galaxy/source/preparation/navigation.json'), JSON.stringify({ source: { path: 'presentation/context.png' } })), - writeFile(join(root, 'site/prepared-navigation-markers.mjs'), `export const PREPARED_NAVIGATION_MARKERS = ${JSON.stringify({ + writeFile(join(root, 'site/prepared/prepared-navigation-markers.mjs'), `export const PREPARED_NAVIGATION_MARKERS = ${JSON.stringify({ galaxy: { context: { url: '/navigation/galaxy-context.webp' } } })};`), - writeFile(join(root, 'site/prepared-catalogue.mjs'), 'export const CATALOGUE_ENTRIES = [];'), + writeFile(join(root, 'site/prepared/prepared-catalogue.mjs'), 'export const CATALOGUE_ENTRIES = [];'), ]); assert.equal((await prepareSearchThumbnails(root)).written, 1); const { data } = await sharp(join(root, 'public/navigation/search/galaxy@2x.webp')).ensureAlpha().raw().toBuffer({ resolveWithObject: true }); diff --git a/packages/bake/src/site-assets/prepare-search-thumbnails.ts b/packages/bake/src/site-assets/prepare-search-thumbnails.ts index e4ee54b5fcb..2a2b70ac0e0 100644 --- a/packages/bake/src/site-assets/prepare-search-thumbnails.ts +++ b/packages/bake/src/site-assets/prepare-search-thumbnails.ts @@ -43,7 +43,7 @@ async function markerPicture(projectRoot: string, id: string): Promise> { - const module: unknown = await import(pathToFileURL(resolve(projectRoot, 'site/prepared-catalogue.mjs')).href); + const module: unknown = await import(pathToFileURL(resolve(projectRoot, 'site/prepared/prepared-catalogue.mjs')).href); if (!isRecord(module) || !Array.isArray(module.CATALOGUE_ENTRIES)) throw new TypeError('Invalid prepared catalogue.'); const previews = new Map(); for (const entry of module.CATALOGUE_ENTRIES) { @@ -63,7 +63,7 @@ async function featuredStarPreviews(projectRoot: string): Promise }; const output = resolve(projectRoot, 'public/navigation/search'); await mkdir(output, { recursive: true }); diff --git a/packages/bake/src/sources/facility-artwork-refresh.ts b/packages/bake/src/sources/facility-artwork-refresh.ts index 88908ba803d..797a0ea4653 100644 --- a/packages/bake/src/sources/facility-artwork-refresh.ts +++ b/packages/bake/src/sources/facility-artwork-refresh.ts @@ -10,8 +10,8 @@ import { parsePreparedSources, parsePreparedExploration, parseExplorationImage, * routes, which validate the prepared facilities and sources. */ export async function prepareArtworkRefresh(root: string, before: Buffer, after: Buffer, images: ReadonlyMap, routes: DatasetRoutes) { const previous = requireRecord(JSON.parse(before.toString())), next = requireRecord(JSON.parse(after.toString())); - const facilities = requireRecord(JSON.parse(await readFile(resolve(root, 'site/prepared-facilities.json'), 'utf8'))); - const sources = requireRecord(JSON.parse(await readFile(resolve(root, 'site/prepared-sources.json'), 'utf8'))); + const facilities = requireRecord(JSON.parse(await readFile(resolve(root, 'site/prepared/prepared-facilities.json'), 'utf8'))); + const sources = requireRecord(JSON.parse(await readFile(resolve(root, 'site/prepared/prepared-sources.json'), 'utf8'))); const validatedSources = parsePreparedSources(sources, routes), validatedFacilities = parsePreparedExploration(facilities, validatedSources.sources, routes); const previousEntries = requireArray(previous.entries).map(value => requireRecord(value)); const nextEntries = requireArray(next.entries).map(value => requireRecord(value)); @@ -33,6 +33,6 @@ export async function prepareArtworkRefresh(root: string, before: Buffer, after: } facilities.images = prepared; parsePreparedExploration(facilities, validatedSources.sources, routes); parsePreparedSources(sources, routes); - return [{ path: resolve(root, 'site/prepared-facilities.json'), text: JSON.stringify(facilities, null, 2) + '\n' }, - { path: resolve(root, 'site/prepared-sources.json'), text: JSON.stringify(sources, null, 2) + '\n' }]; + return [{ path: resolve(root, 'site/prepared/prepared-facilities.json'), text: JSON.stringify(facilities, null, 2) + '\n' }, + { path: resolve(root, 'site/prepared/prepared-sources.json'), text: JSON.stringify(sources, null, 2) + '\n' }]; } diff --git a/packages/objects/README.md b/packages/objects/README.md index a730b4ea958..0acaf0e2ea4 100644 --- a/packages/objects/README.md +++ b/packages/objects/README.md @@ -16,7 +16,7 @@ entry of the prepared catalogue, as the registry and a page's object directory b reads, `contextColor()` picks a body's world-context color, and `validateWorldRotation()` checks a rotation. The host binds `loadScene` to its own scene type; the site's client build compiles these modules from source, one module each. Preparation reads the same registry through `readPreparedObjects(root)` in `@cssearth/objects/node`: it decodes the -prepared catalogue `prepare:catalog` writes (`PREPARED_CATALOGUE`: `site/prepared-catalogue.mjs`, every scene object's +prepared catalogue `prepare:catalog` writes (`PREPARED_CATALOGUE`: `site/prepared/prepared-catalogue.mjs`, every scene object's descriptor with its distance and discovery and every prepared focus in registry order, written by `preparedCatalogueModule()`) with these contracts and binds a `loadScene` that refuses to mount, so a preparer that lists objects through it does not import the application. A site test (run in the universe runtime lane) holds both reads equal. diff --git a/packages/objects/src/node/prepared-registry.test.ts b/packages/objects/src/node/prepared-registry.test.ts index 6a83e526032..1c918f3b68f 100644 --- a/packages/objects/src/node/prepared-registry.test.ts +++ b/packages/objects/src/node/prepared-registry.test.ts @@ -42,7 +42,7 @@ async function checkout(records: { scenes?: PreparedCatalogueRow[]; focuses?: un await mkdir(join(root, 'src/objects', id), { recursive: true }); await writeFile(join(root, 'src/objects', id, 'object.json'), JSON.stringify(descriptor(id, order, classification, 'milky-way'))); } - await mkdir(join(root, 'site')); + await mkdir(join(root, 'site/prepared'), { recursive: true }); // The catalogue order is the order prepare:catalog wrote, not the alphabetical one. await writeFile(join(root, PREPARED_CATALOGUE.entries), records.module ?? preparedCatalogueModule([...records.scenes ?? scenes, ...records.focuses ?? [focus], ...records.insides ?? insides] as PreparedCatalogueRow[])); diff --git a/packages/objects/src/node/prepared-registry.ts b/packages/objects/src/node/prepared-registry.ts index 963f29a4a12..47e0d0010bc 100644 --- a/packages/objects/src/node/prepared-registry.ts +++ b/packages/objects/src/node/prepared-registry.ts @@ -10,7 +10,7 @@ import type { NavigableObject, NavigationDistance, ObjectDiscovery, WorldBody } /** The catalogue file, relative to the checkout: a module of each object's descriptor, as its folder's object.json holds * it, with its distance and discovery, in registry order. A page never loads it: it reads one entry at a time. */ export const PREPARED_CATALOGUE = Object.freeze({ - entries: 'site/prepared-catalogue.mjs', + entries: 'site/prepared/prepared-catalogue.mjs', }); /** One row of the prepared catalogue: a package's descriptor with its distance and discovery. Every object's row has this diff --git a/packages/objects/src/prepared-data/source/format-reader-ledger.ts b/packages/objects/src/prepared-data/source/format-reader-ledger.ts index 4d95c003975..765e6a93e56 100644 --- a/packages/objects/src/prepared-data/source/format-reader-ledger.ts +++ b/packages/objects/src/prepared-data/source/format-reader-ledger.ts @@ -66,5 +66,5 @@ export const FORMAT_READER_POLICIES: readonly FormatReaderPolicy[] = [ { schema: SYSTEM_TEXT_SCHEMA, readers: ['readSystemText'], paths: ['navigation/system-text.json'] }, { schema: OBJECT_TEXT_SCHEMA, readers: ['parseObjectText'], paths: ['text.json'] }, { schema: PREPARED_TEXT_SCHEMA, readers: ['parsePreparedText'], paths: ['prepared/text.json'] }, - { schema: PREPARED_EXPLORATION_SCHEMA, readers: ['parsePreparedExploration'], paths: ['site/prepared-facilities.json'] }, + { schema: PREPARED_EXPLORATION_SCHEMA, readers: ['parsePreparedExploration'], paths: ['site/prepared/prepared-facilities.json'] }, ]; diff --git a/packages/objects/src/provenance/source-credits.ts b/packages/objects/src/provenance/source-credits.ts index 523037e75bc..6bc82cabff2 100644 --- a/packages/objects/src/provenance/source-credits.ts +++ b/packages/objects/src/provenance/source-credits.ts @@ -1,6 +1,6 @@ /** The short provider index each page's "Sources" link shows and the rows its Sources tab lists, computed once from the prepared source usage when the catalogue * is written (site/build/prepare/prepare-facilities.mts). The site reads this file instead of the whole catalogue: with batch 1 of the exoplanets - * (2026-09-29) `site/prepared-sources.json` reached 41 MB, and importing it put the dev server and the site typecheck past 4 GB. + * (2026-09-29) `site/prepared/prepared-sources.json` reached 41 MB, and importing it put the dev server and the site typecheck past 4 GB. * The complete author and institutional credits stay in each body's README, which the link opens. */ import { isRecord } from '@cssearth/core'; import type { SourceKind, SourceRecord, SourceResolver } from '../sources/catalog.js'; diff --git a/packages/telescope-cli/src/query.mts b/packages/telescope-cli/src/query.mts index 85297582172..0ff470506c6 100644 --- a/packages/telescope-cli/src/query.mts +++ b/packages/telescope-cli/src/query.mts @@ -347,7 +347,7 @@ export const LEDGER_TELESCOPES: readonly string[] = Object.keys(ADAPTERS); /** Load only the shipped target catalogue, so human entry points can resolve ambiguity before archive access. */ export async function loadTargetCatalogue(root: string): Promise { // These package descriptors are the source of the application's generated catalogue. Reading them keeps this CLI usable - // in a clean checkout, before `prepare` has emitted site/prepared-catalogue.mjs. + // in a clean checkout, before `prepare` has emitted site/prepared/prepared-catalogue.mjs. const objectRoot = resolve(root, 'src/objects'), targetCatalogue: TargetCatalogueEntry[] = []; for (const directory of (await readdir(objectRoot, { withFileTypes: true })).filter(entry => entry.isDirectory()).map(entry => entry.name).sort()) { const value = await readJsonSource(resolve(objectRoot, directory, 'object.json')).catch((error: unknown) => { if (hasErrorCode(error, 'ENOENT')) return undefined; throw error; }); diff --git a/site/application-world-context.characterization.test.mts b/site/application-world-context.characterization.test.mts index e6278ea4d74..bd75655b62d 100644 --- a/site/application-world-context.characterization.test.mts +++ b/site/application-world-context.characterization.test.mts @@ -45,7 +45,7 @@ mock.module(new URL('./application-world-frames.mts', import.meta.url).href, { n mock.module(new URL('./application-world-visibility.mts', import.meta.url).href, { namedExports: { worldVisibilityPolicy: { minorMoonIds: [] }, createApplicationWorldVisibility: () => ({ selectObject: (id: string) => effects.push(['visibility-select', id]), setSurrounding: (ids: readonly string[]) => effects.push(['surrounding', ids]), setIllustrationModelsEnabled() {}, setHighlightedClassification() {} }) } }); // One holder's picture lies on walls: the bank of 'known' says so. -mock.module(new URL('./prepared-context-objects.mts', import.meta.url).href, { namedExports: { CONTEXT_OBJECT_DESCRIPTORS: { 'known-layers': { properties: { surrounds: true, host: 'known' } }, 'outer-layers': { properties: { host: 'mars-parent' } } } } }); +mock.module(new URL('./prepared/prepared-context-objects.mts', import.meta.url).href, { namedExports: { CONTEXT_OBJECT_DESCRIPTORS: { 'known-layers': { properties: { surrounds: true, host: 'known' } }, 'outer-layers': { properties: { host: 'mars-parent' } } } } }); mock.module(new URL('./object-directory.mts', import.meta.url).href, { namedExports: { knownAncestors: (...args: unknown[]) => { effects.push(['known-ancestors', ...args]); return [{ id: 'known' }]; }, ancestorIds: (...args: unknown[]) => { effects.push(['ancestor-ids', ...args]); return ancestorResult; } } }); mock.module(new URL('./world/context-availability.mts', import.meta.url).href, { namedExports: { CONTEXT_AVAILABILITY: {} } }); mock.module(new URL('./browser/diagnostics-policy.mts', import.meta.url).href, { namedExports: { DIAGNOSTICS_ENABLED: true } }); diff --git a/site/application-world-context.mts b/site/application-world-context.mts index a08bc291396..b8adf6087c3 100644 --- a/site/application-world-context.mts +++ b/site/application-world-context.mts @@ -14,7 +14,7 @@ import { suppressMinorMoonOrbitPaint } from './world/moon-orbit-policy.mts'; import { mountCatalogueMoonLabels } from './catalogue-moon-labels.mts'; import { loadApplicationUniverse } from './application-world-resources.mts'; import { ancestorIds, knownAncestors } from './object-directory.mts'; -import { CONTEXT_OBJECT_DESCRIPTORS } from './prepared-context-objects.mts'; +import { CONTEXT_OBJECT_DESCRIPTORS } from './prepared/prepared-context-objects.mts'; import { surroundingHosts } from './world/surrounded-body.mts'; /** The world's prepared data and planner worker, which `startup-boot.mts` starts while the first body still loads. */ diff --git a/site/application-world-resources.characterization.test.mts b/site/application-world-resources.characterization.test.mts index 9975d2b3e50..b7564c95232 100644 --- a/site/application-world-resources.characterization.test.mts +++ b/site/application-world-resources.characterization.test.mts @@ -42,7 +42,7 @@ const requests: string[] = []; let bankResponse: Response | null = null; let catalogLoads = 0, plannerCount = 0; let loadFailure: Error | null = new Error('initial volume decode'); -mock.module(new URL('./prepared-context-objects.mts', import.meta.url).href, { namedExports: { CONTEXT_OBJECT_DESCRIPTORS: declarations, CONTEXT_OBJECT_ASSET_URLS: assets, CONTEXT_GALAXY_SAMPLE: {} } }); +mock.module(new URL('./prepared/prepared-context-objects.mts', import.meta.url).href, { namedExports: { CONTEXT_OBJECT_DESCRIPTORS: declarations, CONTEXT_OBJECT_ASSET_URLS: assets, CONTEXT_GALAXY_SAMPLE: {} } }); mock.module(new URL('./world/context-availability.mts', import.meta.url).href, { namedExports: { CONTEXT_AVAILABILITY: availability } }); mock.module(new URL('./world-context-plan.mts', import.meta.url).href, { namedExports: { ...world, WORLD_DOT_BANKS: ['inside'], onWorldSystems: (callback: typeof onSystems) => { onSystems = callback; return () => {}; } } }); diff --git a/site/application-world-resources.mts b/site/application-world-resources.mts index 862cd649d79..cea46d206d0 100644 --- a/site/application-world-resources.mts +++ b/site/application-world-resources.mts @@ -2,11 +2,11 @@ import { IMAGE_MESH_SCHEMA, bankCataloguePoints, parseDatasetBillboards, parseCa import { DATASET_VISIBILITY } from './browser/runtime-policy.mts'; import { isRecord } from '@cssearth/core'; // Generated after the prepared dataset payloads are restored: text now, validated below. -import datasetBillboardText from './prepared-dataset-billboards.json?raw'; +import datasetBillboardText from './prepared/prepared-dataset-billboards.json?raw'; import { createPreparedUniverse, loadPreparedCssVolume, loadPreparedPointAppearance, loadPreparedCssSurfaceShell, loadPreparedCssImageLayers, loadPreparedVolumeDatasets } from '@cssearth/renderer/universe'; import { APPLICATION_WORLD_CONTEXT as applicationContext, APPLICATION_WORLD_PLANNER_SOURCE, WORLD_DOT_BANKS, onWorldSystems } from './world-context-plan.mts'; import { preparedBodyBillboards } from '@cssearth/renderer/navigation/prepared-body-billboards.ts'; -import { CONTEXT_OBJECT_ASSET_URLS, CONTEXT_OBJECT_DESCRIPTORS } from './prepared-context-objects.mts'; +import { CONTEXT_OBJECT_ASSET_URLS, CONTEXT_OBJECT_DESCRIPTORS } from './prepared/prepared-context-objects.mts'; import { CONTEXT_AVAILABILITY } from './world/context-availability.mts'; import { PREPARED_WORLD_PRESENTATION } from './prepared-world-presentation.mts'; import { createInFlightLoader } from './browser/in-flight-loader.mts'; diff --git a/site/build/content/prepare.ts b/site/build/content/prepare.ts index a383c6200b3..bd2ac266955 100644 --- a/site/build/content/prepare.ts +++ b/site/build/content/prepare.ts @@ -5,7 +5,7 @@ import { readChartAssetRecipe } from '@cssearth/objects'; import { PREPARED_CONTENT_SCHEMA, validateObjectContentEnvelope, type ObjectContentSource, type PreparedObjectContent, type PreparedObjectContentDocument, type GalleryRecipe } from '@cssearth/objects'; import { mkdir, readFile, writeFile } from "node:fs/promises"; import { basename, resolve } from "node:path"; -import { PREPARED_SHELL_TITLES } from "../../prepared-shell-titles.mjs"; +import { PREPARED_SHELL_TITLES } from "../../prepared/prepared-shell-titles.mjs"; import { datasetBillboardColors, prepareDatasetLabels, prepareDatasets } from "@cssearth/bake/objects/content"; import { parseFactsheet, verifyFactsheetSources } from '@cssearth/bake/sources'; import type { ContentPreparationContext, PreparedObjectContentAssets, PreparedRasterAssets } from '@cssearth/bake/objects/content'; diff --git a/site/build/prepare/body-additions.test.mts b/site/build/prepare/body-additions.test.mts index cbd4e2bea7c..bbc2c5a791e 100644 --- a/site/build/prepare/body-additions.test.mts +++ b/site/build/prepare/body-additions.test.mts @@ -95,7 +95,7 @@ test('independent asteroid, moon and comet branches merge without changing exist git('config', 'user.email', 'test@example.invalid'); git('config', 'commit.gpgsign', 'false'); git('config', 'core.hooksPath', '/dev/null'); - await write(resolve(root, '.gitignore'), '/site/prepared-catalogue.mjs\n/site/prepared-overview-objects.json\n/site/prepared-context-objects.mts\n/packages/astronomy/src/data/generated/\n'); + await write(resolve(root, '.gitignore'), '/site/prepared/prepared-catalogue.mjs\n/site/prepared/prepared-overview-objects.json\n/site/prepared/prepared-context-objects.mts\n/packages/astronomy/src/data/generated/\n'); await mkdir(resolve(root, 'packages/astronomy/data/fixtures'), { recursive: true }); await addBody(root, 'sun', 'star'); await addBody(root, 'existing-body', 'asteroid'); diff --git a/site/build/prepare/prepare-catalog.mts b/site/build/prepare/prepare-catalog.mts index 20e2223c6b7..72256a67027 100644 --- a/site/build/prepare/prepare-catalog.mts +++ b/site/build/prepare/prepare-catalog.mts @@ -32,15 +32,16 @@ export function dotCatalogueIds(contexts: readonly { id: string; type?: string } /** Every object shares one folder, so the application globs only the context objects it loads, each by name. Their * prepared resources are a literal map from each inventory (contextObjectAssetUrls), never a module per file: an eager - * `?url` glob made every page import all 2,071 files of 29 context objects in dev, 4 s of Safari's module loading. */ + * `?url` glob made every page import all 2,071 files of 29 context objects in dev, 4 s of Safari's module loading. + * The module sits in site/prepared; each glob takes `base: '..'` so its keys stay `../src/objects//...`. */ export function contextObjectModule(contexts: readonly { id: string; type?: string }[], assets: Readonly>) { const list = (patterns: readonly string[]) => `[${patterns.map(pattern => `'${pattern}'`).join(', ')}]`; const galaxies = dotCatalogueIds(contexts).galaxies; return '// Generated by pnpm prepare:catalog. Context objects are registered descriptors without a catalog entry.\n' + // The galaxy catalogue's display sample (which of its rows are drawn as dots) comes with the application. - (galaxies === undefined ? '' : `import galaxySample from '../src/objects/${galaxies}/prepared/display-sample.json' with { type: 'json' };\n`) + + (galaxies === undefined ? '' : `import galaxySample from '../../src/objects/${galaxies}/prepared/display-sample.json' with { type: 'json' };\n`) + `export const CONTEXT_GALAXY_SAMPLE: unknown = ${galaxies === undefined ? 'undefined' : 'galaxySample'};\n` + - `export const CONTEXT_OBJECT_DESCRIPTORS: Record = import.meta.glob(${list(contexts.map(({ id }) => `../src/objects/${id}/object.json`))}, { eager: true, import: 'default' });\n` + + `export const CONTEXT_OBJECT_DESCRIPTORS: Record = import.meta.glob(${list(contexts.map(({ id }) => `../src/objects/${id}/object.json`))}, { base: '..', eager: true, import: 'default' });\n` + // Every prepared resource stays an independently cached, demand-loaded file; the loaders decide which bank is fetched. `export const CONTEXT_OBJECT_ASSET_URLS: Record = ${JSON.stringify(assets)};\n`; } @@ -54,8 +55,8 @@ export const CONTEXT_OBJECT_PREPARED_FILES = ['datasets', 'datasets', 'presentat export function contextObjectJsonModule(contexts: readonly { id: string }[]) { const files = `{${CONTEXT_OBJECT_PREPARED_FILES.join(',')}}.json`; return '// Generated by site/build/prepare/prepare-catalog.mts for the build only: the prepared files pages read while rendering.\n' + - `export const CONTEXT_OBJECT_PREPARED_JSON: Record = import.meta.glob([${contexts.map(({ id }) => `'../src/objects/${id}/prepared/${files}'`).join(', ')}], { eager: true, import: 'default' });\n` + - `export const CONTEXT_OBJECT_SOURCE_MANIFESTS: Record = import.meta.glob([${contexts.map(({ id }) => `'../src/objects/${id}/source/manifest.json'`).join(', ')}], { eager: true, import: 'default' });\n`; + `export const CONTEXT_OBJECT_PREPARED_JSON: Record = import.meta.glob([${contexts.map(({ id }) => `'../src/objects/${id}/prepared/${files}'`).join(', ')}], { base: '..', eager: true, import: 'default' });\n` + + `export const CONTEXT_OBJECT_SOURCE_MANIFESTS: Record = import.meta.glob([${contexts.map(({ id }) => `'../src/objects/${id}/source/manifest.json'`).join(', ')}], { base: '..', eager: true, import: 'default' });\n`; } /** A bank a body's dataset shows (a galaxy's image layers, a nebula's volume). Its files are named when it is loaded, not @@ -110,7 +111,7 @@ export async function readHostedContextBanks(contexts: readonly { id: string; ty return hosted; } -/** The build-only record of the hosted banks (`site/prepared-hosted-banks.json`, read by `site/hosted-banks.mts`): each +/** The build-only record of the hosted banks (`site/prepared/prepared-hosted-banks.json`, read by `site/hosted-banks.mts`): each * bank's carriers and descriptor, and the files of a bank that has no list of its own (`/world/context-assets/.json`). */ export function hostedBankRecords(hosted: Readonly>, descriptors: ReadonlyMap, inline: Readonly>) { @@ -268,26 +269,26 @@ export async function prepareCatalog({ projectRoot = root } = {}) { const descriptor = descriptors.get(id); return { descriptor, distance, discovery: discoveries[index]! }; }))); - await rm(resolve(projectRoot, 'site/prepared-overview-objects.json'), { force: true }); + await rm(resolve(projectRoot, 'site/prepared/prepared-overview-objects.json'), { force: true }); const datasetBanks = await readDatasetBanks(entries, projectRoot); - await writeGenerated(resolve(projectRoot, 'site/prepared-dataset-volumes.json'), JSON.stringify(await readDatasetVolumes(datasetBanks, projectRoot)) + '\n'); - await writeGenerated(resolve(projectRoot, 'site/prepared-fit-boxes.json'), JSON.stringify(prepareFitBoxes(descriptors)) + '\n'); + await writeGenerated(resolve(projectRoot, 'site/prepared/prepared-dataset-volumes.json'), JSON.stringify(await readDatasetVolumes(datasetBanks, projectRoot)) + '\n'); + await writeGenerated(resolve(projectRoot, 'site/prepared/prepared-fit-boxes.json'), JSON.stringify(prepareFitBoxes(descriptors)) + '\n'); await rm(resolve(projectRoot, 'site/prepared-local-group-galaxies.json'), { force: true }); const contexts = await readContextObjects(resolve(projectRoot, 'src/objects'), descriptors); // Read by the build only (site/dot-catalogue-data.mts and the preparation steps that read a catalogue's records). - await writeGenerated(resolve(projectRoot, 'site/prepared-dot-catalogues.json'), JSON.stringify(dotCatalogueIds(contexts)) + '\n'); - await writeGenerated(resolve(projectRoot, 'site/prepared-stellar-extents.json'), JSON.stringify(await readStellarExtents([...entries, ...contexts], projectRoot)) + '\n'); + await writeGenerated(resolve(projectRoot, 'site/prepared/prepared-dot-catalogues.json'), JSON.stringify(dotCatalogueIds(contexts)) + '\n'); + await writeGenerated(resolve(projectRoot, 'site/prepared/prepared-stellar-extents.json'), JSON.stringify(await readStellarExtents([...entries, ...contexts], projectRoot)) + '\n'); const { inline, banks } = splitContextObjectAssets(contexts, await contextObjectAssetUrls(contexts, projectRoot, assetOrigin())); const hosted = await readHostedContextBanks(contexts, descriptors, datasetBanks, projectRoot); // The application's code names what the world draws from any page; a hosted bank comes with its carriers' entries. const world = contexts.filter(({ id }) => hosted[id] === undefined), worldIds = new Set(world.map(({ id }) => id)); - await writeGenerated(resolve(projectRoot, 'site/prepared-context-objects.mts'), contextObjectModule(world, + await writeGenerated(resolve(projectRoot, 'site/prepared/prepared-context-objects.mts'), contextObjectModule(world, Object.fromEntries(Object.entries(inline).filter(([path]) => worldIds.has(assetObjectId(path) ?? ''))))); // Read by the build only (site/hosted-banks.mts). - await writeGenerated(resolve(projectRoot, 'site/prepared-hosted-banks.json'), JSON.stringify(hostedBankRecords(hosted, descriptors, inline)) + '\n'); + await writeGenerated(resolve(projectRoot, 'site/prepared/prepared-hosted-banks.json'), JSON.stringify(hostedBankRecords(hosted, descriptors, inline)) + '\n'); // Read by the build only (site/pages/world/context-assets/[id].json.ts). - await writeGenerated(resolve(projectRoot, 'site/prepared-context-bank-assets.json'), JSON.stringify(banks) + '\n'); - await writeGenerated(resolve(projectRoot, 'site/prepared-context-json.mts'), contextObjectJsonModule(contexts)); + await writeGenerated(resolve(projectRoot, 'site/prepared/prepared-context-bank-assets.json'), JSON.stringify(banks) + '\n'); + await writeGenerated(resolve(projectRoot, 'site/prepared/prepared-context-json.mts'), contextObjectJsonModule(contexts)); return entries; } diff --git a/site/build/prepare/prepare-facilities.mts b/site/build/prepare/prepare-facilities.mts index a300b42e596..37fe720ba23 100644 --- a/site/build/prepare/prepare-facilities.mts +++ b/site/build/prepare/prepare-facilities.mts @@ -33,7 +33,7 @@ const SCENE_OBJECTS = readPreparedObjects(resolve(import.meta.dirname, '../../.. export const explorationCompilerClosure = [ 'site/build/prepare/prepare-facilities.mts', 'packages/bake/src/sources/spatial-source-citations.ts', 'packages/catalog/src/spatial.ts', 'packages/objects/src/prepared-data/catalogue/galaxy-catalog.ts', 'packages/objects/src/prepared-data/catalogue/spatial-relations.ts', 'packages/objects/src/prepared-data/catalogue/cluster-catalog.ts', 'packages/objects/src/provenance/exploration-catalog.ts', 'packages/objects/src/provenance/exploration-contributions.ts', 'packages/objects/src/provenance/prepared-exploration.ts', 'packages/objects/src/provenance/object-lineage.ts', 'packages/objects/src/provenance/product-input-evidence.ts', 'packages/objects/src/node/prepared-registry.ts', 'packages/objects/src/registry/object-schema.ts', - 'packages/objects/src/registry/object-catalog.ts', 'site/prepared-catalogue.mjs', 'site/build/prepare/prepare-catalog.mts', 'packages/objects/src/node/catalog-directory.ts', + 'packages/objects/src/registry/object-catalog.ts', 'site/prepared/prepared-catalogue.mjs', 'site/build/prepare/prepare-catalog.mts', 'packages/objects/src/node/catalog-directory.ts', 'packages/objects/src/registry/navigable-object.ts', 'packages/objects/src/registry/navigation-distance.ts', 'packages/bake/src/navigation/navigation-destinations.ts', 'packages/objects/src/registry/object-zoom.ts', 'packages/objects/src/registry/object-tree.ts', 'site/source/facilities/catalog.json', 'site/source/facilities/render-library.json', 'site/source/facilities/emblem-library.json', @@ -195,10 +195,10 @@ export async function prepareFacilities({ root = resolve(import.meta.dirname, '. const payload = { schema: PREPARED_EXPLORATION_SCHEMA, catalog, agencies, images, emblems, graph: compileContributions(objects, catalog, DATASET_ROUTES) }; const prepared = parsePreparedExploration(payload,sources,DATASET_ROUTES); - const output = { path: resolve(root, 'site/prepared-facilities.json'), text: JSON.stringify(payload, null, 2) + '\n' }; - const sourcesOutput = {path:resolve(root,'site/prepared-sources.json'),text:JSON.stringify(sourcePayload,null,2)+'\n'}; + const output = { path: resolve(root, 'site/prepared/prepared-facilities.json'), text: JSON.stringify(payload, null, 2) + '\n' }; + const sourcesOutput = {path:resolve(root,'site/prepared/prepared-sources.json'),text:JSON.stringify(sourcePayload,null,2)+'\n'}; // What the pages read of it: each object's provider index (@cssearth/objects/provenance). - const creditsOutput = {path:resolve(root,'site/prepared-source-credits.json'),text:JSON.stringify(sourceCredits(preparedSources.usage,preparedSources.sources))+'\n'}; + const creditsOutput = {path:resolve(root,'site/prepared/prepared-source-credits.json'),text:JSON.stringify(sourceCredits(preparedSources.usage,preparedSources.sources))+'\n'}; const catalogueOutputs = [sourcesOutput,creditsOutput,output]; const outputs = [...volumes.flatMap(volume => volume.outputs),...catalogueOutputs]; if (publish) await writePreparedSet(publish === 'catalogues' ? catalogueOutputs : outputs); diff --git a/site/build/prepare/prepare-feature-index.mts b/site/build/prepare/prepare-feature-index.mts index e2d5956fe12..c0f28514745 100644 --- a/site/build/prepare/prepare-feature-index.mts +++ b/site/build/prepare/prepare-feature-index.mts @@ -98,7 +98,7 @@ export async function prepareFeatureIndex({ root = process.cwd() }: { root?: str await mkdir(resolve(root, 'public/features'), { recursive: true }); await writeFile(resolve(root, 'public/features/index.json'), encoded); const pin = { schema: FEATURE_INDEX_SCHEMA, url: FEATURE_INDEX_URL, count: features.length, objects: objects.map(object => object.id) }; - await writeFile(resolve(root, 'site/prepared-feature-index.json'), `${JSON.stringify(pin, null, 2)}\n`); + await writeFile(resolve(root, 'site/prepared/prepared-feature-index.json'), `${JSON.stringify(pin, null, 2)}\n`); return pin; } diff --git a/site/build/prepare/prepare-moon-labels.mts b/site/build/prepare/prepare-moon-labels.mts index 9b13764a15e..cd8e9f56851 100644 --- a/site/build/prepare/prepare-moon-labels.mts +++ b/site/build/prepare/prepare-moon-labels.mts @@ -36,7 +36,7 @@ export function parseMoonVector(input: unknown, code: string, center: string, ep return { positionKm: position, ephemeris }; } -/** Write site/moon-labels.prepared.json from the saved Horizons replies; `refresh` first re-requests them from JPL. */ +/** Write site/prepared/moon-labels.prepared.json from the saved Horizons replies; `refresh` first re-requests them from JPL. */ export async function prepareMoonLabels({ refresh = false }: { refresh?: boolean } = {}) { const path = 'site/source/moon-horizons.json.gz'; if (refresh) { @@ -89,7 +89,7 @@ export async function prepareMoonLabels({ refresh = false }: { refresh?: boolean parentDistanceM: state ? Math.hypot(...state.positionKm) * 1000 : null }; }); }); - await writeFile('site/moon-labels.prepared.json', `${JSON.stringify({ schema: 'cssearth-moon-labels@1', + await writeFile('site/prepared/moon-labels.prepared.json', `${JSON.stringify({ schema: 'cssearth-moon-labels@1', referenceFrame: world.frame.referenceFrame, epochJdTt: epoch, qualification: 'Properly named catalogue moons only; provisional designations stay in the full sidebar catalogue. Horizons geometric ICRF vectors at the prepared world epoch, relative to each planet. No fabricated positions: moons without Horizons states remain in the sidebar only.', moons }, null, 2)}\n`); console.log(`${moons.filter(moon => moon.positionM).length} positioned labels; ${moons.filter(moon => !moon.positionM).length} without positions.`); diff --git a/site/build/prepare/prepare-shell-icons.mts b/site/build/prepare/prepare-shell-icons.mts index adf91806968..e88e01125e3 100644 --- a/site/build/prepare/prepare-shell-icons.mts +++ b/site/build/prepare/prepare-shell-icons.mts @@ -12,7 +12,7 @@ const SVG_ROOT = /]*\bwidth="([0-9.]+)"[^>]*\bheight="([0-9.]+)"[^>]*\b export async function prepareShellIcons({ sourceRoot = resolve(import.meta.dirname, "../../source/icons"), publicRoot = resolve(import.meta.dirname, "../../../public/shell"), - moduleOutput = resolve(import.meta.dirname, "../../prepared-shell-icons.mjs"), + moduleOutput = resolve(import.meta.dirname, "../../prepared/prepared-shell-icons.mjs"), } = {}) { validateManifest(); await mkdir(publicRoot, { recursive: true }); diff --git a/site/build/prepare/prepare-shell-titles.mts b/site/build/prepare/prepare-shell-titles.mts index 77858217a5b..5ce53f82d80 100644 --- a/site/build/prepare/prepare-shell-titles.mts +++ b/site/build/prepare/prepare-shell-titles.mts @@ -14,7 +14,7 @@ const SVG_METADATA = /([^<]+)<\/metadata>/u; export async function prepareShellTitles({ sourceRoot = resolve(import.meta.dirname, "../../source/titles"), publicRoot = resolve(import.meta.dirname, "../../../public/shell"), - moduleOutput = resolve(import.meta.dirname, "../../prepared-shell-titles.mjs"), + moduleOutput = resolve(import.meta.dirname, "../../prepared/prepared-shell-titles.mjs"), } = {}) { validateManifest(); await mkdir(publicRoot, { recursive: true }); diff --git a/site/build/prepare/prepare-text.mts b/site/build/prepare/prepare-text.mts index 27a2e744110..d6518fea82a 100644 --- a/site/build/prepare/prepare-text.mts +++ b/site/build/prepare/prepare-text.mts @@ -81,7 +81,7 @@ export async function prepareText({ ids = [] as readonly string[], check = false ...catalogueTextWarnings(bodies.map(body => ({ text: body.text, name: body.context.name })))]; let composition = 'checked'; try { - const exploration = parsePreparedExploration(await readJson(resolve(projectRoot, 'site/prepared-facilities.json')), sourceResolver(sourceCatalog), DATASET_ROUTES); + const exploration = parsePreparedExploration(await readJson(resolve(projectRoot, 'site/prepared/prepared-facilities.json')), sourceResolver(sourceCatalog), DATASET_ROUTES); const found = bodies.flatMap(body => compositionGroups(body, exploration).flatMap(blocks => compositionWarnings(body.id, blocks))); warnings.push(...new Map(found.map(finding => [JSON.stringify(finding), finding])).values()); } catch (error) { diff --git a/site/build/prepare/prepare-world-presentation.mts b/site/build/prepare/prepare-world-presentation.mts index 074b0ba33ac..72f8102e489 100644 --- a/site/build/prepare/prepare-world-presentation.mts +++ b/site/build/prepare/prepare-world-presentation.mts @@ -9,7 +9,7 @@ import { readFileSync } from 'node:fs'; import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { dirname, resolve } from 'node:path'; import majorMoons from '../../source/major-moons.json' with { type: 'json' }; -import catalogueIds from '../../prepared-dot-catalogues.json' with { type: 'json' }; +import catalogueIds from '../../prepared/prepared-dot-catalogues.json' with { type: 'json' }; import { discoveryVisibility, type ObjectDiscovery } from '@cssearth/objects'; import { WORLD_OBJECTS } from '../../world-objects.mts'; import { APPLICATION_WORLD_CONTEXT } from '../../world-context-plan.mts'; @@ -21,13 +21,13 @@ import { isExtremeTransNeptunian } from '@cssearth/astronomy'; const SCENE_OBJECTS = readPreparedObjects(resolve(import.meta.dirname, '../../..')).sceneObjects; -const output = resolve(import.meta.dirname, '../../prepared-world-presentation.json'); +const output = resolve(import.meta.dirname, '../../prepared/prepared-world-presentation.json'); /** A dot layer's presentation record, with the distances named in `fields`. Its catalogue is the one context object of its * type (prepare-catalog.mts dotCatalogueIds), so no catalogue is named here. */ function layerPresentation(layer: 'galaxies' | 'clusters', fields: readonly Field[]): Record { const id: unknown = (catalogueIds as Record)[layer]; - if (typeof id !== 'string') throw new TypeError(`site/prepared-dot-catalogues.json names no catalogue for the ${layer} dot layer. Run pnpm prepare:catalog.`); + if (typeof id !== 'string') throw new TypeError(`site/prepared/prepared-dot-catalogues.json names no catalogue for the ${layer} dot layer. Run pnpm prepare:catalog.`); const path = `src/objects/${id}/source/presentation.json`; return readCataloguePresentationDistances(JSON.parse(readFileSync(resolve(import.meta.dirname, '../../..', path), 'utf8')), fields); } @@ -160,7 +160,7 @@ export function prepareWorldPresentation() { }; } -/** Write site/prepared-world-presentation.json, leaving an unchanged file untouched. */ +/** Write site/prepared/prepared-world-presentation.json, leaving an unchanged file untouched. */ export async function writeWorldPresentation() { const text = `${JSON.stringify(prepareWorldPresentation())}\n`; await mkdir(dirname(output), { recursive: true }); diff --git a/site/build/prepare/prepared-icons.test.mts b/site/build/prepare/prepared-icons.test.mts index 2b25388a6f9..8ad520e43f9 100644 --- a/site/build/prepare/prepared-icons.test.mts +++ b/site/build/prepare/prepared-icons.test.mts @@ -7,7 +7,7 @@ const test = sourceTest(); import { SourceEvidence } from "../../overview/source-evidence-values.test-support.mts"; import { prepareShellIcons } from "./prepare-shell-icons.mts"; -import { PREPARED_SHELL_ICONS } from "../../prepared-shell-icons.mjs"; +import { PREPARED_SHELL_ICONS } from "../../prepared/prepared-shell-icons.mjs"; import { SHELL_ICON_SOURCES } from "../../source/icons/manifest.mts"; const projectRoot = resolve(import.meta.dirname, "../.."); @@ -25,7 +25,7 @@ test("prepares one source-bound 20px SVG family for sidebar sections", async (co assert.equal( result.moduleSource, - await readFile(resolve(projectRoot, "site/prepared-shell-icons.mjs"), "utf8"), + await readFile(resolve(projectRoot, "site/prepared/prepared-shell-icons.mjs"), "utf8"), ); assert.deepEqual(Object.keys(result.prepared), [ "facts", diff --git a/site/build/prepare/refresh-source-icons.mts b/site/build/prepare/refresh-source-icons.mts index a0eabd13348..2511cef336d 100644 --- a/site/build/prepare/refresh-source-icons.mts +++ b/site/build/prepare/refresh-source-icons.mts @@ -57,7 +57,7 @@ async function siteIcon(host: string): Promise { return { sourceUrl: `https://${host}/` }; } -const credits = parseSourceCredits(JSON.parse(await readFile(resolve(root, 'site/prepared-source-credits.json'), 'utf8'))); +const credits = parseSourceCredits(JSON.parse(await readFile(resolve(root, 'site/prepared/prepared-source-credits.json'), 'utf8'))); const all = process.argv.includes('--all'), retry = all || process.argv.includes('--retry'); const recorded = parseSourceIcons(JSON.parse(await readFile(recordPath, 'utf8').catch(() => '{}'))); // --all keeps each recorded address, which may be a correction, and asks it again. diff --git a/site/catalogue-moon-labels.characterization.test.mts b/site/catalogue-moon-labels.characterization.test.mts index 27953ccc8e4..d6f5f9351b6 100644 --- a/site/catalogue-moon-labels.characterization.test.mts +++ b/site/catalogue-moon-labels.characterization.test.mts @@ -8,7 +8,7 @@ mock.module('@cssearth/renderer/labels/stable-label-layout.ts', { namedExports: admitStableLabels: (...args: Parameters) => { candidates = args[0]; return admit(...args); }, } }); import type { WorldCameraPose } from '@cssearth/engine'; -mock.module(new URL('./moon-labels.prepared.json', import.meta.url).href, { defaultExport: { +mock.module(new URL('./prepared/moon-labels.prepared.json', import.meta.url).href, { defaultExport: { schema: 'cssearth-moon-labels@1', referenceFrame: 'world', epochJdTt: 1, moons: [{ id: 'moon-a', name: 'Moon A', parentId: 'planet', positionM: [100, 0, 0], parentDistanceM: 100 }], } }); diff --git a/site/catalogue-moon-labels.mts b/site/catalogue-moon-labels.mts index 0a4d88a7992..81c0a958944 100644 --- a/site/catalogue-moon-labels.mts +++ b/site/catalogue-moon-labels.mts @@ -2,7 +2,7 @@ import { fromEyeM } from '@cssearth/engine'; import { createOpacityFader } from '@cssearth/renderer'; import type { OpacityClock } from '@cssearth/renderer/stars/opacity-clock.ts'; import { admitStableLabels } from '@cssearth/renderer/labels/stable-label-layout.ts'; -import prepared from './moon-labels.prepared.json' with { type: 'json' }; +import prepared from './prepared/moon-labels.prepared.json' with { type: 'json' }; import { sourceArray, sourceId, sourceObject, sourceText, sourceUnique } from '@cssearth/objects/sources'; import { cssViewFromOrientation, rotateWorldPosition } from '@cssearth/engine'; import { rayHitsSphereBefore } from '@cssearth/engine'; diff --git a/site/components/ObjectFactsheet.astro b/site/components/ObjectFactsheet.astro index 334530bd282..8eda3e44192 100644 --- a/site/components/ObjectFactsheet.astro +++ b/site/components/ObjectFactsheet.astro @@ -1,6 +1,6 @@ --- import type { Props as ObjectProps } from "../contracts/object-shell-types"; -import { PREPARED_SHELL_ICONS } from "../prepared-shell-icons.mjs"; +import { PREPARED_SHELL_ICONS } from "../prepared/prepared-shell-icons.mjs"; import { orderFacts } from "@cssearth/objects"; interface Props { diff --git a/site/components/ObjectNavigationMarker.astro b/site/components/ObjectNavigationMarker.astro index 11b22674e9c..8ff886a65a9 100644 --- a/site/components/ObjectNavigationMarker.astro +++ b/site/components/ObjectNavigationMarker.astro @@ -1,5 +1,5 @@ --- -import { PREPARED_NAVIGATION_MARKERS } from "../prepared-navigation-markers.mjs"; +import { PREPARED_NAVIGATION_MARKERS } from "../prepared/prepared-navigation-markers.mjs"; import { markerStyle } from "@cssearth/renderer/navigation/marker-presentation.ts"; interface Props { diff --git a/site/components/ObjectShell.astro b/site/components/ObjectShell.astro index 34d8f717f67..ccc762614fc 100644 --- a/site/components/ObjectShell.astro +++ b/site/components/ObjectShell.astro @@ -3,7 +3,7 @@ import { DIAGNOSTICS_ENABLED } from "../browser/diagnostics-policy.mts"; import ObjectInformationPanel from "./ObjectInformationPanel.astro"; import type { Props } from "../contracts/object-shell-types"; import { SCENE_OBJECTS } from "../objects.mts"; -import { PREPARED_SHELL_TITLES } from "../prepared-shell-titles.mjs"; +import { PREPARED_SHELL_TITLES } from "../prepared/prepared-shell-titles.mjs"; import { requireObjectControls } from "@cssearth/objects"; import SourcesLink from './SourcesLink.astro'; import ObjectResults from "./ObjectResults.astro"; diff --git a/site/dot-catalogue-data.characterization.test.mts b/site/dot-catalogue-data.characterization.test.mts index bfb85eef72a..e1fccbfaa13 100644 --- a/site/dot-catalogue-data.characterization.test.mts +++ b/site/dot-catalogue-data.characterization.test.mts @@ -8,7 +8,7 @@ import { pathToFileURL } from 'node:url'; const subject = new URL('./dot-catalogue-data.mts', import.meta.url); const nodeSource = new URL('./prepared/prepared-world-context-node-source.mts', import.meta.url); -const idsSource = new URL('./prepared-dot-catalogues.json', import.meta.url); +const idsSource = new URL('./prepared/prepared-dot-catalogues.json', import.meta.url); const part = (sources: unknown[], objects: unknown[]) => ({ schema: 'nebula', sources, objects }); function evaluate(ids: unknown = { galaxies: 'galaxy', clusters: 'cluster' }, parts = [ diff --git a/site/dot-catalogue-data.mts b/site/dot-catalogue-data.mts index cfdea3cf607..55faf9e86a2 100644 --- a/site/dot-catalogue-data.mts +++ b/site/dot-catalogue-data.mts @@ -2,11 +2,11 @@ import { PREPARED_NEBULA_CATALOG_SCHEMA, parsePreparedGalaxyCatalog, parsePrepar import { nodeProjectFileUrl } from './prepared/prepared-world-context-node-source.mts'; // Build-owned catalogues: their contents are not application JavaScript, and no page fetches one whole. // Each dot layer's catalogue is the one context object of its type (site/build/prepare/prepare-catalog.mts dotCatalogueIds). -import catalogueIds from './prepared-dot-catalogues.json' with { type: 'json' }; +import catalogueIds from './prepared/prepared-dot-catalogues.json' with { type: 'json' }; async function catalogue(layer: 'galaxies' | 'clusters'): Promise { const id: unknown = (catalogueIds as Record)[layer]; - if (typeof id !== 'string') throw new TypeError(`site/prepared-dot-catalogues.json names no catalogue for the ${layer} dot layer. Run pnpm prepare:catalog.`); + if (typeof id !== 'string') throw new TypeError(`site/prepared/prepared-dot-catalogues.json names no catalogue for the ${layer} dot layer. Run pnpm prepare:catalog.`); return (await import(/* @vite-ignore */ nodeProjectFileUrl(import.meta.url, `src/objects/${id}/prepared/catalogue.json`), { with: { type: 'json' } })).default; } const galaxies = parsePreparedGalaxyCatalog(await catalogue('galaxies')), clusters = parsePreparedClusterCatalog(await catalogue('clusters')); diff --git a/site/exploration-catalog.mts b/site/exploration-catalog.mts index 47e0320a8cc..c7a3ab1a24d 100644 --- a/site/exploration-catalog.mts +++ b/site/exploration-catalog.mts @@ -1,4 +1,4 @@ -import input from './prepared-facilities.json' with { type: 'json' }; +import input from './prepared/prepared-facilities.json' with { type: 'json' }; import { parsePreparedExploration } from '@cssearth/objects/provenance'; import { DATASET_ROUTES } from '@cssearth/objects/provenance'; diff --git a/site/hosted-banks.characterization.test.mts b/site/hosted-banks.characterization.test.mts index 8c7c2b014d6..d870629f81f 100644 --- a/site/hosted-banks.characterization.test.mts +++ b/site/hosted-banks.characterization.test.mts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import { mock, test } from 'node:test'; import { importWithJson } from './test/fixtures/module-import.mts'; const bank = { schema: 'cssearth-object@2', id: 'dots', type: 'catalogue-point-bank', properties: { host: 'star' } }; -mock.module(new URL('./prepared-hosted-banks.json', import.meta.url).href, { defaultExport: { +mock.module(new URL('./prepared/prepared-hosted-banks.json', import.meta.url).href, { defaultExport: { dots: { carriers: ['star'], descriptor: bank, files: { 'prepared/dots.bin': '/dots.bin' } }, volume: { carriers: ['cloud'], descriptor: { id: 'volume', type: 'volume-dataset-bank' } }, } }); @@ -14,7 +14,7 @@ test('banks select direct carriers and only the orbital centre’s point bank', assert.deepEqual(hostedBanksOf('cloud'), [{ descriptor: { id: 'volume', type: 'volume-dataset-bank' } }]); }); test('hosted-bank import refuses malformed carriers, descriptors and file URLs', () => { - const subject = new URL('./hosted-banks.mts', import.meta.url), dependency = new URL('./prepared-hosted-banks.json', import.meta.url); + const subject = new URL('./hosted-banks.mts', import.meta.url), dependency = new URL('./prepared/prepared-hosted-banks.json', import.meta.url); for (const value of [null, { dots: { carriers: [1], descriptor: bank } }, { dots: { carriers: [], descriptor: { ...bank, id: 'wrong' } } }, { dots: { carriers: [], descriptor: bank, files: { x: 1 } } }]) { const result = importWithJson(subject, dependency, value); assert.equal(result.status, 1); diff --git a/site/hosted-banks.mts b/site/hosted-banks.mts index 2c0dc7153b3..2110e8c0674 100644 --- a/site/hosted-banks.mts +++ b/site/hosted-banks.mts @@ -1,5 +1,5 @@ import { isRecord } from '@cssearth/core'; -import records from './prepared-hosted-banks.json' with { type: 'json' }; +import records from './prepared/prepared-hosted-banks.json' with { type: 'json' }; /** A bank the world draws only for the bodies that hold it (prepare-catalog.mts `readHostedContextBanks`), as an object * entry carries it: its descriptor, and its files when it has no list of its own (`/world/context-assets/.json`). */ @@ -7,7 +7,7 @@ export interface HostedBank { readonly descriptor: unknown; readonly files?: Rea interface HostedBankRecord extends HostedBank { readonly carriers: readonly string[] } function parseRecords(value: unknown): ReadonlyMap { - const path = 'site/prepared-hosted-banks.json'; + const path = 'site/prepared/prepared-hosted-banks.json'; if (!isRecord(value)) throw new TypeError(`${path}: expected banks by id; run pnpm prepare:catalog.`); return new Map(Object.entries(value).map(([id, record]) => { if (!isRecord(record) || !Array.isArray(record.carriers) || !record.carriers.every(carrier => typeof carrier === 'string') diff --git a/site/layouts/ObjectLayout.astro b/site/layouts/ObjectLayout.astro index 7b7477a6d5f..9765c3716a2 100644 --- a/site/layouts/ObjectLayout.astro +++ b/site/layouts/ObjectLayout.astro @@ -7,7 +7,7 @@ import { errorReportBootstrap } from "../startup/error-report.mts"; import { initialShellContextBootstrap } from '../startup/initial-shell-context.mts'; import { startupRequestsBootstrap } from '../directory/startup-requests.mts'; import { defaultDatasetBank } from '../server/object-page-data.mts'; -import contextBankAssets from '../prepared-context-bank-assets.json'; +import contextBankAssets from '../prepared/prepared-context-bank-assets.json'; import { SYSTEM_VIEW_HOSTS } from '../system-framing.mts'; import { inlineSceneImage, resolveBuildSceneAddress } from '../server-assets/asset-origin.mts'; import { SCENE_OBJECTS, requireObject } from "../objects.mts"; diff --git a/site/object-entry.mts b/site/object-entry.mts index 9de852ecd8b..997747a2424 100644 --- a/site/object-entry.mts +++ b/site/object-entry.mts @@ -1,6 +1,6 @@ import { isRecord } from '@cssearth/core'; import { OBJECTS } from './objects.mts'; -import { CATALOGUE_ENTRIES } from './prepared-catalogue.mjs'; +import { CATALOGUE_ENTRIES } from './prepared/prepared-catalogue.mjs'; const entryId = (entry: unknown) => !isRecord(entry) ? undefined : isRecord(entry.descriptor) ? entry.descriptor.id : entry.id; const ENTRIES = new Map(CATALOGUE_ENTRIES.map(entry => [entryId(entry), entry])); diff --git a/site/objects.mts b/site/objects.mts index 3a055ba2ca6..91582dad45c 100644 --- a/site/objects.mts +++ b/site/objects.mts @@ -1,5 +1,5 @@ import { checkObjectTree, defineObjects } from '@cssearth/objects'; -import { CATALOGUE_ENTRIES } from './prepared-catalogue.mjs'; +import { CATALOGUE_ENTRIES } from './prepared/prepared-catalogue.mjs'; import { objectFromEntry } from './object-directory.mts'; import type { NavigableObject } from './directory/object-entry-types.mts'; export type { ObjectEntry, CatalogEntry, NavigableObject } from './directory/object-entry-types.mts'; diff --git a/site/pages/catalogues/dots.bin.ts b/site/pages/catalogues/dots.bin.ts index eba75120729..7b3efd13e3c 100644 --- a/site/pages/catalogues/dots.bin.ts +++ b/site/pages/catalogues/dots.bin.ts @@ -2,7 +2,7 @@ import type { APIRoute } from 'astro'; import { catalogueDots, encodeCatalogueDots } from '@cssearth/objects'; import { packPreparedBank } from '@cssearth/objects/node'; import { DOT_CATALOGUES } from '../../dot-catalogue-data.mts'; -import { CONTEXT_GALAXY_SAMPLE } from '../../prepared-context-objects.mts'; +import { CONTEXT_GALAXY_SAMPLE } from '../../prepared/prepared-context-objects.mts'; // The galaxy catalogue's dots as the page draws them (@cssearth/objects catalogue-dots.ts): the sampled Local Group // galaxies without a package, their ids and places, as one prepared bank the data worker reads. The catalogues themselves diff --git a/site/pages/objects/[id]/entry.json.ts b/site/pages/objects/[id]/entry.json.ts index 2651bff36cd..6c67127aff4 100644 --- a/site/pages/objects/[id]/entry.json.ts +++ b/site/pages/objects/[id]/entry.json.ts @@ -6,7 +6,7 @@ import { OBJECTS, ancestorsOf } from '../../../objects.mts'; import { APPLICATION_WORLD_CONTEXT } from '../../../world-context-plan.mts'; import { hostedBanksOf } from '../../../hosted-banks.mts'; import { surroundedBody, surroundingHosts } from '../../../world/surrounded-body.mts'; -import bankAssets from '../../../prepared-context-bank-assets.json' with { type: 'json' }; +import bankAssets from '../../../prepared/prepared-context-bank-assets.json' with { type: 'json' }; import { readFile } from 'node:fs/promises'; import { resolve } from 'node:path'; diff --git a/site/pages/world/context-assets/[id].json.ts b/site/pages/world/context-assets/[id].json.ts index dbdfbdcd1ae..e41bd86f831 100644 --- a/site/pages/world/context-assets/[id].json.ts +++ b/site/pages/world/context-assets/[id].json.ts @@ -1,5 +1,5 @@ import type { APIRoute, GetStaticPaths } from 'astro'; -import bankAssets from '../../../prepared-context-bank-assets.json'; +import bankAssets from '../../../prepared/prepared-context-bank-assets.json'; // One bank's prepared files by path (prepare-catalog.mts `splitContextObjectAssets`): the world reads a bank's list when a // body's dataset first shows it (application-world-resources.mts), so no page carries the lists of banks it does not show. diff --git a/site/prepared-world-presentation.characterization.test.mts b/site/prepared-world-presentation.characterization.test.mts index 33d5ec5862a..509257e5a94 100644 --- a/site/prepared-world-presentation.characterization.test.mts +++ b/site/prepared-world-presentation.characterization.test.mts @@ -6,7 +6,7 @@ const input = { schema: 'cssearth-world-presentation@6', moons: { major: ['moon' clusters: { fadeStartDistanceM: 7, fullDistanceM: 8 }, categoryFrames: { planet: { centreM: [0, 0, 0], minimumM: [-1, -2, -3], maximumM: [1, 2, 3] } }, }; test('prepared world import validates ids, distances, boxes and ordered corners', () => { - const subject = new URL('./prepared-world-presentation.mts', import.meta.url), dependency = new URL('./prepared-world-presentation.json', import.meta.url); + const subject = new URL('./prepared-world-presentation.mts', import.meta.url), dependency = new URL('./prepared/prepared-world-presentation.json', import.meta.url); const valid = importWithJson(subject, dependency, input, ` const value = loaded.PREPARED_WORLD_PRESENTATION; console.log(JSON.stringify({ ...value, categoryFrames: [...value.categoryFrames], frozenIds: [value.moons.major, value.moons.minor, value.defaultFeatureIds, value.orbitFeatureIds, value.hiddenOrbitIds].map(Object.isFrozen) })); @@ -34,7 +34,7 @@ console.log(JSON.stringify({ ...value, categoryFrames: [...value.categoryFrames] test('equal category bounds are valid and optional member, host and holder ids are checked', () => { const subject = new URL('./prepared-world-presentation.mts', import.meta.url); - const dependency = new URL('./prepared-world-presentation.json', import.meta.url); + const dependency = new URL('./prepared/prepared-world-presentation.json', import.meta.url); const frame = { centreM: [1, 2, 3], minimumM: [1, 2, 3], maximumM: [1, 2, 3], memberIds: ['moon'], hostIds: ['planet'], holderIds: ['system'] }; const result = importWithJson(subject, dependency, { ...input, categoryFrames: { planet: frame } }); assert.equal(result.status, 0, result.stderr); diff --git a/site/prepared-world-presentation.mts b/site/prepared-world-presentation.mts index f660616050d..e2b29dbcbc1 100644 --- a/site/prepared-world-presentation.mts +++ b/site/prepared-world-presentation.mts @@ -1,7 +1,7 @@ // Written by site/build/prepare/prepare-world-presentation.mts from the moon groups, the JPL mission targets and the galaxy // and cluster presentation recipes; the browser only validates it. It lists no system: a page reads a system's members // and framing from the bodies its world holds (site/object-systems.mts, site/system-framing.mts). -import prepared from './prepared-world-presentation.json' with { type: 'json' }; +import prepared from './prepared/prepared-world-presentation.json' with { type: 'json' }; import { isRecord } from '@cssearth/core'; const ids = (value: unknown, name: string): readonly string[] => { @@ -42,7 +42,7 @@ function categoryFrames(value: unknown) { function parseWorldPresentation(value: unknown) { if (!isRecord(value) || value.schema !== 'cssearth-world-presentation@6' || !isRecord(value.moons)) { - throw new TypeError(`site/prepared-world-presentation.json is ${isRecord(value) ? String(value.schema) : typeof value}, not cssearth-world-presentation@6; run pnpm prepare:world-context.`); + throw new TypeError(`site/prepared/prepared-world-presentation.json is ${isRecord(value) ? String(value.schema) : typeof value}, not cssearth-world-presentation@6; run pnpm prepare:world-context.`); } return Object.freeze({ moons: Object.freeze({ major: ids(value.moons.major, 'moons.major'), minor: ids(value.moons.minor, 'moons.minor') }), diff --git a/site/prepared-catalogue.d.mts b/site/prepared/prepared-catalogue.d.mts similarity index 100% rename from site/prepared-catalogue.d.mts rename to site/prepared/prepared-catalogue.d.mts diff --git a/site/prepared-navigation-markers.d.mts b/site/prepared/prepared-navigation-markers.d.mts similarity index 100% rename from site/prepared-navigation-markers.d.mts rename to site/prepared/prepared-navigation-markers.d.mts diff --git a/site/prepared-shell-icons.d.mts b/site/prepared/prepared-shell-icons.d.mts similarity index 100% rename from site/prepared-shell-icons.d.mts rename to site/prepared/prepared-shell-icons.d.mts diff --git a/site/prepared-shell-titles.d.mts b/site/prepared/prepared-shell-titles.d.mts similarity index 100% rename from site/prepared-shell-titles.d.mts rename to site/prepared/prepared-shell-titles.d.mts diff --git a/site/search/prepared-catalogue-index.mts b/site/search/prepared-catalogue-index.mts index dbece9991f0..b92edce8f42 100644 --- a/site/search/prepared-catalogue-index.mts +++ b/site/search/prepared-catalogue-index.mts @@ -2,7 +2,7 @@ import { designationNames } from './object-search.mts'; import { distanceDescription } from '@cssearth/objects'; import { objectTypeLabel, SEARCH_OBJECTS } from './search-objects.mts'; import { sidebarThumbnail } from '../content/sidebar-thumbnails.mts'; -import { PREPARED_NAVIGATION_MARKERS } from '../prepared-navigation-markers.mjs'; +import { PREPARED_NAVIGATION_MARKERS } from '../prepared/prepared-navigation-markers.mjs'; import { markerStyle } from '@cssearth/renderer/navigation/marker-presentation.ts'; import { sourceDocumentation } from '../source-documentation.mts'; import type { CatalogueIndex, CatalogueIndexEntry } from './catalogue-index.mts'; @@ -13,7 +13,7 @@ import { systemCard } from '../system-card.mts'; const THUMBNAIL_SCALE = 14 / Math.max(...Object.values(PREPARED_NAVIGATION_MARKERS).map(({ presentation }) => presentation.size)); /** A result row shows an object by its marker: its prepared search thumbnail when it has a context sprite (`preview`), * else its sprite of the marker sheet, as the styles the row writes. The row carries them, so a page holds no table of - * every object's marker (site/prepared-navigation-markers.mjs was 820 KB of every page's code, 2026-10-02). */ + * every object's marker (site/prepared/prepared-navigation-markers.mjs was 820 KB of every page's code, 2026-10-02). */ export function objectResultMarker(object: { readonly id: string; readonly color: string }) { const prepared = PREPARED_NAVIGATION_MARKERS[object.id]; if (!prepared) return Object.freeze({ id: object.id, color: object.color }); diff --git a/site/server/feature-pin.mts b/site/server/feature-pin.mts index b71750a4f82..a615dcd0e44 100644 --- a/site/server/feature-pin.mts +++ b/site/server/feature-pin.mts @@ -1,5 +1,5 @@ import { parseFeaturePin } from '../search/feature-search.mts'; -import pin from '../prepared-feature-index.json' with { type: 'json' }; +import pin from '../prepared/prepared-feature-index.json' with { type: 'json' }; /** This build's cross-body feature index, or null when no body publishes named features. */ export const FEATURE_PIN = parseFeaturePin(JSON.stringify(pin)); diff --git a/site/server/search-server.mts b/site/server/search-server.mts index c9c4af329b5..6225dc2c647 100644 --- a/site/server/search-server.mts +++ b/site/server/search-server.mts @@ -25,7 +25,7 @@ export function searchServer(): Plugin { send(data().then(search => handleSearchRequest(new Request(route, { method: request.method }), search))); }; // Read at request time: the pin is regenerated by prepare-feature-index while dev runs. - const readPin = async () => parseFeaturePin(await readFile(resolve(import.meta.dirname, '../prepared-feature-index.json'), 'utf8')); + const readPin = async () => parseFeaturePin(await readFile(resolve(import.meta.dirname, '../prepared/prepared-feature-index.json'), 'utf8')); const devData = (server: ViteDevServer) => async (): Promise => ({ pin: await readPin(), read: readPublicFile, diff --git a/site/source-documentation.mts b/site/source-documentation.mts index 33d9cdda165..0d87bb38372 100644 --- a/site/source-documentation.mts +++ b/site/source-documentation.mts @@ -1,7 +1,7 @@ import { execFileSync } from 'node:child_process'; import { existsSync, readFileSync } from 'node:fs'; import { resolve } from 'node:path'; -import credits from './prepared-source-credits.json' with { type: 'json' }; +import credits from './prepared/prepared-source-credits.json' with { type: 'json' }; import { parseSourceCredits } from '@cssearth/objects/provenance'; import { projectRoot } from '@cssearth/core/node'; import { parseSourceIcons } from './content/source-icons.mts'; diff --git a/site/stellar-extents.mts b/site/stellar-extents.mts index c2ba36b7ee6..c4835dc9c11 100644 --- a/site/stellar-extents.mts +++ b/site/stellar-extents.mts @@ -1,4 +1,4 @@ -import extents from './prepared-stellar-extents.json' with { type: 'json' }; +import extents from './prepared/prepared-stellar-extents.json' with { type: 'json' }; /** Published stellar extents, radius in metres by object id, prepared from each object's `source/stellar-extent.json` * by `pnpm prepare:catalog`. */ diff --git a/site/system-framing.mts b/site/system-framing.mts index 340c5859293..6e6a9d45b96 100644 --- a/site/system-framing.mts +++ b/site/system-framing.mts @@ -11,8 +11,8 @@ type FramingFrame = Pick number): PositionM => [map(0), map(1), map(2)]; -import datasetVolumes from './prepared-dataset-volumes.json' with { type: 'json' }; -import fitBoxes from './prepared-fit-boxes.json' with { type: 'json' }; +import datasetVolumes from './prepared/prepared-dataset-volumes.json' with { type: 'json' }; +import fitBoxes from './prepared/prepared-fit-boxes.json' with { type: 'json' }; import { SYSTEM_FRAMING_ANGLES, SYSTEM_FRAMING_PADDING_PIXELS } from './browser/runtime-policy.mts'; import { systemFramingRadii } from './world/system-framing-radii.mts'; export { systemFramingRadii } from './world/system-framing-radii.mts'; @@ -94,7 +94,7 @@ export function loadSystemView(id: string, read?: (id: string) => Promise = new Map( Object.entries(datasetVolumes).map(([id, bank]) => { const host: unknown = 'host' in bank ? bank.host : undefined; - if (host !== undefined && typeof host !== 'string') throw new TypeError(`site/prepared-dataset-volumes.json: ${id}.host must be an object id.`); + if (host !== undefined && typeof host !== 'string') throw new TypeError(`site/prepared/prepared-dataset-volumes.json: ${id}.host must be an object id.`); return [id, { frame: parseDensityVolumeFrame(bank.frame), ...(host === undefined ? {} : { host }) }]; })); @@ -103,7 +103,7 @@ export const DATASET_VOLUMES: ReadonlyMap = new Map(Object.entries(fitBoxes as Record>).map(([id, box]) => { const position = (field: 'centreM' | 'minimumM' | 'maximumM'): PositionM => { const value = box[field]; - if (!Array.isArray(value) || value.length !== 3 || !value.every(Number.isFinite)) throw new TypeError(`site/prepared-fit-boxes.json: ${id}.${field} must be three numbers.`); + if (!Array.isArray(value) || value.length !== 3 || !value.every(Number.isFinite)) throw new TypeError(`site/prepared/prepared-fit-boxes.json: ${id}.${field} must be three numbers.`); return [value[0], value[1], value[2]]; }; return [id, { centre: position('centreM'), candidate: { minimumM: position('minimumM'), maximumM: position('maximumM'), cameraToReference: [1, 0, 0, 0, 1, 0, 0, 0, 1] } }]; @@ -118,7 +118,7 @@ function boxZoomTarget(from: WorldCameraPose, box: { centre: PositionM; candidat /** Fit the galaxies inside `objectId` at the current viewing angle, centred on them: an overview's `zoom.frame` {fit: drawn-galaxies}. */ export function drawnGalaxiesZoomTarget(objectId: string, from: WorldCameraPose, optics: Optics, rect: MapViewport) { const box = FIT_BOXES.get(objectId); - if (!box) throw new TypeError(`site/prepared-fit-boxes.json has no box for ${objectId}: its zoom.frame fits the galaxies inside it. Run pnpm prepare:catalog.`); + if (!box) throw new TypeError(`site/prepared/prepared-fit-boxes.json has no box for ${objectId}: its zoom.frame fits the galaxies inside it. Run pnpm prepare:catalog.`); return boxZoomTarget(from, box, optics, rect); } diff --git a/site/test/hosted-banks.test.mts b/site/test/hosted-banks.test.mts index e5c57c5b481..8439634099f 100644 --- a/site/test/hosted-banks.test.mts +++ b/site/test/hosted-banks.test.mts @@ -52,7 +52,7 @@ test('an object entry carries the banks it holds and the dots of the body it orb const { objectFromEntry } = await import('../object-directory.mts'); const { OBJECT_ENTRY_IDS } = await import('../object-entry.mts'); const records: Record = - (await import('../prepared-hosted-banks.json', { with: { type: 'json' } })).default as never; + (await import('../prepared/prepared-hosted-banks.json', { with: { type: 'json' } })).default as never; const entryOf = async (id: string) => (await GET({ params: { id } } as never)).json() as Promise>; const carried = (entry: Record) => ((entry.banks ?? []) as { descriptor: { id: string } }[]).map(bank => bank.descriptor.id); const navigable = new Set(OBJECT_ENTRY_IDS); diff --git a/site/world/navigation-preparation.test.mts b/site/world/navigation-preparation.test.mts index f824c1c90df..c8258ba91e3 100644 --- a/site/world/navigation-preparation.test.mts +++ b/site/world/navigation-preparation.test.mts @@ -66,7 +66,7 @@ test('metadata-only preparation does not replace or remove images', async contex test('body markers and resolved context images leave space outside their silhouette transparent', async () => { const descriptors = await markersOf(['earth', 'moon', 'saturn', 'comet-67p']); - const { PREPARED_NAVIGATION_MARKERS } = await import('../prepared-navigation-markers.mjs'); + const { PREPARED_NAVIGATION_MARKERS } = await import('../prepared/prepared-navigation-markers.mjs'); for (const { objectId } of descriptors) { const context = PREPARED_NAVIGATION_MARKERS[objectId]?.context; const images = [`body-${objectId}@2x.webp`, @@ -81,7 +81,7 @@ test('body markers and resolved context images leave space outside their silhoue }); test('every context image has its committed search preview, and no preview outlives its image', async () => { - const { PREPARED_NAVIGATION_MARKERS } = await import('../prepared-navigation-markers.mjs'); + const { PREPARED_NAVIGATION_MARKERS } = await import('../prepared/prepared-navigation-markers.mjs'); const expected = Object.entries(PREPARED_NAVIGATION_MARKERS).filter(([, marker]) => marker.context).map(([id]) => `${id}@2x.webp`).sort(); assert.deepEqual((await readdir(resolve(projectRoot, 'public/navigation/search'))).sort(), expected, 'run node packages/bake/cli/prepare-navigation.mts after drawing a context image'); @@ -116,7 +116,7 @@ for (const failure of ["object source", "late utility source", "publication", "r ...expectedOutputFiles.filter((filename) => filename !== "body-download@2x.webp").map((filename) => [resolve(outputRoot, filename), `accepted ${filename}`] as const), [resolve(outputRoot, "new-body.webp"), "accepted legacy marker"], [resolve(outputRoot, "unrelated.txt"), "unrelated output"], - [resolve(root, "site/prepared-navigation-markers.mjs"), "accepted presentation"], + [resolve(root, "site/prepared/prepared-navigation-markers.mjs"), "accepted presentation"], ]); for (const [path, bytes] of previous) await writeFile(path, bytes); const filenames = (await readdir(outputRoot)).sort(); diff --git a/site/world/object-navigation.test.mts b/site/world/object-navigation.test.mts index afb2bdf11e5..77c59ba451a 100644 --- a/site/world/object-navigation.test.mts +++ b/site/world/object-navigation.test.mts @@ -10,7 +10,7 @@ import { objectNavigation, SEARCH_OBJECTS, PLANET_NAVIGATION_OBJECTS } from "../ import { BODY_MARKER_ATLAS_PAGE_SIZE, loadMarkerDescriptors } from "@cssearth/bake/navigation"; import { markerStyle, resolveMarkerStyle } from '@cssearth/renderer/navigation/marker-presentation.ts'; import { validateMarkerPresentation } from '@cssearth/objects'; -import { PREPARED_NAVIGATION_MARKERS } from "../prepared-navigation-markers.mjs"; +import { PREPARED_NAVIGATION_MARKERS } from "../prepared/prepared-navigation-markers.mjs"; test("search contains every object, including the Sun and the objects seen from inside; only planets enter the scale", () => { // An object seen from inside (the Milky Way, the Local Group) is an object like any other, with one search row.