Repository navigation
Commit 35e549b
fix(plugin-dev): load setup/account through literal specifiers, so init() makes no main-process round trip under vitest (#20452)
Fixes #20376
Clause-②: no
## What this changes
`DevPlugin.init()`'s setup / account app-package loop imported both
packages through one variable specifier, `import(/* @vite-ignore */
spec[0])`. Under vitest a variable specifier cannot be resolved when
`dev-plugin.ts` is transformed, so every call went to the main vitest
process, mocked packages included: two main-process round trips per
`init()`, inside every clocked test window that boots `DevPlugin`. The
main process is shared by the whole run, so on a busy CI shard those
requests wait on other files' work. That is the shape of the `Test Core
(6/6)` red on PR #20365 (job 108779589417): the mount-refusal suite's
first case timed out at 5026 ms and its second took 2336 ms, while its
other four took 16-72 ms, in a package run reading `transform 74.29s,
import 137.46s, tests 11.87s`.
Route A of the seat's answer on the card: each loop entry now carries
its own literal loader (`() => import('@objectstack/setup')`, `() =>
import('@objectstack/account')`). The `try` / `catch` and
`reportOptionalLoadFailure` around each load are byte-for-byte
unchanged. No timeout, retry or test-arrangement change.
Files:
- `packages/plugins/plugin-dev/src/dev-plugin.ts`: the loop only (two
tuple entries, one call line, one comment block).
-
`packages/plugins/plugin-dev/src/dev-plugin-literal-imports.pin.test.ts`
(new): every dynamic `import()` in `dev-plugin.ts` takes a string
literal, except `organizationsPkg` (ADR-0132); read off the TypeScript
AST.
-
`packages/plugins/plugin-dev/src/dev-plugin-optional-load-failure.test.ts`:
one case pinning that setup / account, when they cannot be imported,
still reach the absent arm (one line each at `warn`, with the resolver's
code and the failing specifier, never at `error`).
- `packages/plugins/plugin-dev/vitest.config.ts`: two anchored alias
entries for `@objectstack/setup` / `@objectstack/account`, which
`check:test-source-alias` requires once the specifiers are literal (see
Acceptance notes).
- `.changeset/20376-plugin-dev-literal-app-imports.md`:
`@objectstack/plugin-dev` `patch`.
## Takeover record
This branch was started by a dev whose seat session ran out of tokens
after pushing `952e1249` and `4160ecbe`, with no PR and no report. This
continuation read both commits hunk by hunk as unverified work, merged
`origin/main` (`0fcb10184c`) and re-ran every measurement below itself.
- Kept: the `dev-plugin.ts` code lines, the changeset's frontmatter and
`Clause-②` line, and the pin's `__dirname` seed (`4160ecbe`).
- Corrected in `917a31bc72`: the `dev-plugin.ts` comment and the
changeset said the loop now loads "like every other load / package",
which the deliberate `organizationsPkg` variable specifier contradicts
(now: every other declared-dependency load); the changeset said the
build keeps `await import("…")`, while `dist/index.mjs` and
`dist/index.js` both keep a native `import("…")` inside an arrow; the
pin header carried the earlier run's load timings (44.5-102.0 ms per
`init()`, a 5048 ms local timeout), which this run did not re-measure,
and now states the counts below plus the CI job reading, which this run
read from the job log itself.
- Corrected in `35833d2a2c`: the pin's private regex comment stripper
was refused by `check:comment-mask-adoption`; the pin now walks the
TypeScript syntax tree (`typescript` is already a devDependency of this
package).
- Added in `917a31bc72`: the absent-arm pin (no existing pin covered
this loop's failure path).
- Added in `a24f6ea84c`: the two vitest alias entries
(`check:test-source-alias` red without them).
## Measurements (H1-H4)
**H1: the round trips, counted.** A throwaway vite plugin (deleted
after) logged every `resolveId` and `transform` the main process served,
around six back-to-back `init()`s in a throwaway probe with every
package mocked absent. Same method, same run shape, both legs:
| `dev-plugin.ts` | main-process requests during the six `init()`s | per
`init()` |
|---|---|---|
| as on `origin/main` (blob `b132ad3f4`) | 20 | first: 10 (2 bare
`resolveId`, 2 `/@fs` `resolveId`, 2 `dist/index.mjs` transforms, 4
`resolveId`s of their imports); each later: 2 (`@objectstack/setup`,
`@objectstack/account`) |
| this branch, `a24f6ea84c` | 0 | 0 |
The real `dev-plugin-tenancy-mount-refusal.test.ts` under the same
logger (importer `dev-plugin.ts`): on `origin/main`, 9 `resolveId`s of
each package (nine `init()`s, 18 in all) plus both `dist` transforms; on
this branch, none in the test phase. Every leg logged both absent-app
warnings on every `init()` (6 of 6).
**H2: the pin fails when the variable specifier comes back.** `node
scripts/ablation-replace.mjs`, WRAP mode, at `35833d2a2c`:
- the call line back to `await import(/* @vite-ignore */ spec[0])`: ①
red (`1 failed | 2 passed`), received `[ 'spec[0]', 'organizationsPkg'
]`;
- the whole hunk back to the `origin/main` blob `b132ad3f4`: ① and ③ red
(`2 failed | 1 passed`; ③ reports 12 literal loads, none of them setup /
account).
Each restore was proven by the tool: blob after restore equals the HEAD
blob (`1f1ac117f`) and `git diff HEAD` is empty. The pin reads
`dev-plugin.ts`'s source text, so no `dist/` leg applies.
**H3: the absent-package path is unchanged.** The new case is green on
this branch and green with `dev-plugin.ts` swapped to the `origin/main`
blob (`10 passed` both), so it pins behaviour the fix preserves. It goes
red when that path breaks: a silent `catch` (`if (err) continue;`) gives
`1 failed | 9 passed`, and demoting the loop's `absentLevel` to `info`
gives `1 failed | 9 passed`. Restores proven as above. (A first attempt
at the silent-catch leg was refused by the tool, because its replacement
contained its own anchor; no test ran, and it was retried with a
different anchor.)
**H4: the suites pass.** `pnpm --filter @objectstack/plugin-dev test` at
`a24f6ea84c`: `Test Files 9 passed (9)`, `Tests 86 passed (86)`,
mount-refusal 6/6, failfast 9/9, tenancy-posture 6/6,
optional-load-failure 10/10, security-enforcement-warning 4/4,
malformed-stack-posture 4/4, `dev-plugin.test.ts` 24/24, i18n reader
20/20, the new pin 3/3.
## Verification at `a24f6ea84c`
- `pnpm turbo run build --filter='@objectstack/plugin-dev^...'
--concurrency=2`: 34/34.
- `pnpm --filter @objectstack/plugin-dev typecheck`: exit 0 (`tsc
--noEmit` and `check:test-typecheck`; `--listFiles` shows both touched
test files in both programs).
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands`: 62 commands, all exit 0; `--ran` reconciliation: 62
derived, 62 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero: every line
carries its exit code). On the way: `check:comment-mask-adoption` and
`check:test-source-alias` were red on this branch's pushed state and are
fixed above; `check:dual-build-cjs-loads` and `check:type-check-debt`
first answered PREREQUISITE NOT MET and were re-run after `pnpm exec
turbo run build --filter='./packages/*' --filter='./packages/*/*'
--concurrency=2` (71/71).
- `node scripts/check-issue-citations.mjs --base origin/main`: exit 0
(also exit 0 with `--base 0fcb101`, the merge base).
- `pnpm lint`: exit 0, no findings.
- NOT MEASURED locally, declared to CI: the `Test Core`, `Dogfood`,
`Build Core` and `Temporal Conformance` jobs and the workspace
type-check lanes the derivation lists as CI-only.
**Declared narrowing — verification ran UNLOCKED.**
`scripts/pm/os-verify-lock.sh`
could not take the shared verify lock on this host: no usable `flock`.
The shared
verify lock is declared Linux-only (`flock` is util-linux, and a stock
macOS does
not ship it), so the command below was run directly, without the lock —
a declared narrowing, not a silent one. No serialization guarantee held
for this
run, nor for any sibling agent in this container while it ran.
pnpm turbo run build --filter='@objectstack/plugin-dev^...'
--concurrency=2
pnpm --filter @objectstack/plugin-dev build
pnpm exec turbo run build --filter='./packages/*'
--filter='./packages/*/*' --concurrency=2
pnpm --filter @objectstack/plugin-dev typecheck
pnpm --filter @objectstack/plugin-dev test
pnpm --filter @objectstack/plugin-dev exec vitest run --maxWorkers=2
(suite, probe and ablation runs)
## Acceptance notes
- **File surface.** `packages/plugins/plugin-dev/vitest.config.ts` is
not in the claim's listed surface. It is the mechanical consequence of
route A: literal specifiers make both packages visible to
`check:test-source-alias`, which then requires them aliased to source.
The two entries are the ones the gate printed; both packages' own
imports were already aliased. The absent-arm case in
`dev-plugin-optional-load-failure.test.ts` is the "smallest pin" the
dispatch's H3 asked for.
- **Census notes carried from the seat's answer, not re-measured under
load.** `dev-plugin-malformed-stack-posture.test.ts` case 4 pays a real
cold load of `objectql` and `driver-memory` inside a window widened by
an explicit `60_000`, and the first case of
`dev-i18n-packages-reader.test.ts` pays a first-use cost. Neither is
failing (idle maxima at `a24f6ea84c`: 76 ms and 28 ms). No CPU-load
generator was run on this shared host.
- **Timing.** This PR rests on the deterministic counts above and the CI
job reading. The earlier run's under-load timings (on the card) were not
re-measured here.
- **Same loop shape in the CLI.** `packages/cli/src/commands/serve.ts`
(around line 4024) loads setup / account through the same kind of
variable-specifier loop (with `webpackIgnore`). In a spawned CLI process
there is no vitest main process to call, so it costs nothing there; a
vitest suite that ran that branch in-process would pay the same round
trip. Not measured. Noted, not filed.
- **A stale docblock sentence.** `dev-plugin.ts`'s
`MODULE_NOT_FOUND_CODES` docblock says the CJS build resolves these
loads through `require()`. The current `dist/index.js` keeps a native
`import("…")` for every dynamic load. It is harmless, because both codes
stay in the set. Noted, not filed.
- **Bundler visibility.** A consumer's bundler now sees
`@objectstack/setup` and `@objectstack/account` statically, as it
already saw the twelve other literal loads. Both are declared
dependencies. `@objectstack/organizations`, which no framework package
may declare, keeps its `webpackIgnore` variable form.
---
_Generated by [Claude
Code](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>1 parent b98fbc2 commit 35e549b
5 files changed
Lines changed: 165 additions & 3 deletions
File tree
- .changeset
- packages/plugins/plugin-dev
- src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
285 | 285 | | |
286 | 286 | | |
287 | 287 | | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
288 | 308 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
734 | 734 | | |
735 | 735 | | |
736 | 736 | | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
737 | 746 | | |
738 | | - | |
739 | | - | |
| 747 | + | |
| 748 | + | |
740 | 749 | | |
741 | 750 | | |
742 | | - | |
| 751 | + | |
743 | 752 | | |
744 | 753 | | |
745 | 754 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
65 | 73 | | |
66 | 74 | | |
67 | 75 | | |
| |||
0 commit comments