Skip to content

Commit c96886d

Browse files
committed
test(qa): ledger the out-of-repo consumer specifiers, held against the exports maps
An `exports` map is a PACKAGING contract, and inside this monorepo nothing is sealed: every in-repo consumer reaches any file through a relative import, a vitest alias or a `paths` entry. So the repo could not observe a sealing regression at all, by construction — twice the seal 17.3.0 introduced (#13123) was found after publish, by the consumer, during an upgrade (#13662, #15325). `consumer-specifiers.ledger.json` imports that knowledge from outside: the bare specifiers named out-of-repo consumers import, each with the consumer, a date and the card that ratified it. The suite packs each ledgered package with `pnpm pack`, unpacks it into a throwaway `node_modules` outside the workspace, and resolves every specifier from there with Node's real resolver under both the `require` and `import` conditions — failing with a message that names the specifier AND the consumer it breaks. Option A of #15589 only. Option B (a changeset-gated seal declaration in `check:published-files`) is deliberately not here. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
1 parent 39a318c commit c96886d

1 file changed

Lines changed: 17 additions & 2 deletions

File tree

‎packages/qa/downstream-contract/test/consumer-specifier-ledger.test.ts‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,14 @@
4141
* resolved by a child `node` whose cwd is that directory and which has nothing
4242
* of this workspace on its resolution path — `createRequire().resolve()` for the
4343
* `require` condition and `import.meta.resolve()` for the `import` condition.
44-
* That is the shape #15325's own pin uses, for the same reason.
44+
*
45+
* That is the same technique as the pin PR #15611 adds under `packages/cli`,
46+
* arrived at for the same reason. ⚠️ That file is NOT on this tree — #15611 is
47+
* open and unmerged — so this suite reproduces the technique rather than reusing
48+
* it, and the two are independent by construction: the pin asks "does the CLI's
49+
* map still spell the subpaths this repo ratified", this asks "does every
50+
* specifier a named out-of-repo consumer imports still resolve". A pin over one
51+
* package's map cannot see the next package that gains one.
4552
*
4653
* ## SEALED_TODAY, and why the red is LEDGERED rather than simply asserted
4754
*
@@ -303,7 +310,15 @@ describe('the ledger itself', () => {
303310

304311
it('records BARE specifiers — never the dist/ deep paths the seals closed', () => {
305312
for (const entry of ENTRIES) {
306-
expect(entry.specifier, 'a ledgered specifier must be a subpath, not the bare package name').toContain('/');
313+
// NOT `toContain('/')` — a SCOPED bare package name (`@objectstack/cli`)
314+
// contains one too, so that spelling admits exactly the entry this rule
315+
// exists to reject. The package name is what the ledger is silent about;
316+
// a subpath is what it records.
317+
expect(
318+
entry.specifier,
319+
`${entry.specifier} is a bare package name, not a subpath. The root entry of an exports ` +
320+
'map is never at risk in the way a subpath is — ledger the subpath the consumer imports',
321+
).not.toBe(packageNameOf(entry.specifier));
307322
expect(
308323
entry.specifier,
309324
`${entry.specifier} is a dist/ deep path — those are what an exports map deliberately seals; ` +

0 commit comments

Comments
 (0)