Question
Can webjs db / test / typecheck run under bun zero-install (no node_modules), and how? This is the deep research for #704 after its first approach (a runner that imports the tool bin + a spawn pin preload) was found broken.
Verdict
The spawned dev tools (drizzle-kit, the bun test runner, tsc) fundamentally need a materialized node_modules; bun's auto-install + global cache + plugins cannot substitute. The reconciling fix is to auto-materialize node_modules via a fast bun install (cheap because the deps are already in bun's global cache) and run the tool from the app context (the existing resolveBin path), NOT the runner / preload / bun x approach. The server (dev / start) stays true zero-install via the pin shim; only the spawned tooling materializes.
Findings (verified empirically on bun 1.3.14)
-
bun x <tool> runs the tool from an ISOLATED temp dir (/tmp/bunx-.../node_modules/<tool>/), so drizzle-kit cannot resolve the APP's drizzle-orm. This fails EVEN WITH a full bun install present, so bun x was always the wrong tool, independent of zero-install. drizzle-kit must run from the app's own node_modules context.
-
The db failure is a version mismatch surfaced by auto-install. drizzle-kit@1.0.0-rc.3 requires drizzle-orm/_relations, which exists in drizzle-orm@1.0.0-rc.3 but NOT in drizzle-orm@0.45.2. npm latest is 0.45.2 (the rc line is a prerelease under a non-latest tag), so bun's bare auto-install resolves drizzle-orm to 0.45.2 and the subpath is missing.
-
Bun plugins (Bun.plugin onLoad / onResolve) do NOT intercept CommonJS require() resolution, only ESM imports. drizzle-kit's bin.cjs is CommonJS, so the pin shim cannot reach its internal require('drizzle-orm/_relations'). A bunfig preload DOES run under bun x (verified), but its onResolve never fires for the CJS require. onLoad on a cached CJS file also breaks it (returning { contents, loader } forces ESM parsing of a CJS module).
-
package.json overrides and bunfig [install.overrides] do NOT reach the bun x temp process, so they cannot pin the version there.
-
The fix works end to end. With the deps already in bun's global cache (zero-install DOES download to the global cache on disk), bun install materializes node_modules in ~57ms (measured). Running drizzle-kit from the app context then fully generates a real migration ([✓] Your SQL migration -> drizzle/<ts>_init/migration.sql). bun test (which has no auto-install at all) and tsc also work once node_modules exists.
Recommendation for #704 (separate implementation issue)
Drop the runner + the keystone-preload-for-tooling + the bin-import wiring (it cannot work). Instead: webjs db / test / typecheck under bun zero-install auto-materialize node_modules via a fast bun install, then run the existing resolveBin path. This is "no MANUAL install" (webjs does it transparently and fast), the honest reconciliation of the zero-install goal with the reality that CommonJS + esbuild tools and bun test need node_modules.
Tradeoff to document: after the first tool invocation node_modules exists, so the app is "installed" thereafter (everything keeps working; the user never typed an install command). The seamless progression is: start zero-install (instant dev), and the first time you need tooling, webjs materializes in tens of milliseconds.
Relates to #704, #684/#698 (the pin shim, which stays for the server), #669 (the zero-install vision), #718 (lockfile reproducibility, which the materialized lock would also serve).
Question
Can
webjs db/test/typecheckrun under bun zero-install (nonode_modules), and how? This is the deep research for #704 after its first approach (a runner that imports the tool bin + a spawn pin preload) was found broken.Verdict
The spawned dev tools (drizzle-kit, the bun test runner, tsc) fundamentally need a materialized
node_modules; bun's auto-install + global cache + plugins cannot substitute. The reconciling fix is to auto-materializenode_modulesvia a fastbun install(cheap because the deps are already in bun's global cache) and run the tool from the app context (the existingresolveBinpath), NOT the runner / preload /bun xapproach. The server (dev / start) stays true zero-install via the pin shim; only the spawned tooling materializes.Findings (verified empirically on bun 1.3.14)
bun x <tool>runs the tool from an ISOLATED temp dir (/tmp/bunx-.../node_modules/<tool>/), so drizzle-kit cannot resolve the APP'sdrizzle-orm. This fails EVEN WITH a fullbun installpresent, sobun xwas always the wrong tool, independent of zero-install. drizzle-kit must run from the app's ownnode_modulescontext.The db failure is a version mismatch surfaced by auto-install. drizzle-kit@1.0.0-rc.3 requires
drizzle-orm/_relations, which exists in drizzle-orm@1.0.0-rc.3 but NOT in drizzle-orm@0.45.2. npmlatestis 0.45.2 (the rc line is a prerelease under a non-latest tag), so bun's bare auto-install resolves drizzle-orm to 0.45.2 and the subpath is missing.Bun plugins (
Bun.pluginonLoad / onResolve) do NOT intercept CommonJSrequire()resolution, only ESM imports. drizzle-kit'sbin.cjsis CommonJS, so the pin shim cannot reach its internalrequire('drizzle-orm/_relations'). A bunfig preload DOES run underbun x(verified), but its onResolve never fires for the CJS require. onLoad on a cached CJS file also breaks it (returning{ contents, loader }forces ESM parsing of a CJS module).package.json
overridesand bunfig[install.overrides]do NOT reach thebun xtemp process, so they cannot pin the version there.The fix works end to end. With the deps already in bun's global cache (zero-install DOES download to the global cache on disk),
bun installmaterializesnode_modulesin ~57ms (measured). Running drizzle-kit from the app context then fully generates a real migration ([✓] Your SQL migration -> drizzle/<ts>_init/migration.sql).bun test(which has no auto-install at all) andtscalso work oncenode_modulesexists.Recommendation for #704 (separate implementation issue)
Drop the runner + the keystone-preload-for-tooling + the bin-import wiring (it cannot work). Instead:
webjs db/test/typecheckunder bun zero-install auto-materializenode_modulesvia a fastbun install, then run the existingresolveBinpath. This is "no MANUAL install" (webjs does it transparently and fast), the honest reconciliation of the zero-install goal with the reality that CommonJS + esbuild tools andbun testneednode_modules.Tradeoff to document: after the first tool invocation
node_modulesexists, so the app is "installed" thereafter (everything keeps working; the user never typed an install command). The seamless progression is: start zero-install (instant dev), and the first time you need tooling, webjs materializes in tens of milliseconds.Relates to #704, #684/#698 (the pin shim, which stays for the server), #669 (the zero-install vision), #718 (lockfile reproducibility, which the materialized lock would also serve).