@@ -91,9 +91,6 @@ import { z } from 'zod';
9191// branch selection to the package-internal policy module #8318 extracted.
9292import { formatZodIssue } from '@objectstack/spec' ;
9393import { zodIssuesToFields } from '@objectstack/spec/api' ;
94- import * as SpecRoot from '@objectstack/spec' ;
95- import * as SpecApi from '@objectstack/spec/api' ;
96- import * as SpecShared from '@objectstack/spec/shared' ;
9794// This package's own copy of the ranking, at the surface every producer here
9895// calls: `saveMetaItem`'s 422 (#5364) and `computeMetadataDiagnostics` (#5598).
9996import { zodIssuesToMetadataIssues } from './protocol.js' ;
@@ -267,16 +264,55 @@ describe('#8660 §1 the two sides cannot be sharing an implementation', () => {
267264 'UNION_BRANCH_SELECTION_LIMIT' ,
268265 ] as const ;
269266
267+ /**
268+ * Every PUBLIC entry point of `@objectstack/spec` that could plausibly carry
269+ * the policy, loaded dynamically.
270+ *
271+ * ⛔ The natural spelling of the root case — `import * as SpecRoot from
272+ * '@objectstack/spec'` — is refused by this repo's `no-restricted-imports`
273+ * rule, and the refusal is correct rather than an obstacle: a static
274+ * namespace binding on the root keeps all fifteen domain namespaces
275+ * (`Data`, `UI`, `Kernel`, …) reachable, which Node ESM cannot tree-shake,
276+ * and that is the ~1.2GB RSS regression the rule exists to prevent. The
277+ * lint run uses `--no-inline-config`, so there is no per-site opt-out — by
278+ * design.
279+ *
280+ * Loading dynamically is not an evasion dressed up as a fix. This file
281+ * already loads the root module for `formatZodIssue` above, so nothing
282+ * extra is pulled in; what the dynamic form drops is the long-lived
283+ * namespace BINDING the rule targets. {@link policySymbolsReachableFrom}
284+ * reduces each namespace to the handful of names §1 asks about and lets the
285+ * object go, so the assertion below is strictly LESS retentive than the
286+ * static spelling it replaces — while making exactly the same claim about
287+ * exactly the same public surface.
288+ */
289+ const SPEC_ENTRY_POINTS : ReadonlyArray < readonly [ string , ( ) => Promise < object > ] > = [
290+ [ '@objectstack/spec' , ( ) => import ( '@objectstack/spec' ) ] ,
291+ [ '@objectstack/spec/api' , ( ) => import ( '@objectstack/spec/api' ) ] ,
292+ [ '@objectstack/spec/shared' , ( ) => import ( '@objectstack/spec/shared' ) ] ,
293+ ] ;
294+
295+ /**
296+ * Which policy symbols one entry point actually exports.
297+ *
298+ * `in` against the module namespace, which is what "publicly reachable"
299+ * means for an ESM consumer — the same test the static spelling ran, on the
300+ * same object, without keeping it.
301+ */
302+ async function policySymbolsReachableFrom ( load : ( ) => Promise < object > ) : Promise < string [ ] > {
303+ const mod = ( await load ( ) ) as Record < string , unknown > ;
304+ return POLICY_SYMBOLS . filter ( ( name ) => name in mod ) ;
305+ }
306+
270307 // If this goes red, the policy became reachable from outside `packages/spec`
271308 // and the right response is to REWIRE `protocol.ts` onto it — deleting its
272309 // copy — not to keep three copies with a parity test over two of them.
273- it . each ( [
274- [ '@objectstack/spec' , SpecRoot as Record < string , unknown > ] ,
275- [ '@objectstack/spec/api' , SpecApi as Record < string , unknown > ] ,
276- [ '@objectstack/spec/shared' , SpecShared as Record < string , unknown > ] ,
277- ] ) ( '%s exports none of the policy symbols, so this package must run its own copy' , ( _entry , mod ) => {
278- expect ( POLICY_SYMBOLS . filter ( ( name ) => name in mod ) ) . toEqual ( [ ] ) ;
279- } ) ;
310+ it . each ( SPEC_ENTRY_POINTS ) (
311+ '%s exports none of the policy symbols, so this package must run its own copy' ,
312+ async ( _entry , load ) => {
313+ expect ( await policySymbolsReachableFrom ( load ) ) . toEqual ( [ ] ) ;
314+ } ,
315+ ) ;
280316
281317 it ( 'drives the spec side through public surfaces that DO exist' , ( ) => {
282318 // The other half of the same claim: the comparison below is not green
0 commit comments