From 102b1c71cfb9c34b5f28529b14bfdcac8ede3f05 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 18:13:28 +0000 Subject: [PATCH] fix(plugin-dev): resolve plugin-security from source in tests, shrink the alias registry `dev-plugin.ts` boots the real SecurityPlugin through a dynamic `await import('@objectstack/plugin-security')`, and the enforcement-warning test deliberately leaves that chain unmocked. Without a source alias that import resolved through `exports` to `dist/`, so the file's verdict was a function of build state: on an unbuilt closure the whole file died at collection (`Test Files 1 failed` / `Tests no tests`), and on a built-but-stale closure it would have passed against the old artifact silently -- the quiet failure `check:test-source-alias` exists to make impossible. The alias goes in `vitest.config.ts`, not on the test's import specifier: a config alias covers the production dynamic import too, because Vite resolves the whole module graph through it. Aliasing the specifier instead was measured to leave the two divergent and re-arm the clocked-window cost #10115 removed. Aliasing a dep to source imports its entire surface into this package's resolution domain, so plugin-security's own `formula` / `metadata-core` / `platform-objects` imports are aliased with it. `platform-objects` publishes a FILE-shaped `./plugin` subpath alongside directory-shaped ones, so that rule is listed separately ahead of the enumerated group. With the import off `dist/`, `@objectstack/plugin-security` comes off `@objectstack/plugin-dev`'s entry in the shrink-only `KNOWN_UNALIASED_TEST_IMPORTS` registry (10 -> 9), which the gate's set-equality audit then requires. Fixes #10112 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r --- packages/plugins/plugin-dev/vitest.config.ts | 42 ++++++++++++++++++++ scripts/check-test-source-alias.mjs | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/plugins/plugin-dev/vitest.config.ts b/packages/plugins/plugin-dev/vitest.config.ts index c0744e6f33..f9689a0ce8 100644 --- a/packages/plugins/plugin-dev/vitest.config.ts +++ b/packages/plugins/plugin-dev/vitest.config.ts @@ -15,6 +15,48 @@ export default defineConfig({ }, resolve: { alias: [ + // [#10112] `@objectstack/plugin-security` and the source graph it drags in. + // + // `dev-plugin.ts` boots the real SecurityPlugin through a dynamic + // `await import('@objectstack/plugin-security')`, and + // `dev-plugin-security-enforcement-warning.test.ts` deliberately leaves that + // chain unmocked -- the real plugin's `init()`/`start()` phase split IS the + // subject. Without these entries that import resolves through `exports` to + // `dist/`, so the file's verdict was a function of build state: on an unbuilt + // closure the whole file died at COLLECTION with `Failed to resolve entry for + // package "@objectstack/plugin-security"` (`Test Files 1 failed` / `Tests no + // tests`), and on a built-but-STALE closure it would have passed against the + // old artifact silently -- the quiet failure `check:test-source-alias` exists + // to make impossible. + // + // ⛔ The alias belongs HERE, never on the test's line-51 specifier. Measured: + // rewriting line 51 to a source path leaves `dev-plugin.ts`'s dynamic import + // still pointing at `dist/`, so the two diverge and line 51 pre-warms a copy + // nothing uses -- bail #1 went 28ms -> 476ms on a built closure, re-arming the + // clocked-window cost that #10115 / PR #10120 landed to remove. A config alias + // covers BOTH specifiers because Vite resolves the whole module graph through + // it, which is why one entry here beats an edit at either import site. + // + // Aliasing a dep to source imports its ENTIRE surface into this package's + // resolution domain (see the gate's "Where the walk goes" note), so + // plugin-security's own `formula` / `metadata-core` / `platform-objects` + // imports have to be aliased too -- without them the failure merely MOVES + // outward one package at a time. + // + // Subpaths precede the bare entry, and the FILE-shaped `./plugin` gets its own + // rule ahead of the directory-shaped group: `platform-objects` publishes both + // kinds, so a single `([a-z-]+)` rule of the sort `@objectstack/spec` can use + // below would resolve `./plugin` to `…/src/plugin/index.ts`. The group + // enumerates the directory-shaped subpaths for exactly that reason. + { find: /^@objectstack\/platform-objects\/plugin$/, replacement: path.resolve(__dirname, '../../platform-objects/src/plugin.ts') }, + { + find: /^@objectstack\/platform-objects\/(identity|pages|security|metadata|system|apps|audit|integration|metadata-translations)$/, + replacement: path.join(path.resolve(__dirname, '../..'), 'platform-objects/src/$1/index.ts'), + }, + { find: /^@objectstack\/platform-objects$/, replacement: path.resolve(__dirname, '../../platform-objects/src/index.ts') }, + { find: /^@objectstack\/plugin-security$/, replacement: path.resolve(__dirname, '../plugin-security/src/index.ts') }, + { find: /^@objectstack\/formula$/, replacement: path.resolve(__dirname, '../../formula/src/index.ts') }, + { find: /^@objectstack\/metadata-core$/, replacement: path.resolve(__dirname, '../../metadata-core/src/index.ts') }, // Subpath BEFORE the bare package: `@objectstack/core` is a PREFIX match with a // FILE replacement, so without this entry it swallows the published `./logger` // subpath and resolves it to `…/core/src/index.ts/logger` — ENOTDIR at run time. diff --git a/scripts/check-test-source-alias.mjs b/scripts/check-test-source-alias.mjs index 2fd610d7de..ed760b803f 100644 --- a/scripts/check-test-source-alias.mjs +++ b/scripts/check-test-source-alias.mjs @@ -410,7 +410,7 @@ const KNOWN_UNALIASED_TEST_IMPORTS = { ], '@objectstack/plugin-dev': [ '@objectstack/driver-memory', '@objectstack/objectql', '@objectstack/plugin-auth', - '@objectstack/plugin-hono-server', '@objectstack/plugin-security', '@objectstack/rest', + '@objectstack/plugin-hono-server', '@objectstack/rest', '@objectstack/runtime', '@objectstack/service-i18n', '@objectstack/service-realtime', '@objectstack/service-storage', ],