Skip to content

Keep the SDK root out of the frontend bundle - #3

Merged
trotterdylan merged 1 commit into
mainfrom
fix-app-bundle-sdk-import
Sep 26, 2026
Merged

trotterdylan merged 1 commit into
mainfrom
fix-app-bundle-sdk-import

Conversation

@trotterdylan

Copy link
Copy Markdown
Contributor

The plugin reached the server image but its frontend bundle would not build, so the install failed and it never appeared in the plugin list:

frontend bundle build for "thread-briefs" failed:
  contract.ts:1:34: ERROR: Could not resolve "@get-bb/plugin-sdk"

app.tsx imported two constants (BRIEFS_CHANGED_CHANNEL, BRIEF_STAGES) from contract.ts, which imports defineRpcContract from the SDK root. That put the SDK into the app bundle's dependency graph — and the server builds that bundle against a production install where the SDK, a devDependency, has been pruned. Only @get-bb/plugin-sdk/app is shimmed for the frontend; the root never is.

Every local check passed because a dev install has the SDK on disk. That is the whole trap.

Fix

shared.ts holds the runtime values the frontend needs and imports nothing at all. app.tsx takes those from there, and its contract.ts import is now type-only so it erases completely. contract.ts builds its zod enum from shared.ts's list, so the stages keep one definition rather than two that can drift.

Verified the way the image does it — prune dev dependencies, then build:

npm ci --omit=dev --legacy-peer-deps   # SDK absent, as in the image
bb plugin build                        # succeeds

and dist/app.meta.json carries neither the SDK nor zod. (Keeping zod out shrinks the bundle too; it is not shimmed and would otherwise be bundled from node_modules.)

Regression guard

bundle.test.ts encodes the invariant: no runtime import of the SDK root, zod, or contract.ts anywhere in the frontend graph, plus shared.ts importing nothing. I checked it earns its keep by reintroducing the original bug — it fails — and it caught one bug in itself along the way (the word "import" inside a comment anchoring a runaway match).

This is the third independent thing standing between a bb.app plugin and this image, after NODE_PATH and plugin-directory ownership. The first two were image problems; this one was mine.

🤖 Generated with Claude Code

The plugin installed in the server image but its frontend bundle would not
build:

  frontend bundle build failed: contract.ts:1:34:
  ERROR: Could not resolve "@get-bb/plugin-sdk"

app.tsx imported two constants from contract.ts, which imports
defineRpcContract from the SDK root. That put the SDK in the app bundle's
graph, and the server builds that bundle against a production install where the
SDK — a devDependency — has been pruned. Only @get-bb/plugin-sdk/app is shimmed
for the frontend; the root never is. Every local check passed because a dev
install has the SDK on disk.

Move the two runtime values the frontend needs into shared.ts, which imports
nothing, and leave app.tsx's contract.ts import type-only so it erases. The zod
enum is now built from shared.ts's list, so the stages keep one definition.
Verified by pruning dev dependencies and building: dist/app.meta.json carries
neither the SDK nor zod.

bundle.test.ts encodes the rule — no runtime import of the SDK root, zod, or
contract.ts from the frontend graph — and fails when the original bug is put
back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@trotterdylan
trotterdylan merged commit 20bd23a into main Sep 26, 2026
4 of 6 checks passed
@trotterdylan
trotterdylan deleted the fix-app-bundle-sdk-import branch September 26, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant