Skip to content

Commit ecba65a

Browse files
committed
chore(rivetkit): wasm support
1 parent e5c68c2 commit ecba65a

202 files changed

Lines changed: 22613 additions & 2409 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agent/specs/rivetkit-core-wasm-support.md

Lines changed: 661 additions & 0 deletions
Large diffs are not rendered by default.

.agent/specs/rivetkit-runtime-boundary-cleanup.md

Lines changed: 392 additions & 0 deletions
Large diffs are not rendered by default.

.agent/supabase-functions-kitchen-sink-serverless-e2e.ts

Lines changed: 456 additions & 0 deletions
Large diffs are not rendered by default.

.agent/workerd-kitchen-sink-serverless-e2e.ts

Lines changed: 420 additions & 0 deletions
Large diffs are not rendered by default.

.github/workflows/publish.yaml

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
fi
8080
8181
# ---------------------------------------------------------------------------
82-
# build — matrix of 10 native/engine artifacts (11 on release for Windows)
82+
# build — matrix of native/engine artifacts
8383
# ---------------------------------------------------------------------------
8484
build:
8585
needs: [context]
@@ -232,6 +232,45 @@ jobs:
232232
path: artifacts/${{ matrix.artifact }}
233233
if-no-files-found: error
234234

235+
# ---------------------------------------------------------------------------
236+
# build-wasm — wasm package artifact built in parallel with native artifacts
237+
# ---------------------------------------------------------------------------
238+
build-wasm:
239+
needs: [context]
240+
name: "Build rivetkit-wasm"
241+
if: needs.context.outputs.is_fork != 'true'
242+
runs-on: depot-ubuntu-24.04-8
243+
permissions:
244+
contents: read
245+
steps:
246+
- uses: actions/checkout@v4
247+
with:
248+
lfs: ${{ needs.context.outputs.trigger == 'release' }}
249+
- run: corepack enable
250+
- uses: actions/setup-node@v4
251+
with:
252+
node-version: "22"
253+
cache: pnpm
254+
- uses: actions-rust-lang/setup-rust-toolchain@v1
255+
with:
256+
toolchain: stable
257+
target: wasm32-unknown-unknown
258+
rustflags: ""
259+
- uses: Swatinem/rust-cache@v2
260+
with:
261+
shared-key: "rivetkit-wasm-publish"
262+
cache-on-failure: true
263+
- name: Install wasm package dependencies
264+
run: pnpm install --frozen-lockfile --filter=@rivetkit/rivetkit-wasm
265+
- name: Build wasm package
266+
run: pnpm --filter=@rivetkit/rivetkit-wasm build
267+
- name: Upload wasm package artifact
268+
uses: actions/upload-artifact@v4
269+
with:
270+
name: wasm-package
271+
path: rivetkit-typescript/packages/rivetkit-wasm/pkg
272+
if-no-files-found: error
273+
235274
# ---------------------------------------------------------------------------
236275
# docker-images — per-arch runtime images pushed to Docker Hub
237276
# ---------------------------------------------------------------------------
@@ -298,12 +337,13 @@ jobs:
298337
# publish — npm publish + R2 upload + Docker manifest + release tail
299338
# ---------------------------------------------------------------------------
300339
publish:
301-
needs: [context, build, docker-images]
340+
needs: [context, build, build-wasm, docker-images]
302341
name: "Publish"
303342
if: |
304343
!cancelled() &&
305344
needs.context.outputs.is_fork != 'true' &&
306345
needs.build.result == 'success' &&
346+
needs.build-wasm.result == 'success' &&
307347
needs.docker-images.result == 'success'
308348
runs-on: depot-ubuntu-24.04-8
309349
permissions:
@@ -343,6 +383,16 @@ jobs:
343383
path: engine-artifacts
344384
pattern: engine-*
345385
merge-multiple: true
386+
- name: Download wasm package artifact
387+
uses: actions/download-artifact@v4
388+
with:
389+
name: wasm-package
390+
path: rivetkit-typescript/packages/rivetkit-wasm/pkg
391+
- name: Validate wasm package artifact
392+
run: |
393+
test -f rivetkit-typescript/packages/rivetkit-wasm/pkg/rivetkit_wasm.js
394+
test -f rivetkit-typescript/packages/rivetkit-wasm/pkg/rivetkit_wasm.d.ts
395+
test -f rivetkit-typescript/packages/rivetkit-wasm/pkg/rivetkit_wasm_bg.wasm
346396
347397
- name: Place native binaries in platform packages
348398
run: |
@@ -397,7 +447,9 @@ jobs:
397447
398448
# ---- build TypeScript packages (turbo dep graph picks up native) ----
399449
- name: Build TypeScript packages
400-
run: pnpm build -F rivetkit -F '@rivetkit/*' -F '!@rivetkit/shared-data' -F '!@rivetkit/engine-frontend' -F '!@rivetkit/mcp-hub' -F '!@rivetkit/rivetkit-napi'
450+
env:
451+
SKIP_WASM_BUILD: "1"
452+
run: pnpm build -F rivetkit -F '@rivetkit/*' -F '!@rivetkit/shared-data' -F '!@rivetkit/engine-frontend' -F '!@rivetkit/mcp-hub' -F '!@rivetkit/rivetkit-napi' -F '!@rivetkit/rivetkit-wasm'
401453

402454
# ---- shared publish (runs for all triggers) ----
403455
- name: Finalize package versions for publish

CLAUDE.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Design constraints, invariants, and reference commands for the Rivet monorepo. F
2222

2323
**Always use versioned BARE (`vbare`) instead of raw `serde_bare` for any persisted or wire-format encoding unless explicitly told otherwise.** Raw `serde_bare::to_vec` / `from_slice` has no version header, so any future schema change forces hand-rolled `LegacyXxx` fallback structs. `vbare::OwnedVersionedData` plus a versioned `*.bare` schema is the standard pattern. Acceptable raw-bare exceptions: ephemeral in-memory encodings that never cross a process boundary or hit disk, and wire formats whose protocol version is coordinated out-of-band (e.g. an HTTP path like `/v{PROTOCOL_VERSION}/...` or another channel that pins both peers to one schema per call).
2424

25+
- Avoid raw `f64` fields in vbare protocol schemas that use hashable maps; generated Rust derives `Eq`/`Hash`, so encode floats as fixed bytes or an ordered wrapper.
26+
2527
When talking about "Rivet Actors" make sure to capitalize "Rivet Actor" as a proper noun and lowercase "actor" as a generic noun.
2628

2729
## Commands
@@ -90,6 +92,13 @@ docker-compose up -d
9092
## Dependency Management
9193

9294
- Prefer the Tokio-shaped APIs from `antiox` (`antiox/sync/mpsc`, `antiox/task`, etc.) over ad hoc Promise queues, custom channel wrappers, or event-emitter coordination.
95+
- `rivet-envoy-client` transport features are mutually exclusive; native builds use the default `native-transport`, while wasm builds must set `default-features = false` and enable `wasm-transport`.
96+
- `rivet-envoy-client` wasm WebSocket code lives behind `target_arch = "wasm32"` with a native-host `wasm-transport` stub so feature checks do not compile browser APIs on developer machines.
97+
- `rivetkit-core` wasm builds use `--no-default-features --features wasm-runtime,sqlite-remote`; keep native process and runner-config HTTP code behind `native-runtime`.
98+
- Core-owned lifecycle tasks in `rivetkit-core` should spawn through `RuntimeSpawner` so native builds use Send-capable tasks and wasm builds use local tasks.
99+
- `rivet-envoy-client::async_counter::AsyncCounter` is the shared HTTP request counter type consumed by core sleep logic; do not pull `rivet-util` into core for that counter.
100+
- For `wasm32-unknown-unknown` Rust checks, use target-specific minimal Tokio plus `getrandom/js` and `uuid/js`; scan production dependencies with `cargo tree -e normal` so dev-dependencies do not create false native-dependency hits.
101+
- Use `scripts/cargo/check-rivetkit-core-wasm.sh` as the canonical wasm gate for `rivetkit-core`; it checks the wasm build, scans native dependency leaks, and verifies native transport/runtime features fail on wasm.
93102
- The high-level `rivetkit` crate stays a thin typed wrapper over `rivetkit-core` and re-exports shared transport/config types instead of redefining them.
94103
- When `rivetkit` needs ergonomic helpers on a `rivetkit-core` type it re-exports, prefer an extension trait plus `prelude` re-export instead of wrapping and replacing the core type.
95104
- `engine/sdks/*/api-*` are auto-generated SDK outputs; update the source API schema and regenerate them instead of editing them by hand.
@@ -98,6 +107,7 @@ docker-compose up -d
98107

99108
- Core tests that touch the `_RIVET_TEST_INSPECTOR_TOKEN` env override must share a process-wide lock with startup tests that assert inspector-token initialization side effects; otherwise parallel `cargo test` runs can flip `init_inspector_token(...)` between the env-override no-op path and the KV-backed path.
100109
- For the fast static/http/bare driver verifier, pass only the files listed under `## Fast Tests` in `.agent/notes/driver-test-progress.md`; `tests/driver/*.test.ts` also pulls in slow-suite files and gives bogus gate failures.
110+
- Wasm host smoke tests can drive `buildNativeFactory` through `WasmCoreRuntime` fake bindings to cover actor callbacks, KV, state serialization, remote SQLite routing, and NAPI import boundaries without checked-in wasm-pack output.
101111
- When moving Rust inline tests out of `src/`, keep a tiny source-owned `#[cfg(test)] #[path = "..."] mod tests;` shim so the moved file still has private module access without widening runtime visibility. Prefer a dedicated moved-test file per source module; reusing stale shared `tests/modules/*.rs` files can silently rot against private APIs and explode once you wire them back in.
102112
- Tracing assertions on spawned Rust futures should bind an explicit `tracing::Dispatch` with `.with_subscriber(...)` on the spawned future; thread-local `set_default(...)` can miss the real logs in full async suite runs.
103113

@@ -108,6 +118,9 @@ docker-compose up -d
108118
- Native SQLite VFS recent-page preload hints are actor-side Rust state surfaced by `NativeDatabase::snapshot_preload_hints()`; persist and consume them through runtime/envoy wiring, not JS APIs.
109119
- SQLite VFS file handles must enforce their reader or writer role; reader-owned handles fail closed on mutating callbacks.
110120
- Native SQLite single-statement work should route through the native execute path; keep `exec` as the multi-statement compatibility path.
121+
- Pegboard-envoy remote SQL execution should use `rivetkit-sqlite::database::open_database_from_engine` instead of direct `rusqlite` calls so native routing policy stays shared.
122+
- Pegboard-envoy remote SQL executor caches should use `Arc<OnceCell<NativeDatabaseHandle>>` values so first-use initialization stays lazy and single-flight per `(actor_id, sqlite_generation)`.
123+
- Sent remote SQL requests must fail with `sqlite.remote_indeterminate_result` on WebSocket disconnect; only unsent remote SQL may be sent after reconnect.
111124
- Native SQLite manual transactions keep an idle writer open until autocommit returns; route subsequent work through the writer instead of reader classification.
112125
- Native SQLite read mode may hold multiple read-only connections, while write mode must hold exactly one writable connection and no readers; TypeScript must not be the routing policy boundary.
113126
- For NAPI bridge wiring (TSF callback layout, cancellation tokens, `#[napi(object)]` rules), see `docs-internal/engine/napi-bridge.md`.
@@ -139,6 +152,7 @@ When the user asks to track something in a note, store it in `.agent/notes/` by
139152
- rivetkit-napi must be pure bindings. If code would be duplicated by a future V8 runtime, it belongs in rivetkit-core instead.
140153
- rivetkit-napi serves through `CoreRegistry` + `NapiActorFactory`; do not reintroduce the deleted `BridgeCallbacks` JSON-envelope envoy path or `startEnvoy*Js` exports.
141154
- NAPI `ActorContext.sql()` returns `JsNativeDatabase` directly; do not reintroduce a standalone `SqliteDb` wrapper export.
155+
- TypeScript runtime adapters expose `CoreRuntime` from `rivetkit/src/registry/runtime.ts`; keep raw `@rivetkit/rivetkit-napi` and future `@rivetkit/rivetkit-wasm` imports inside `src/registry/*-runtime.ts`.
142156
- rivetkit (Rust) is a thin typed wrapper. If it does more than deserialize, delegate to core, and serialize, the logic should move to rivetkit-core.
143157
- rivetkit (TypeScript) owns only: workflow engine, agent-os, client library, Zod schema validation for user-defined types, and actor definition types.
144158
- Errors use universal `RivetError` (group/code/message/metadata) at all boundaries. No custom error classes in TS.
@@ -277,6 +291,7 @@ When the user asks to track something in a note, store it in `.agent/notes/` by
277291
- Only add design constraints, invariants, and non-obvious rules that shape how new code should be written. Do not add general trivia, current implementation wiring, KV-key layouts, module organization, API signatures, ephemeral migration state, or anything a reader can learn by reading the code. That content belongs in module doc-comments, `docs-internal/`, or `.claude/reference/`.
278292
- When the user asks to update any `CLAUDE.md`, add one-line bullet points only, or add a new section containing one-line bullet points.
279293
- Architectural internals and runtime wiring belong in `docs-internal/engine/`. Agent-procedural guides (test-harness gotchas, build troubleshooting, docs-sync tables) belong in `.claude/reference/`. Link them from the [Reference Docs](#reference-docs) index below instead of inlining.
294+
- Every directory that has a `CLAUDE.md` must also have an `AGENTS.md` symlink pointing to it (`ln -s CLAUDE.md AGENTS.md` from the same directory). When creating a new `CLAUDE.md`, create the symlink in the same change.
280295

281296
## Reference Docs
282297

Cargo.lock

Lines changed: 58 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ members = [
6363
"rivetkit-rust/packages/actor-persist",
6464
"rivetkit-rust/packages/rivetkit-core",
6565
"rivetkit-rust/packages/shared-types",
66-
"rivetkit-typescript/packages/rivetkit-napi"
66+
"rivetkit-rust/packages/rivetkit-sqlite",
67+
"rivetkit-rust/packages/rivetkit-sqlite-types",
68+
"rivetkit-typescript/packages/rivetkit-napi",
69+
"rivetkit-typescript/packages/rivetkit-wasm"
6770
]
6871

6972
[workspace.package]
@@ -122,7 +125,6 @@ members = [
122125
regex = "1.4"
123126
replace_with = "0.1.8"
124127
rstest = "0.26.1"
125-
rusqlite = { version = "0.32.1", features = [ "bundled" ] }
126128
rustls-pemfile = "2.2.0"
127129
rustyline = "15.0.0"
128130
scc = "3.6.12"
@@ -539,6 +541,7 @@ members = [
539541

540542
[workspace.dependencies.rivet-envoy-client]
541543
path = "engine/sdks/rust/envoy-client"
544+
default-features = false
542545

543546
[workspace.dependencies.rivet-envoy-protocol]
544547
path = "engine/sdks/rust/envoy-protocol"
@@ -558,6 +561,9 @@ members = [
558561
[workspace.dependencies.rivetkit-sqlite]
559562
path = "rivetkit-rust/packages/rivetkit-sqlite"
560563

564+
[workspace.dependencies.rivetkit-sqlite-types]
565+
path = "rivetkit-rust/packages/rivetkit-sqlite-types"
566+
561567
[workspace.dependencies.rivetkit-core]
562568
path = "rivetkit-rust/packages/rivetkit-core"
563569

docker/build/darwin-arm64.Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ COPY . .
3535
RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \
3636
export NODE_OPTIONS="--max-old-space-size=8192" && \
3737
export SKIP_NAPI_BUILD=1 && \
38+
export SKIP_WASM_BUILD=1 && \
3839
pnpm install --ignore-scripts && \
3940
VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build -F @rivetkit/engine-frontend; \
4041
fi
@@ -62,7 +63,7 @@ RUN --mount=type=cache,id=cargo-registry-darwin-arm64,target=/usr/local/cargo/re
6263
fi && \
6364
mkdir -p /artifacts && \
6465
if [ "$BUILD_TARGET" = "engine" ]; then \
65-
cargo build --bin rivet-engine $CARGO_FLAG --target aarch64-apple-darwin && \
66+
cargo build -p rivet-engine --bin rivet-engine $CARGO_FLAG --target aarch64-apple-darwin && \
6667
cp target/aarch64-apple-darwin/$PROFILE_DIR/rivet-engine /artifacts/rivet-engine-aarch64-apple-darwin; \
6768
elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \
6869
cd rivetkit-typescript/packages/rivetkit-napi && \

docker/build/darwin-x64.Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ COPY . .
3535
RUN if [ "$BUILD_TARGET" = "engine" ] && [ "$BUILD_FRONTEND" = "true" ]; then \
3636
export NODE_OPTIONS="--max-old-space-size=8192" && \
3737
export SKIP_NAPI_BUILD=1 && \
38+
export SKIP_WASM_BUILD=1 && \
3839
pnpm install --ignore-scripts && \
3940
VITE_APP_API_URL="${VITE_APP_API_URL}" VITE_FEATURE_FLAGS="${VITE_FEATURE_FLAGS}" npx turbo build -F @rivetkit/engine-frontend; \
4041
fi
@@ -62,7 +63,7 @@ RUN --mount=type=cache,id=cargo-registry-darwin-x64,target=/usr/local/cargo/regi
6263
fi && \
6364
mkdir -p /artifacts && \
6465
if [ "$BUILD_TARGET" = "engine" ]; then \
65-
cargo build --bin rivet-engine $CARGO_FLAG --target x86_64-apple-darwin && \
66+
cargo build -p rivet-engine --bin rivet-engine $CARGO_FLAG --target x86_64-apple-darwin && \
6667
cp target/x86_64-apple-darwin/$PROFILE_DIR/rivet-engine /artifacts/rivet-engine-x86_64-apple-darwin; \
6768
elif [ "$BUILD_TARGET" = "rivetkit-napi" ]; then \
6869
cd rivetkit-typescript/packages/rivetkit-napi && \

0 commit comments

Comments
 (0)