From 613a2b0ca0aff4452e12a56831eda218126942fb Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 8 Sep 2026 01:38:02 +0000 Subject: [PATCH] test(types): pin the host-only resolution defect on a name the workspace can never contain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The host-only example was `@objectstack/organizations`, a real cloud-private package when the pin was written. Once that package became a workspace member, pnpm's hoisted store carried it and the framework-anchored resolve succeeded on any built tree — so the assertion held in CI only because the shard running it had not built that package. Build order, not the property it asserts. The example is now `@fixture/host-only`, a scope this workspace cannot contain, and the case proves its own premise instead of assuming it: it first resolves the name FROM THE HOST APP (without which a name existing nowhere would satisfy the assertion vacuously), then requires the framework-side failure to name the BARE SPECIFIER. A reachable-but-unbuilt package throws MODULE_NOT_FOUND naming its missing entry file instead, so that throw can no longer satisfy this pin in either build state. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8 --- packages/types/src/node.test.ts | 49 +++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/types/src/node.test.ts b/packages/types/src/node.test.ts index 90029660ef..394683344f 100644 --- a/packages/types/src/node.test.ts +++ b/packages/types/src/node.test.ts @@ -41,8 +41,20 @@ import { readHostDeclaration, } from './node.js'; -/** The cloud-private package at the heart of cloud#1013. */ -const ORGANIZATIONS = '@objectstack/organizations'; +/** + * The HOST-ONLY package the defect case below uses as its example: a name this + * file writes into the fixture host app and that exists nowhere else. + * + * ⚠️ It must stay a `@fixture/*` name (#16552). The pin below was written + * against `@objectstack/organizations`, a real cloud-private package at the + * time. #16215 brought that package into this workspace, pnpm's hoisted store + * started carrying it, and the assertion turned into a function of whether the + * runner had BUILT it — green on a shard that had not (which is every CI shard + * that runs this file), red on any full local build. A name the workspace owns + * cannot state "the framework cannot resolve it", and no workspace name is safe + * from becoming one; only a name the workspace can never contain is. + */ +const HOST_ONLY = '@fixture/host-only'; /** A package that fails while it EVALUATES — not while it resolves. */ const BROKEN = '@fixture/throws-on-load'; /** @@ -99,7 +111,7 @@ beforeAll(() => { JSON.stringify({ name: 'host-app-fixture', type: 'module', - dependencies: { [ORGANIZATIONS]: '*' }, + dependencies: { [HOST_ONLY]: '*' }, // #4719 fixture amendment: the evaluation-crash case below imports this // package, and an undeclared name is no longer looked up in the host's // node_modules at all — so the crash it exists to prove would be masked by @@ -111,9 +123,12 @@ beforeAll(() => { }), 'utf8', ); + // Modelled on the real enterprise plugin (cloud#1013): the SHAPE is what + // `serve` / `bootStack` construct, and the callers' cases below assert it. The + // package NAME is deliberately fixture-only, for the reason given at HOST_ONLY. writeFixturePackage( hostRoot, - ORGANIZATIONS, + HOST_ONLY, 'export class OrganizationsPlugin { name = "com.objectstack.organizations"; }\n', ); writeFixturePackage(hostRoot, BROKEN, 'throw new Error("fixture package exploded on import");\n'); @@ -163,14 +178,32 @@ describe('host-app package resolution (cloud#1013, #4700)', () => { // node -e "require.resolve('@objectstack/organizations')" -> MODULE_NOT_FOUND // A bare `import()` in serve.ts / harness.ts resolved from exactly here, // which is why declaring the dependency in the app changed nothing. - expect(() => createHostRequire(PACKAGE_ROOT).resolve(ORGANIZATIONS)).toThrow( - /Cannot find module/, + // + // PREMISE, proved here instead of assumed (#16552). The sentence this case + // states needs an example that IS host-only, and the old one silently + // stopped being one. Both legs below are load-bearing: + // + // 1. the name resolves from the HOST APP — without this, a name that + // exists nowhere at all satisfies leg 2 and the case pins nothing; + // 2. and from the framework package it is ABSENT — asserted on the bare + // specifier, not on `/Cannot find module/` alone, because those are two + // different verdicts. A package the framework CAN see, whose entry file + // merely is not on disk, also throws MODULE_NOT_FOUND — naming + // `//dist/index.js`, not the specifier. That throw is what + // held this pin green in CI while the property went unguarded: the + // example package was reachable and simply unbuilt on that shard. Read + // as a bare-specifier failure, the pin can no longer be satisfied by an + // unbuilt workspace package, in either build state. + const fromHost = createHostRequire(hostRoot).resolve(HOST_ONLY); + expect(fromHost).toContain(hostRoot); + expect(() => createHostRequire(PACKAGE_ROOT).resolve(HOST_ONLY)).toThrow( + new RegExp(`Cannot find module '${HOST_ONLY}'`), ); }); it('resolves a package that exists ONLY in the host app', async () => { const importFromHost = createHostImporter(hostRoot); - const mod = await importFromHost(ORGANIZATIONS); + const mod = await importFromHost(HOST_ONLY); // The export `serve` and `bootStack` construct: `new mod.OrganizationsPlugin()`. expect(typeof mod.OrganizationsPlugin).toBe('function'); expect(new mod.OrganizationsPlugin().name).toBe('com.objectstack.organizations'); @@ -548,7 +581,7 @@ describe('the undeclared fallback resolves from the CALLER (#10943)', () => { // The fix moves one branch. The declared path must still resolve from the // host app, and an undeclared-but-NODE_PATH-reachable package must still be // refused: a caller base is not a way back into the hoisted store. - const mod = await createHostImporter(hostRoot, { fallbackImport })(ORGANIZATIONS); + const mod = await createHostImporter(hostRoot, { fallbackImport })(HOST_ONLY); expect(new mod.OrganizationsPlugin().name).toBe('com.objectstack.organizations'); const err = await createHostImporter(undeclaringRoot, { fallbackImport })( HOISTED_ONLY,