Skip to content

feat: support Bun as a Caplets process runtime#241

Merged
ian-pascoe merged 2 commits into
mainfrom
feat/bun-process-runtime
Jul 24, 2026
Merged

feat: support Bun as a Caplets process runtime#241
ian-pascoe merged 2 commits into
mainfrom
feat/bun-process-runtime

Conversation

@ian-pascoe

@ian-pascoe ian-pascoe commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • define runtime support by execution boundary: Caplets processes support Node.js 22+ and Bun 1.3.14+, Node remains the default, and pnpm remains the sole dependency manager
  • replace the synchronous SQLite driver with @libsql/client and Drizzle's asynchronous libSQL adapter, migrating storage operations and transaction boundaries
  • add runtime-native Bun HTTP/WebSocket handling and select the Node or Bun Sentry SDK at runtime
  • add Node 22/24 and Bun 1.3.14/latest release-blocking CI matrices, package metadata, runtime smoke coverage, domain glossary entries, and ADR 0009

Verification

  • pnpm verify
  • pnpm --filter @caplets/core test
  • node scripts/check-package-runtime.mjs
  • bun scripts/check-package-runtime.mjs
  • pnpm install --frozen-lockfile

The built-package smoke covers HTTP discovery, WebSockets, MCP lifecycle, authentication, project binding, shutdown, and bounded 256 MiB bundle upload/download behavior under both runtimes.

Summary by CodeRabbit

  • New Features
    • Added Bun 1.3.14+ as a supported alternative runtime (Node.js 22+ remains default).
    • Introduced runtime-native HTTP/WebSocket serving and runtime-aware telemetry.
    • Updated storage to async SQLite behavior for more reliable execution.
  • Bug Fixes
    • Improved async/await sequencing across database operations and transactional flows.
    • Fixed SQLite query/transaction handling to ensure awaited results.
  • Documentation
    • Updated README/SDK descriptions, glossary, and added an ADR for supported runtimes and verification requirements.
  • Tests
    • Expanded release-blocking CI verification for Bun (minimum + latest) and updated test suites for async/libSQL execution.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5df62e77-a672-4df0-a3d7-76c72d796ee3

📥 Commits

Reviewing files that changed from the base of the PR and between 6d0896c and 47d77cc.

📒 Files selected for processing (8)
  • packages/core/src/admin-api/router.ts
  • packages/core/src/serve/http.ts
  • packages/core/src/storage/database.ts
  • packages/core/src/storage/installations.ts
  • packages/core/src/storage/migrations.ts
  • packages/core/src/storage/project-bindings.ts
  • packages/core/src/storage/vault-grants.ts
  • packages/core/test/admin-api-router.test.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/core/src/admin-api/router.ts
  • packages/core/src/storage/project-bindings.ts
  • packages/core/src/storage/database.ts
  • packages/core/src/storage/migrations.ts
  • packages/core/src/serve/http.ts
  • packages/core/src/storage/installations.ts
  • packages/core/src/storage/vault-grants.ts

📝 Walkthrough

Walkthrough

Caplets adds Bun 1.3.14+ support alongside Node.js 22+, introduces runtime-aware HTTP and telemetry behavior, replaces synchronous SQLite access with async libSQL storage, and expands CI, migration, smoke-test, and documentation coverage.

Changes

Cross-runtime support

Layer / File(s) Summary
Runtime contracts and verification
.changeset/*, .github/workflows/*, package.json, packages/*/package.json, README.md, docs/adr/*, mise.toml, scripts/*
Adds Bun engine requirements, Bun CI verification, runtime smoke adjustments, release notes, and Node/Bun support documentation.
Bun HTTP and telemetry
packages/core/src/serve/http.ts, packages/core/src/telemetry/*, packages/core/src/cli.ts
Adds Bun HTTP/WebSocket startup, SSE handling, runtime descriptors, runtime-labeled reliability telemetry, and Bun-native Sentry selection.

Async SQLite storage

Layer / File(s) Summary
libSQL database and migration foundation
packages/core/src/storage/database.ts, migrations.ts, legacy-migration.ts, types.ts
Replaces better-sqlite3 with libSQL, adds serialized client operations, asynchronous migrations, and updated database types.
Async repository transactions and outcomes
packages/core/src/storage/*.ts
Converts SQLite repository reads, writes, transactions, legacy flows, activity logging, and mutation checks to awaited operations using rowsAffected.
SQLite integration and runtime validation
packages/core/test/*, scripts/*
Updates tests for async libSQL clients, lock contention, migration behavior, rollback sequencing, telemetry, and Bun buffering limits.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Poem

A rabbit hops where Bun winds run,
While SQLite waits for async sun.
WebSockets thump and telemetry sings,
LibSQL tidies transaction strings.
Node still leads the launcher trail—
Two runtimes race beneath one veil.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Bun support as a Caplets process runtime.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/bun-process-runtime

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​libsql/​client@​0.17.410010010089100
Added@​sentry/​bun@​10.67.0991009096100

View full report

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds Bun 1.3.14+ as a supported process runtime alongside Node.js 22+, replacing the synchronous better-sqlite3 driver with @libsql/client and a hand-rolled SqliteOperationQueue to serialize async SQLite access under both runtimes. It also adds Bun-native HTTP/WebSocket handling, runtime-selected Sentry SDK initialization, a Bun-compatible bundle upload stream adapter, and an expanded CI matrix covering Node 22/24 and Bun 1.3.14/latest.

  • Storage layer: better-sqlite3 removed; all SQLite operations are now async via @libsql/client wrapped by SqliteOperationQueue (a Promise-chaining mutex); ephemeral (:memory:) databases use a temp dir that is cleaned up on close.
  • HTTP/WebSocket: startHttpRuntimeServer selects between @hono/node-server (Node) and Bun.serve (Bun) at startup; a prependEventStreamPreamble helper works around Bun's SSE buffering for MCP GET streams; requestBodyReadable replaces the Node-only Readable.fromWeb for bundle uploads.
  • Telemetry: runtimeDescriptor() detects the active runtime from process.versions; @sentry/bun is dynamically imported on Bun so each host uses its native Sentry transport.

Confidence Score: 5/5

Safe to merge. The storage migration from better-sqlite3 to @libsql/client is thorough, the SqliteOperationQueue correctly serializes all async DB access, and the Bun HTTP/WebSocket paths are well-isolated from the Node path. The only annotation issues found are type-level and do not affect runtime behavior.

The core storage change is carefully implemented: the operation queue serializes every libsql method, transactions wrap commit/rollback/close to release the lock, and ephemeral databases clean up their map entries. The Bun HTTP server and WebSocket bridge are thin adapters over the same Hono app with no shared state mutations. CI coverage spans minimum and latest versions of both runtimes. The one annotation mismatch in caplet-records.ts mirrors a pattern already flagged elsewhere and carries no behavioral risk.

packages/core/src/storage/caplet-records.ts has two methods with misleading return type annotations (importBundlesInTransaction and getInTransaction); no other files require special attention.

Important Files Changed

Filename Overview
packages/core/src/storage/database.ts Replaces better-sqlite3 with @libsql/client; adds SqliteOperationQueue to serialize concurrent async operations; handles ephemeral (:memory:) databases with temp dirs; queue entries correctly cleaned up for ephemeral paths
packages/core/src/storage/migrations.ts Migrates SQLite migration runner from better-sqlite3 exclusive transaction to libsql async write transaction; logic preserves hash/order integrity checks and schema-version upsert
packages/core/src/storage/caplet-records.ts All SQLite calls migrated to async; importBundlesInTransaction and getInTransaction carry incorrect nested-Promise return type annotations (P2); runtime behavior unaffected
packages/core/src/serve/http.ts Adds Bun-native HTTP/WebSocket server path alongside Node adapter; SSE preamble injection for Bun GET streams; double-stop behavior for graceful drain already noted in previous thread
packages/core/src/telemetry/providers.ts Selects @sentry/bun vs @sentry/node SDK at runtime using runtimeDescriptor(); Bun path uses makeFetchTransport and defaultStackParser from @sentry/bun as expected
packages/core/src/telemetry/runtime-environment.ts New module: runtimeDescriptor() detects bun vs node from process.versions; injectable for testing; straightforward implementation
packages/core/src/admin-api/router.ts Replaces Readable.fromWeb (Node-only) with requestBodyReadable helper using manual ReadableStreamDefaultReader iteration, enabling Bun compatibility for bundle upload streaming
packages/core/src/storage/legacy-migration.ts Removes runSynchronous wrapper; imports/verifications are now properly awaited inside the async SQLite transaction callback; cleaner error propagation
.github/workflows/ci.yml Adds verify-bun job with Bun 1.3.14/latest matrix; runs pnpm verify (Node tests) + bun package-runtime smoke; release-blocking coverage for minimum and latest supported Bun versions

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start([HTTP serve / DB open]) --> RuntimeCheck{process.versions.bun?}

    RuntimeCheck -->|Yes| BunHTTP[Bun.serve with bunWebSocket handler]
    RuntimeCheck -->|No| NodeHTTP["@hono/node-server with ws WebSocketServer"]

    BunHTTP --> BunSSE[prependEventStreamPreamble for MCP GET streams]
    BunHTTP --> BunUpload[requestBodyReadable via ReadableStreamDefaultReader]

    RuntimeCheck -->|Yes| BunSentry["import('@sentry/bun') + makeFetchTransport"]
    RuntimeCheck -->|No| NodeSentry["import('@sentry/node')"]

    RuntimeCheck -->|Yes| LibSQL["@libsql/client createClient(file://...)"]
    RuntimeCheck -->|No| LibSQL

    LibSQL --> OpQueue[SqliteOperationQueue Promise mutex]
    OpQueue --> SerializedClient[serializeSqliteClient Proxy]
    SerializedClient --> DrizzleLibSQL["drizzle-orm/libsql"]
    SerializedClient --> SerializedTx[serializeSqliteTransaction Proxy]
    SerializedTx --> CommitRelease["commit / rollback / close → release()"]
Loading

Reviews (2): Last reviewed commit: "fix: address Bun runtime review findings" | Re-trigger Greptile

Comment thread packages/core/src/storage/installations.ts
Comment thread packages/core/src/storage/database.ts
Comment thread packages/core/src/serve/http.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/storage/project-bindings.ts (1)

321-371: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use { behavior: "immediate" } for these sqlite read-then-write transactions. mutateBindingSqlite selects a binding row before conditionally inserting/updating it, grantPreparedVaultSqlite selects a subject before inserting/updating, and revokeSqlite selects subject/key state before deleting. Add { behavior: "immediate" } to each sqlite db.transaction(...) call to follow the existing sqlite transaction pattern and avoid deferred-transaction lock-upgrade failures under concurrency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/storage/project-bindings.ts` around lines 321 - 371, Use
the SQLite immediate transaction mode for all three read-then-write flows:
update mutateBindingSqlite in packages/core/src/storage/project-bindings.ts
(lines 321-371), grantPreparedVaultSqlite in
packages/core/src/storage/vault-grants.ts (lines 145-159), and revokeSqlite in
packages/core/src/storage/vault-grants.ts (lines 1087-1098) so each
db.transaction call receives { behavior: "immediate" } while preserving their
existing mutation logic.
🧹 Nitpick comments (2)
packages/core/src/storage/database.ts (1)

353-447: 🚀 Performance & Scalability | 🔵 Trivial

Serialization proxy and async mutex look correct. acquire() assigns this.tail before awaiting the predecessor (proper FIFO chaining), and the transaction proxy releases only on commit/rollback/close while passing query methods through, so in-transaction statements don't re-enter the queue.

One operational note: all client execute/batch calls for a given path are serialized through a single queue, so read concurrency is fully serialized process-wide per database file. If read throughput becomes a concern under load, consider a read path that doesn't hold the write mutex.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/storage/database.ts` around lines 353 - 447, Retain the
current SqliteOperationQueue and transaction-proxy behavior; no correctness
change is required. If optimizing throughput, update serializeSqliteClient’s
read-operation path so read-only calls can avoid holding the per-path queue
while preserving serialization for writes and transaction lifecycle methods.
packages/core/src/storage/migrations.ts (1)

106-110: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard rollback() so a failing commit() doesn't mask the original error.

Per the libSQL docs, rollback() cannot be called after commit(). If await transaction.commit() throws, the catch block calls await transaction.rollback(), which will throw a secondary error and mask the real commit failure (and may leave the transaction unclosed). Suppress the rollback error so the original propagates.

♻️ Suggested guard
     await transaction.commit();
   } catch (error) {
-    await transaction.rollback();
+    await transaction.rollback().catch(() => undefined);
     throw error;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/storage/migrations.ts` around lines 106 - 110, Update the
transaction error handling around transaction.commit() so rollback failures are
suppressed while preserving and rethrowing the original commit error. Guard the
await transaction.rollback() call in the catch block, ensuring any rollback
exception cannot mask the original error or prevent transaction cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/admin-api/router.ts`:
- Around line 679-690: Update requestBodyChunks and its requestBodyReadable
integration so destroying the wrapped input also cancels the underlying Web
ReadableStream reader, not merely releases its lock. Track the active reader or
generator cleanup outside the async iterator and ensure destroyInput awaits or
triggers that cleanup, including when destroy completes synchronously during an
in-flight read. Preserve normal chunk iteration and lock release behavior.

In `@packages/core/src/serve/http.ts`:
- Around line 180-185: Update the binary branch in the WebSocket message handler
to pass only the message view’s exact byte range, using its byteOffset and
byteLength to create a bounded payload before calling createWSMessageEvent.
Preserve the existing string handling and event dispatch through
socket.data.events.onMessage.

In `@packages/core/src/storage/database.ts`:
- Around line 331-340: Update the HostStorage close callback in the returned
instance to remove the ephemeral path’s entry from SQLITE_OPERATION_QUEUES after
draining the queue and before cleanup. Restrict eviction to ephemeral
configurations so shared on-disk queues remain reusable, and preserve the
existing drain, client close, and temporary-root removal behavior.

In `@packages/core/src/storage/vault-grants.ts`:
- Around line 789-818: Update assertLegacyGrantsMatchSqlite to process grants
sequentially instead of using Promise.all: iterate through grants with an
awaited loop, perform each legacyGrantSubjectSqlite call and database select
only after the previous grant completes, preserve the existing conflict error
and result shape, and return the collected { existing, values } entries after
all grants are checked.

---

Outside diff comments:
In `@packages/core/src/storage/project-bindings.ts`:
- Around line 321-371: Use the SQLite immediate transaction mode for all three
read-then-write flows: update mutateBindingSqlite in
packages/core/src/storage/project-bindings.ts (lines 321-371),
grantPreparedVaultSqlite in packages/core/src/storage/vault-grants.ts (lines
145-159), and revokeSqlite in packages/core/src/storage/vault-grants.ts (lines
1087-1098) so each db.transaction call receives { behavior: "immediate" } while
preserving their existing mutation logic.

---

Nitpick comments:
In `@packages/core/src/storage/database.ts`:
- Around line 353-447: Retain the current SqliteOperationQueue and
transaction-proxy behavior; no correctness change is required. If optimizing
throughput, update serializeSqliteClient’s read-operation path so read-only
calls can avoid holding the per-path queue while preserving serialization for
writes and transaction lifecycle methods.

In `@packages/core/src/storage/migrations.ts`:
- Around line 106-110: Update the transaction error handling around
transaction.commit() so rollback failures are suppressed while preserving and
rethrowing the original commit error. Guard the await transaction.rollback()
call in the catch block, ensuring any rollback exception cannot mask the
original error or prevent transaction cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f0e3c4c-3ed8-41b9-a1a5-55de447b7cbb

📥 Commits

Reviewing files that changed from the base of the PR and between 6875498 and 6d0896c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (63)
  • .changeset/fuzzy-buns-travel.md
  • .github/workflows/ci.yml
  • CONTEXT.md
  • README.md
  • docs/adr/0009-scope-runtime-support-by-execution-boundary.md
  • mise.toml
  • package.json
  • packages/cli/package.json
  • packages/core/package.json
  • packages/core/rolldown.config.ts
  • packages/core/src/admin-api/router.ts
  • packages/core/src/cli.ts
  • packages/core/src/serve/http.ts
  • packages/core/src/storage/backend-auth-flows.ts
  • packages/core/src/storage/backend-auth.ts
  • packages/core/src/storage/caplet-records.ts
  • packages/core/src/storage/coordination.ts
  • packages/core/src/storage/dashboard-sessions.ts
  • packages/core/src/storage/database.ts
  • packages/core/src/storage/idempotency.ts
  • packages/core/src/storage/installations.ts
  • packages/core/src/storage/legacy-migration.ts
  • packages/core/src/storage/migrations.ts
  • packages/core/src/storage/operator-activity.ts
  • packages/core/src/storage/project-bindings.ts
  • packages/core/src/storage/remote-security.ts
  • packages/core/src/storage/setup-state.ts
  • packages/core/src/storage/types.ts
  • packages/core/src/storage/vault-grants.ts
  • packages/core/src/storage/vault-state.ts
  • packages/core/src/storage/vault-values.ts
  • packages/core/src/telemetry/events.ts
  • packages/core/src/telemetry/index.ts
  • packages/core/src/telemetry/privacy.ts
  • packages/core/src/telemetry/providers.ts
  • packages/core/src/telemetry/runtime-environment.ts
  • packages/core/src/telemetry/runtime.ts
  • packages/core/test/backend-auth-flow-storage.test.ts
  • packages/core/test/backend-auth-state-table.test.ts
  • packages/core/test/backend-auth-storage.test.ts
  • packages/core/test/caplet-bundle-streaming.test.ts
  • packages/core/test/caplet-record-storage.test.ts
  • packages/core/test/current-host-administration.test.ts
  • packages/core/test/dashboard-session-store.test.ts
  • packages/core/test/dashboard-session.test.ts
  • packages/core/test/host-storage-config.test.ts
  • packages/core/test/host-storage.test.ts
  • packages/core/test/idempotency-storage.test.ts
  • packages/core/test/installation-storage.test.ts
  • packages/core/test/project-binding-storage.test.ts
  • packages/core/test/remote-security-storage.test.ts
  • packages/core/test/setup-state-storage.test.ts
  • packages/core/test/storage-records-cli.test.ts
  • packages/core/test/telemetry-events.test.ts
  • packages/core/test/telemetry-providers.test.ts
  • packages/core/test/telemetry-runtime.test.ts
  • packages/core/test/vault-grant-storage.test.ts
  • packages/core/test/vault-state-storage.test.ts
  • packages/core/test/vault-value-storage.test.ts
  • packages/sdk/README.md
  • packages/sdk/package.json
  • scripts/check-package-runtime.mjs
  • scripts/package-runtime-plan-000-smoke.mjs
💤 Files with no reviewable changes (1)
  • packages/sdk/package.json

Comment thread packages/core/src/admin-api/router.ts Outdated
Comment thread packages/core/src/serve/http.ts
Comment thread packages/core/src/storage/database.ts
Comment thread packages/core/src/storage/vault-grants.ts
@ian-pascoe

Copy link
Copy Markdown
Contributor Author

Addressed the outside-diff and review-body findings in 47d77cc:

  • SQLite project-binding mutation, vault grant, and vault revoke read-then-write transactions now use { behavior: "immediate" }.
  • Migration rollback failures are suppressed so they cannot mask the original commit failure.
  • The per-path SQLite operation queue remains intentionally serialized; the throughput note required no correctness change.
  • Bun shutdown retains deliberate graceful-to-forced escalation after application session cleanup, with a guard preventing redundant calls after completion.

Validation: core suite (2,490 tests), core typecheck/lint, and built-package Node and Bun lifecycle/RSS smokes passed.

@ian-pascoe
ian-pascoe merged commit f5c4808 into main Jul 24, 2026
12 checks passed
@ian-pascoe
ian-pascoe deleted the feat/bun-process-runtime branch July 24, 2026 10:22
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