feat(runtime): add portable runtime support
req: push/009 req: push/010 req: push/011
This commit is contained in:
@@ -1,21 +1,31 @@
|
||||
# Active frontier — peak idiomatic examples
|
||||
# PLAN — Cloudflare Durable Objects proof
|
||||
|
||||
**Parent ID:** `examples-idiom/001`
|
||||
Parent outcome: prove that hemx keeps its semantic `.heml` authoring, generated typed resources, compile-time target checking, canonical `EffectBatch` wire format, and tiny runtime while a Cloudflare Durable Object owns one durable collaborative room and hibernating WebSocket fan-out.
|
||||
|
||||
- **User value:** A newcomer can move from first app to production reference and advanced integration while seeing one coherent hemx model: plain `.heml`, generated resources, typed Rust handlers/effects, real links/forms/URLs, server-owned truth, native fallback, and explicit leaf adapters.
|
||||
- **State:** Ready — the canonical starter and Workout exemplar are already strong; bounded teaching leaks remain in the HTML gallery, client-local example, SaaS reference, and advanced Kanban boundary.
|
||||
- **Non-goals:** no new framework primitives, client router, VDOM, selector authoring API, reactive expression language, global client store, CSS framework/design system, generalized asset pipeline, visual redesign, or feature expansion. Do not churn `examples/workout` or `examples/techdemo` without a concrete failing contract.
|
||||
- **Build:**
|
||||
- **`examples-idiom/002` — Done: the SaaS reference tells the production truth.** Settings is a real `/settings` link enhanced by the existing page-swap runtime, direct `/settings` renders the fallback page, and `/events` now sends an initial canonical batch followed by ongoing server-owned status updates; `?once` remains only as a bounded production-reference probe. The removed handler no longer simulates navigation with response effects. Raw selector/form construction remains confined to test adapters because generated handles are the asserted boundary, not an app authoring API. req: canonical_authoring/001 req: nav/001 req: nav/002 req: nav/004 req: push/003 req: examples/003 req: examples/004 req: examples/014
|
||||
- **`examples-idiom/003` — Make the HTML pattern gallery mechanically copyable.** In `examples/html_examples/src/main.rs`, generated `gallery` resources, and nearby templates/tests, express the dependent-select flow through typed generated values/partials instead of duplicated string-to-option mapping, and remove request fields discarded only to satisfy example plumbing where the generated handler contract permits it. Preserve each visible htmx slug, native form semantics, server-owned state, inserted-content behavior, and no-reload smoke. req: htmx_equivalents/001 req: htmx_equivalents/003 req: htmx_equivalents/005 req: canonical_authoring/001 req: form/004
|
||||
- **`examples-idiom/004` — Make client-local code visibly a leaf adapter.** In `examples/client_local/src/lib.rs` and its `.heml` surface, project the generated client event/state into a tiny typed counter-domain input/output instead of rendering raw event kind and encoded state as the example's product value. Keep the ordinary `#[hemx::handler(client)]` shape, generated event/state boundary, native event semantics, and no durable client state graph. req: client_local/001 req: client_local/003 req: client_local/004 req: canonical_authoring/017
|
||||
- **`examples-idiom/005` — Separate the advanced Kanban adapter from ordinary hemx app code.** Move the cohesive sync/presence/session/storage transport responsibility from `examples/kanban/src/main.rs` behind one clearly named local integration module with a small route/state contract; keep board templates and ordinary handlers nearby and unchanged where possible. Update `examples/kanban/README.md` to label the fixture as the advanced local/offline/sync north-star, route beginners to the starter/Workout/gallery first, and name legacy `/sync-demo`/`sync.js` as a compatibility probe rather than recommended authoring. Preserve replay, export, deletion, reconnection, auth, and multiplayer browser proof. req: state/001 req: state/002 req: local/001 req: local/002 req: milestone/001 req: sync/001 req: sync/008
|
||||
- **`examples-idiom/006` — Publish and enforce the example ladder.** In `README.md`, example READMEs, and the nearest existing xtask/example checks, identify `app new`/`examples/v0` as the first canonical app, `html_examples` as the pattern gallery, Workout as the product exemplar, SaaS as the production integration reference, `client_local` as the narrow leaf-adapter proof, Techdemo as exhaustive verification, and Kanban as advanced north-star integration. Add the smallest repository-owned guard that fails when beginner/reference authoring regresses to raw IDs, selectors, raw wire/effect constructors, `$OUT_DIR` includes, or app-authored DOM mutation; keep legitimate advanced/test adapters scoped rather than banning tokens globally. req: examples/001 req: examples/003 req: examples/004 req: examples/005 req: examples/007 req: examples/008 req: examples/009 req: canonical_authoring/001 req: progressive_disclosure/001 req: progressive_disclosure/002 req: progressive_disclosure/003
|
||||
- **Blocked by:** none. The separate v1 legal release gate remains blocked on the owner license decision but does not block example work.
|
||||
- **Proof:** each slice must make its user path observable, preserve the named native/recovery path, and pass its focused package/browser proof. Parent closure requires `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo run -p hemx-xtask -- workout test`, focused SaaS/client-local/Kanban package tests, `cargo run -p hemx-xtask -- test`, `cargo check --workspace`, `cargo fmt --check`, `redgate list`, `redgate refs`, and a clean diff. Frustration signals to reject: a beginner must author selectors/raw IDs/wire ops; navigation loses URL/history; JavaScript becomes durable truth; an example claims live/recovery behavior with a one-shot stub; or advanced sync machinery appears to be the default app model.
|
||||
Non-goals: a generic Cloudflare framework, Cloudflare-owned auth policy, RPC/alarm/queue abstractions, global object discovery, offline reconciliation, deployment automation, or treating stored HTML/effects as business truth.
|
||||
|
||||
## Blocked release decision retained
|
||||
## Slice CF-1 — Canonical WebSocket push
|
||||
|
||||
- [ ] **State:** Needs decision — choose the repository distribution license and approved third-party SPDX set, then add root license file(s), workspace package metadata, `deny.toml`, and rerun the release matrix.
|
||||
- **Blocked by:** owner/legal authority. Current third-party set includes Apache-2.0, Apache-2.0 WITH LLVM-exception, BSD-3-Clause, BSL-1.0, MIT, Unicode-3.0, and Unlicense; all 20 workspace packages currently lack license metadata.
|
||||
- **Proof:** `cargo deny check advisories sources licenses` and the full `docs/v1-readiness.md` matrix pass, then readiness changes from NO-GO to GO without publication or deployment.
|
||||
Outcome: a hemx root can receive binary `EffectBatch` updates over a same-origin WebSocket with the same ABI/fingerprint and root-scoped failure behavior as HTTP/SSE.
|
||||
Delta: push/001, push/003, push/005, push/009; runtime/001; failure/001.
|
||||
Path: `data-hemx-ws` on generated root -> runtime WebSocket -> binary frame -> existing `applyBatch` -> generated target or root error outlet.
|
||||
Build: validate the root declaration in hemx-build; add runtime bind/cleanup/error behavior and focused consumer-boundary tests.
|
||||
Risk: accepting text, cross-origin, malformed, or stale-build messages could bypass the canonical compatibility boundary or mutate the wrong root.
|
||||
Proof: `cargo test -p hemx-build -p hemx-js` plus `cargo check --target wasm32-unknown-unknown -p hemx`.
|
||||
Non-goals: client command protocol, reconnect/replay policy beyond the browser WebSocket primitive, multiplexing, or a general transport trait.
|
||||
Residual risk: a real Cloudflare hibernation journey remains for CF-2.
|
||||
State: Ready.
|
||||
Blocked by: none.
|
||||
|
||||
## Slice CF-2 — Durable room exemplar
|
||||
|
||||
Outcome: two browser clients in one named room see a counter mutation rendered from durable Rust state without reload, and reopening the room after object restart/eviction restores the persisted count.
|
||||
Delta: push/001, push/002, push/003, push/009, push/010, push/011; canonical_authoring/001; state/001; abi/004.
|
||||
Path: Worker room URL -> stable Durable Object name -> `.heml` page -> WebSocket upgrade -> typed increment command -> persisted counter -> generated counter partial -> canonical `EffectBatch` -> hibernating sockets -> both roots update.
|
||||
Build: add one `examples/cloudflare_do` worker-rs exemplar with build-time hemx generation, one semantic template, one Durable Object class, Wrangler migration/binding, and focused pure tests for command/state/render output.
|
||||
Risk: target-toolchain incompatibility, using an in-memory socket registry, persisting UI output instead of state, or emitting noncanonical WebSocket bytes would invalidate the proof.
|
||||
Proof: native tests for command/render behavior; `cargo check --target wasm32-unknown-unknown -p hemx-cloudflare-do-example`; then `wrangler dev` browser smoke when Wrangler is available.
|
||||
Non-goals: production auth/CSRF/tenancy, alarms, queues, RPC wrappers, multi-object transactions, deployment, billing, or a public `hemx-cloudflare` crate.
|
||||
Residual risk: hosted Cloudflare deployment, jurisdiction policy, and production credentials remain external.
|
||||
State: Ready for local build; hosted runtime proof is Blocked.
|
||||
Blocked by: Wrangler runtime availability and Cloudflare account credentials for hosted verification.
|
||||
|
||||
Reference in New Issue
Block a user