Skip to content

Commit 19a8800

Browse files
committed
build(spec): changeset, README and gate docblock for the shared-chunk layout
Claude-Session: https://claude.ai/code/session_01Q3XCv7AXcUSSD479Vt1LXy Co-authored-by: Claude <noreply@anthropic.com>
1 parent f693373 commit 19a8800

3 files changed

Lines changed: 28 additions & 10 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
build(spec): every entry point shares one module graph, so a schema two entries reach ships once and is the same object through either
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`@objectstack/spec`'s JS outputs used to be one self-contained bundle per entry point. A consumer that imported several subpaths bundled one copy of every schema those subpaths share (`FieldSchema` was defined in 14 published `.mjs` files), and the same symbol reached through two subpaths was two different objects (`defineConnector` from `@objectstack/spec` was not `defineConnector` from `@objectstack/spec/integration`).
11+
12+
The build now emits shared chunks (`dist/chunk-<hash>.mjs` and `dist/chunk-<hash>.js`) that every entry imports. Measured on the console's own import list (256 value imports across 11 subpaths, esbuild `bundle` + `minify` + `platform: browser`, gzip level 9): 1,628.4 KB gzipped from the per-entry layout, 602.9 KB from this one. `FieldSchema` is defined once. Across the 119 export names two entries both expose, all 119 are now the same object under the `browser` condition, and 114 under Node, against 0 before. The 5 that still differ under Node are the metadata-collection normalizers reached through both `@objectstack/spec` and `@objectstack/spec/shared`: they sit on the path to the Node-only postgres URL grammar, so the Node outputs of the `browser`-conditioned entries keep their own copy of them.
13+
14+
What to do: nothing. The `exports` map and the type declarations are unchanged. Import only through the `exports` map: the chunk files are not entry points, and their names change from build to build.

‎packages/spec/README.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ The package publishes one entry per protocol domain (`@objectstack/spec/data`,
1919
(`@objectstack/spec/meta-spelling` — the `/meta/:type` URL-spelling contract
2020
and the `defineStack()` manifest-collection vocabulary, two deliberately
2121
distinct maps per objectstack#8424).
22-
Each entry is a self-contained bundle: what an entry's module graph reaches is
23-
what every consumer of that entry pays for.
22+
The entries share one module graph: a module two entries reach ships once, in
23+
a shared chunk under `dist/`, and is the same object through either entry.
24+
Chunks are not entry points — import only through the `exports` map. Chunk
25+
boundaries follow FILE-level reachability, so an entry links every chunk its
26+
module graph touches: what an entry's module graph reaches is still what every
27+
consumer of that entry may pay for.
2428

2529
**Standing principle** (maintainer ruling 2026-08-20, recorded verbatim on
2630
objectstack#10096):

‎packages/spec/scripts/check-browser-reachable-entries.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@
6161
* `dist/<entry>/index.mjs` convention. Repointing a subpath at a
6262
* schema-bearing bundle moves what this reads, exactly as it moves what a
6363
* consumer loads.
64-
* - **"it only reads one file."** Today every entry is self-contained
65-
* (`splitting: false`, and the scan finds zero relative specifiers across all
66-
* 34 bundles), so a one-file check would pass today and go quietly blind the
67-
* day `splitting` is turned on and zod arrives through a shared chunk. So the
68-
* walk is TRANSITIVE over relative hops, and a relative specifier that does
69-
* not resolve is a hard error — an incomplete walk reporting "no zod" is the
70-
* false green this gate exists to prevent. The self-test drives that walk
71-
* over a synthetic chunk chain, because this tree cannot exercise it.
64+
* - **"it only reads one file."** Every entry imports shared chunks
65+
* (`tsup.config.ts`, `oneModuleGraph`), so zod or a server-only package
66+
* arrives through a relative hop far more often than in the entry file
67+
* itself. So the walk is TRANSITIVE over relative hops, and a relative
68+
* specifier that does not resolve is a hard error — an incomplete walk
69+
* reporting "no zod" is the false green this gate exists to prevent. The
70+
* self-test drives that walk over a synthetic chunk chain as well, so the
71+
* walk stays pinned whatever the build's chunk layout is.
7272
* - **"it reads prose."** The bundles carry documentation strings, and one of
7373
* them really does contain the text `from '@objectstack/spec/ui'` inside a
7474
* `reason:` string in `dist/index.mjs` — a naive grep reports it as an

0 commit comments

Comments
 (0)