You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Bun zero-install (bun run dev/start via the webjs-bun.mjs bootstrap, no node_modules) runs dependencies at LATEST, ignoring package.json + bun.lock (#684 research, #683 docs). That is a breaking-major hazard and is why #682 (defaulting bun create to --no-install) was closed.
The #684 research found a working fix: Bun honors INLINE versions in a specifier (import 'zod@3.22.4'), and an onLoad transform (webjs already runs one for TS-strip on Bun) can rewrite a bare specifier to its package.json-pinned inline version, so auto-install fetches the pinned version. Verified in a sprite: an onLoad rewrite of import('zod/...') to import('zod@3.22.4/...') made a bare import resolve to 3.22.4 instead of latest (F_VERSION=3.22.4).
Design / approach
In webjs's Bun onLoad plugin, after (or as part of) the TS-strip transform, rewrite import specifiers of DECLARED dependencies (from the app's package.json, preferably the exact version from bun.lock when present) to inline-versioned specifiers: from 'zod' becomes from 'zod@3.22.4', import('zod/sub') becomes import('zod@3.22.4/sub'). Bun auto-install then fetches the pinned version. This makes zero-install run the app's pinned deps, not latest.
Only the app's DIRECT deps need rewriting; a pinned direct dep brings its own subtree from its manifest. Confirm that holds.
Implementation notes (for the implementing agent)
Where: webjs's Bun-side Bun.pluginonLoad lives in packages/server/src/action-seed-bun.js (the TS-strip + seeding plugin). Add the specifier-rewrite there (or a sibling step in the same plugin), gated to Bun + the zero-install path. The bootstrap is webjs-bun.mjs (generated by packages/cli/lib/create.js, Zero-install dev/start on Bun via native auto-install (carve from #669) #675).
Read the pinned version from bun.lock (exact) when present, else the package.json range floor. Only rewrite specifiers whose bare name is a declared dep; leave everything else (relative, # alias, node:, already-versioned) untouched.
Landmines: do NOT rewrite the #-alias imports or relative paths; compose cleanly with the existing TS-strip onLoad (one plugin, ordered); the @webjsdev/* framework specifiers are also declared deps, so they would get pinned too (intended). Verify transitive-dep resolution stays deterministic. A bare import placed after an inline-versioned one in the same file ENOENT'd in the spike; rewriting ALL specifiers avoids that.
Tests: a test/bun/*.mjs cross-runtime assert that a bare import of a pinned dep resolves to the pinned version under zero-install (the research: can Bun zero-install respect package.json versions (not fetch latest)? #684 F experiment, automated); a counterfactual (without the rewrite, it resolves latest). This is runtime-sensitive Bun code, so it must be proven on Bun.
Problem
Bun zero-install (
bun run dev/startvia thewebjs-bun.mjsbootstrap, nonode_modules) runs dependencies at LATEST, ignoring package.json +bun.lock(#684 research, #683 docs). That is a breaking-major hazard and is why #682 (defaulting bun create to--no-install) was closed.The #684 research found a working fix: Bun honors INLINE versions in a specifier (
import 'zod@3.22.4'), and anonLoadtransform (webjs already runs one for TS-strip on Bun) can rewrite a bare specifier to its package.json-pinned inline version, so auto-install fetches the pinned version. Verified in a sprite: an onLoad rewrite ofimport('zod/...')toimport('zod@3.22.4/...')made a bare import resolve to 3.22.4 instead of latest (F_VERSION=3.22.4).Design / approach
In webjs's Bun
onLoadplugin, after (or as part of) the TS-strip transform, rewrite import specifiers of DECLARED dependencies (from the app's package.json, preferably the exact version frombun.lockwhen present) to inline-versioned specifiers:from 'zod'becomesfrom 'zod@3.22.4',import('zod/sub')becomesimport('zod@3.22.4/sub'). Bun auto-install then fetches the pinned version. This makes zero-install run the app's pinned deps, not latest.Only the app's DIRECT deps need rewriting; a pinned direct dep brings its own subtree from its manifest. Confirm that holds.
Implementation notes (for the implementing agent)
Bun.pluginonLoadlives inpackages/server/src/action-seed-bun.js(the TS-strip + seeding plugin). Add the specifier-rewrite there (or a sibling step in the same plugin), gated to Bun + the zero-install path. The bootstrap iswebjs-bun.mjs(generated bypackages/cli/lib/create.js, Zero-install dev/start on Bun via native auto-install (carve from #669) #675).import(variable), re-exports). Rewrite parsed import/export specifiers only.bun.lock(exact) when present, else the package.json range floor. Only rewrite specifiers whose bare name is a declared dep; leave everything else (relative,#alias, node:, already-versioned) untouched.#-alias imports or relative paths; compose cleanly with the existing TS-strip onLoad (one plugin, ordered); the@webjsdev/*framework specifiers are also declared deps, so they would get pinned too (intended). Verify transitive-dep resolution stays deterministic. A bare import placed after an inline-versioned one in the same file ENOENT'd in the spike; rewriting ALL specifiers avoids that.test/bun/*.mjscross-runtime assert that a bare import of a pinned dep resolves to the pinned version under zero-install (the research: can Bun zero-install respect package.json versions (not fetch latest)? #684 F experiment, automated); a counterfactual (without the rewrite, it resolves latest). This is runtime-sensitive Bun code, so it must be proven on Bun.agent-docs/runtime.md+ the runtime docs page. And this UNBLOCKS bun create defaults to no install (zero-install onboarding); node unchanged #682 (default bun create to--no-installbecomes safe), so revisit that.Acceptance criteria
#-alias / node: / already-versioned specifiers untouched.test/buncross-runtime test proves the pin + a counterfactual; proven on Bun.