Skip to content

examples/: hello-world + kitchen-sink — runnable, self-checking embedder examples - #55

Merged
lann merged 1 commit into
mainfrom
embedder-examples
Aug 10, 2026
Merged

examples/: hello-world + kitchen-sink — runnable, self-checking embedder examples#55
lann merged 1 commit into
mainfrom
embedder-examples

Conversation

@lann

@lann lann commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

The examples commit from #53's branch — it raced #53's auto-merge (pushed after the merge fired) and never reached main; cherry-picked clean onto current main. Full description in the commit message; short version:

Two self-contained WIT + Rust-guest + TS-host examples, the operator-directed first documentation priority:

  • hello-world/: smallest complete embedding — translate → instantiate → call, no imports.
  • kitchen-sink/: representative embedder-API tour — imports record shape; sync / fallible (WitError) / suspending imports in both marker forms (incl. @suspending on a host-resource method — A2 exercised end-to-end); resources both directions (Symbol.dispose on guest drop; TS using); the non-obvious value spellings (variant/enum/record/flags; outermost option vs return-place result vs nested option/result as data, with the option-boxing rule pinned at all three option<option<u32>> depths).

Both hosts self-check and fail loudly on drift. just examples runs both; the step joins the CI core matrix (this PR's own run demonstrates it). examples/ joins the Deno workspace so hosts import @deltic/runtime/... as a published consumer would (#16). READMEs: examples index reframed two-audience; root README routes embedders in.

Gates: examples + full core set green locally (conformance 1254/0, 0 unexpected/stale; sched-seeds; shells); deno.lock untouched; ports lockfiles clean.

…er examples

Consumer-facing examples as the first documentation priority (per operator
direction): each is a self-contained WIT + Rust-guest (wit-bindgen) +
TS-host triple that can be copied out of the repo and built as-is, with a
run.sh reproducing the fixture pipeline (cargo -> wasm-tools component new
-> validate -> deno run). The hosts import @deltic/runtime through the
Deno workspace (examples/ becomes a member) — the same specifiers a
published consumer will use (#16).

hello-world: one export, no imports — translate -> instantiate -> call,
with the Promise-shaped-exports and strings-just-work observations.

kitchen-sink: a representative embedder-API tour, each section numbered
and cross-referenced from its README table:
  - imports record: interface keys, camelCase members, resource class
    position;
  - sync import (enum param as string), fallible import (WitError throw
    -> err; unbranded throw = trap), suspending import in BOTH forms
    (suspending() call form on read-sensor, @suspending decorator on
    channel.send — an A2 host-resource method);
  - host-implemented resource: constructor, methods, static,
    Symbol.dispose on guest drop (asserted via open-count);
  - guest-implemented resource driven with TS `using`;
  - value spellings: variant {tag,val}, enum string, record object,
    flags booleans (absent = false), OUTERMOST option as undefined,
    return-place result as resolve/throw-WitError, nested option/result
    as data — including the option-boxing rule pinned at all three
    depths of option<option<u32>> (maybe-maybe), and the per-chain
    subtlety that an option inside a list is still the outermost of its
    own chain (survey).
  The guest's run-batch drives every import from Rust that is oblivious
  to which of them park its frame — the point of the suspending marker,
  stated in the guest doc comment.

Both hosts are self-checking (assertEq with an undefined-preserving
replacer — JSON would collapse the meaningful outermost-option undefined
into null) and fail loudly on API drift.

Wiring: the `examples` just recipe (deps: shim) runs both; added to the
gates list and to the CI core matrix (.github/justfile) so the examples
are required checks and cannot rot. examples/README.md reframed for its
two audiences (embedder examples first, fixture corpus second); root
README gains layout + documentation rows routing embedders here.

Gates: examples green; test-runtime / wasi-shims / ct-runner / bundle,
conformance (1254/0, 0 unexpected, 0 stale), sched-seeds, shells all
green after the deno.json workspace addition; deno.lock untouched.
@lann
lann enabled auto-merge August 10, 2026 21:25
@lann
lann merged commit acbcba6 into main Aug 10, 2026
4 checks passed
lann added a commit that referenced this pull request Aug 10, 2026
…rips

Investigation first (per #54's brief): a stream traversing host -> guest ->
host with the guest never reading transfers the SharedStreamImpl by identity
— the payload never touches guest memory, so the #54 copy cost doesn't apply
to pure pass-through. But the conventions layer broke the scenario outright:
toHost re-wraps every lifted stream via hostStreamFor, whose double-wrap
assert rejected any host-touched stream (both result and import position),
and after a pass-through both host ends presented one shared HOST_INSTANCE
sentinel, so non-numeric elements tripped the same-instance trap.

Four changes, one PR (operator call):

* cabi/store.ts (#54 proper): storeListIntoValidRange gets the store-side
  mirror of the lift's u8 fast path — Uint8Array sources memcpy via
  bytesOf().set(); plain arrays keep storeInt's exact per-element semantics
  ("int store" assert + mod-256 mask) in a tight loop. 16 KiB stores:
  12.6 -> 0.20 ns/B typed (~5 GB/s, now matching the lift direction),
  13.4 -> 2.9 ns/B plain.

* exec/host_streams.ts (amendment A5): hostStreamFor/hostFutureFor return a
  cached wrapper (WeakMap by shared object) instead of asserting — wrapping
  is idempotent, pass-through round trips and multi-hop chains work, and the
  hazard the assert guarded (two HostActivities pumping one object) is gone
  by construction. bindOnLower's assert stays as an internal invariant.

* exec/host_streams.ts: per-end rendezvous identities (hostEndInstance)
  replace the single HOST_INSTANCE sentinel; host<->host rendezvous is legal
  for every element type. The same-instance restriction (definitions.py
  none_or_number_type, a guard on interleaved lifts in one instance's linear
  memory) keeps firing for real component instances only.

* u8 chunks stay Uint8Array end-to-end (task/streams.ts PayloadChunk,
  HostBuffer chunk accumulation + taken(), embedder packChunk/#chunk,
  StreamWriter takes Chunk<T>): a host-side u8 read costs exactly the one
  rendezvous copy (32 -> 712 MB/s on the 64 KiB pass-through probe); writes
  borrow the caller's chunk until the promise settles (documented), and
  writeAll re-offers subarray views rather than slice copies (review F1).

New fixture examples/guests/stream-pass (pass-through/forward/
pass-through-text — a guest that hands streams on without reading) pins the
scenario end-to-end; store_list_test pins store-path equivalence incl. the
pre-existing mod-256 deviation from definitions.py store_int (memory.ts's,
not this path's).

Contract: embedder-api.md amendment A5 (idempotent wrapping, round-trip
transferability, host<->host rendezvous, Chunk<T> writes + borrow rule).
Docs: architecture.md §7 records the bulk-both-directions policy.

Gates: build, test-rust, test-runtime (356), test-wasi-shims, test-ct-runner,
test-bundle, examples, conformance (1254/0, async 216/0, no stale xfails),
sched-seeds (1, 4242), test-ports, test-webrtc, shells (sm-pinned; jsc-pinned
arch-skipped), browsers (chromium, firefox), websocket-conformance (55/0),
smoke-tls. smoke-c0 legs 2+4 pass; legs 1/3 and iroh-exam scenario 3 fail
identically on the base commit in this environment (missing experiment-mosh
checkout; webrtc backend race) — pre-existing, not this change. Reviewed by
reviewer subagent against definitions.py + A5; F1 fixed, F2-F4 addressed.

Consumer lockfiles (exams/iroh-endpoint, ports/webrtc, ports/websocket):
mechanical catch-up adding the @deltic/examples workspace link from #55.

Closes #54.
@lannbot
lannbot deleted the embedder-examples branch August 23, 2026 16:51
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