Skip to content

harden the bun auto-install pin shim (fail-open, capability probe, escape hatch, CI canary) #715

Description

@vivek7405

Problem

The bun "pin shim" (the inline-import transpile that rewrites a bare import 'zod' into import 'zod@^3.0.0' from package.json / bun.lock so bun zero-install fetches the declared version instead of latest, plus its siblings: the cli @webjsdev retry #709, the spawn preload #704, the importmap version fallback #699) is a WORKAROUND coupled to bun internals (Bun.plugin onLoad, inline-specifier resolution which is bun's own "buggy and untested" auto-install path, bun --preload, Bun.Transpiler.scanImports). It cannot be fault-proof; a future bun release can change or obsolete it. Today it has concrete resilience gaps that would make a bun change break boot rather than degrade gracefully.

This is the CODE half of the resilience plan; the documentation half (canonical shim doc, grep markers, deletion checklist) is #714.

Design / approach (resilience properties to build in)

  1. Fail-open everywhere. CONCRETE GAP: packages/server/src/bun-pin-preload.js calls rewriteDepSpecifiers(src, imports, versions) in its onLoad WITHOUT a try/catch, so a throw breaks the spawned tool load instead of falling back to bun's native resolution. Contrast packages/server/src/action-seed-bun.js, which DOES wrap pinTransform in try/catch (fail-open to raw source). Wrap the preload rewrite the same way, and audit every shim site for fail-open.
  2. Capability probe + self-disable. Detect at boot whether THIS bun already honors package.json / bun.lock natively (a tiny probe resolving a known dep and checking the resolved version). If yes, skip the rewrite entirely, so the shim self-disables the day bun fixes the gap (the runtime version of "easy to trash"). Fail-safe: if the probe is inconclusive, keep the shim ON.
  3. Escape hatch. A WEBJS_BUN_PIN=0 env var (and the webjs config block) to disable the whole pin mechanism, so a user on a future bun that breaks the shim can opt out without waiting for a webjs release. Mirror the existing WEBJS_ELIDE=0 / WEBJS_SEED=0 pattern.
  4. CI against bun latest + canary. The Bun matrix CI (scripts/run-bun-tests.js) currently pins a bun version, so a future bun behavior change is invisible until a user hits it. Add a job running the bun-pin assertions against bun LATEST and CANARY so a behavior change FAILS loudly and early (the real safety net for a workaround built on evolving internals).
  5. Centralization audit. Confirm all bun-pin coupling lives in the enumerated shim sites (ties docs: make the bun auto-install pin shim self-documenting + deletable #714), so a bun change is a one-area fix.

Design record context (for the cold implementing agent)

The pin shim is the ONLY mechanism for the zero-install DEFAULT (no install ever), because a committed bun.lock cannot exist without a resolve step (bun install or bun install --lockfile-only, both explicit; bun run writes no lockfile, verified on bun 1.3.14). So the bun.lock path (#705) is a separate OPT-IN reproducibility tier, NOT a more-fault-proof replacement for the default. Resilience for the default means hardening the rewrite (this issue), not swapping it out. Empirically (verified this session) inline specifiers resolve where bare imports do not; bun's source comment says auto-install "always installs the latest version regardless of package.json". The capability probe is what lets webjs notice if and when that changes.

Implementation notes (for the implementing agent)

Acceptance criteria

  • bun-pin-preload.js rewrite is fail-open (a throw serves raw source; a counterfactual proves it).
  • A capability probe exists and self-disables the shim when bun honors package.json / bun.lock natively (fail-safe to ON when unsure).
  • WEBJS_BUN_PIN=0 disables the whole pin mechanism (env + config), documented.
  • CI runs the bun-pin assertions against bun latest + canary.
  • The shim's bun coupling is confirmed centralized.

Relates to #685, #698, #699, #700, #703, #704, #709, #714, #705.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions