From c427b2695739ad4d0ff7a8577645052a827f6bfa Mon Sep 17 00:00:00 2001 From: Ivan Banov Date: Mon, 12 Oct 2026 11:35:40 +0200 Subject: [PATCH] refactor: one layout rule for crates, mirrored from packages crates/ holds what ships, flat, like packages/: the engine (core, with its proc macros nested in core/macros because the compiler demands a separate crate) and its bridges (wasm, jsi). The React Native bridge is named for the boundary it crosses, not the tool: crates/jsi (dunky-state-machine-jsi), uniffi today. The sandbox machines in Rust move next to the sandboxes, sandbox/rust, like benchmark/rust; the turbo module built from the bridge is sandbox/jsi (@sandbox/jsi). Docs name the role and keep "bridges" (a runtime boundary) apart from "bindings" (the a11y and event vocabulary). CI paths, the workspace manifest, the wasm build and the sandbox imports follow. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci-checks.yml | 14 +++--- .gitignore | 2 +- .oxlintrc.json | 2 +- AGENTS.md | 14 +++--- ARCHITECTURE.md | 49 ++++++++++--------- CONTRIBUTING.md | 2 +- Cargo.toml | 10 ++-- README.md | 4 +- TODO.md | 8 +-- crates/core/SPEC.md | 2 +- crates/{ => core}/macros/Cargo.toml | 0 crates/{ => core}/macros/src/lib.rs | 0 crates/{ => core}/macros/src/ts.rs | 0 crates/{uniffi => jsi}/.gitignore | 0 crates/{uniffi => jsi}/Cargo.lock | 18 +++---- crates/{uniffi => jsi}/Cargo.toml | 6 +-- crates/{uniffi => jsi}/src/lib.rs | 0 pnpm-lock.yaml | 34 ++++++------- sandbox/README.md | 2 +- sandbox/{uniffi => jsi}/.gitignore | 0 sandbox/{uniffi => jsi}/README.md | 6 +-- sandbox/{uniffi => jsi}/package.json | 4 +- sandbox/{uniffi => jsi}/ubrn.config.yaml | 4 +- sandbox/native/package.json | 2 +- sandbox/native/rust-bench.tsx | 2 +- sandbox/opentui/src/index.tsx | 2 +- sandbox/react/src/main.tsx | 2 +- {crates/sandbox => sandbox/rust}/Cargo.toml | 2 +- .../rust}/examples/typescript.rs | 0 .../sandbox => sandbox/rust}/src/dialog.rs | 0 {crates/sandbox => sandbox/rust}/src/lib.rs | 4 +- .../sandbox => sandbox/rust}/src/palette.rs | 0 sandbox/shared/src/rust-node.ts | 2 +- sandbox/shared/src/rust.ts | 4 +- sandbox/solid/src/main.tsx | 2 +- scripts/build-wasm.sh | 6 +-- website/src/content/docs/rust/index.mdx | 14 +++--- 37 files changed, 114 insertions(+), 109 deletions(-) rename crates/{ => core}/macros/Cargo.toml (100%) rename crates/{ => core}/macros/src/lib.rs (100%) rename crates/{ => core}/macros/src/ts.rs (100%) rename crates/{uniffi => jsi}/.gitignore (100%) rename crates/{uniffi => jsi}/Cargo.lock (99%) rename crates/{uniffi => jsi}/Cargo.toml (88%) rename crates/{uniffi => jsi}/src/lib.rs (100%) rename sandbox/{uniffi => jsi}/.gitignore (100%) rename sandbox/{uniffi => jsi}/README.md (69%) rename sandbox/{uniffi => jsi}/package.json (82%) rename sandbox/{uniffi => jsi}/ubrn.config.yaml (50%) rename {crates/sandbox => sandbox/rust}/Cargo.toml (91%) rename {crates/sandbox => sandbox/rust}/examples/typescript.rs (100%) rename {crates/sandbox => sandbox/rust}/src/dialog.rs (100%) rename {crates/sandbox => sandbox/rust}/src/lib.rs (93%) rename {crates/sandbox => sandbox/rust}/src/palette.rs (100%) diff --git a/.github/workflows/ci-checks.yml b/.github/workflows/ci-checks.yml index 30fe9da..bfd71b2 100644 --- a/.github/workflows/ci-checks.yml +++ b/.github/workflows/ci-checks.yml @@ -33,7 +33,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/ci-setup - # The sandbox machines written in Rust (crates/sandbox -> wasm). + # The sandbox machines written in Rust (sandbox/rust -> wasm). - uses: ./.github/actions/wasm-setup - run: pnpm test:ci @@ -43,7 +43,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/ci-setup - # The sandbox machines written in Rust (crates/sandbox -> wasm). + # The sandbox machines written in Rust (sandbox/rust -> wasm). - uses: ./.github/actions/wasm-setup - run: pnpm typecheck @@ -77,17 +77,17 @@ jobs: with: workspaces: | . -> target - crates/uniffi -> target + crates/jsi -> target - run: cargo fmt --all --check - run: cargo lint - run: cargo test --workspace # Again in release: overflow checks and debug assertions are off there. - run: cargo test --workspace --release - # crates/uniffi (React Native over JSI) sits outside the workspace with its own + # crates/jsi (React Native over JSI) sits outside the workspace with its own # lockfile; its checks run on the host and need no mobile toolchain. - - run: cargo fmt --manifest-path crates/uniffi/Cargo.toml --check - - run: cargo clippy --manifest-path crates/uniffi/Cargo.toml --all-targets -- -D warnings - - run: cargo test --manifest-path crates/uniffi/Cargo.toml + - run: cargo fmt --manifest-path crates/jsi/Cargo.toml --check + - run: cargo clippy --manifest-path crates/jsi/Cargo.toml --all-targets -- -D warnings + - run: cargo test --manifest-path crates/jsi/Cargo.toml versioning: # The release PR consumes changesets rather than adding one — never flag it. diff --git a/.gitignore b/.gitignore index 2febef2..474d8fe 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ dist # Rust /target # wasm-bindgen output (built by `pnpm build:wasm`) -crates/sandbox/pkg +sandbox/rust/pkg # Expo prebuild output (`expo prebuild` / `expo run:*` in the RN sandbox) sandbox/native/ios diff --git a/.oxlintrc.json b/.oxlintrc.json index 07f4324..6d723fb 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -45,5 +45,5 @@ } }, "globals": {}, - "ignorePatterns": ["**/dist/**", "website/src/snippets/**", "crates/sandbox/pkg/**"] + "ignorePatterns": ["**/dist/**", "website/src/snippets/**", "sandbox/rust/pkg/**"] } diff --git a/AGENTS.md b/AGENTS.md index 41a5033..f96083e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,13 +25,13 @@ editing files in that scope — it overrides anything here for that scope ## Scopes -| Scope | Path | What it is | -| --------- | ------------- | -------------------------------------------------------------------------------------------------------------------- | -| Packages | `packages/**` | The core machine, substrate targets (react, solid, native, opentui), and shared internals | -| Benchmark | `benchmark/` | Perf suites: `ts/` (the TS engine against competitor libraries) and `rust/` (the Rust engine) | -| Sandbox | `sandbox/` | Per-substrate demo apps for manual verification | -| Website | `website/` | The docs site | -| Crates | `crates/**` | The Rust engine (`core`, `macros`), its JS binding (`wasm`), the sandbox machines in Rust, the RN binding (`uniffi`) | +| Scope | Path | What it is | +| --------- | ------------- | ------------------------------------------------------------------------------------------------------------------ | +| Packages | `packages/**` | The core machine, substrate targets (react, solid, native, opentui), and shared internals | +| Benchmark | `benchmark/` | Perf suites: `ts/` (the TS engine against competitor libraries) and `rust/` (the Rust engine) | +| Sandbox | `sandbox/` | Per-substrate demo apps for manual verification, the shared TS machines (`shared/`) and their Rust twins (`rust/`) | +| Website | `website/` | The docs site | +| Crates | `crates/**` | The Rust engine (`core`, with its proc macros in `core/macros`) and its bridges (`wasm`, `jsi`) | Some changes are cross-scope: a change in `core/` may need follow-up in the targets, sandboxes, and docs — and vice versa. Check what else your diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1787c52..0050548 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -93,19 +93,19 @@ Zag, whose machines read props directly.) ## Project structure -| File / location | What it owns | -| --------------------------- | ----------------------------------------------------------------- | -| `packages/core/` | State-machine engine (plain-mutation kernel) | -| `packages/shared/bindings/` | Substrate-agnostic event + attr vocabulary (onPress, role, …) | -| `packages/shared/utils/` | mergeProps, composeHandlers | -| `packages//` | Hook + normalize per substrate (react, solid, native, opentui, …) | -| `crates/core/` | The Rust engine: the same spec, in Rust (`dunky-state-machine`) | -| `crates/macros/` | `#[derive(State, Event, Context)]` for Rust machines | -| `crates/wasm/` | Rust machines as JS classes (`export_machine!`) | -| `packages/wasm/` | `fromWasm`: a Rust machine behind the TS `Machine` interface | -| `crates/sandbox/` | The sandbox machines in Rust, twins of `sandbox/shared` ones | -| `crates/uniffi/` | Rust machines for React Native over JSI (uniffi; facade planned) | -| `sandbox/uniffi/` | The turbo module uniffi-bindgen-react-native generates from it | +| File / location | What it owns | +| --------------------------- | ---------------------------------------------------------------------------------- | +| `packages/core/` | State-machine engine (plain-mutation kernel) | +| `packages/shared/bindings/` | Substrate-agnostic event + attr vocabulary (onPress, role, …) | +| `packages/shared/utils/` | mergeProps, composeHandlers | +| `packages//` | Hook + normalize per substrate (react, solid, native, opentui, …) | +| `crates/core/` | The Rust engine: the same spec, in Rust (`dunky-state-machine`) | +| `crates/core/macros/` | `#[derive(State, Event, Context)]`; its own crate only because proc macros must be | +| `crates/wasm/` | Bridge: Rust machines as JS classes (`export_machine!`), for JS runtimes with wasm | +| `packages/wasm/` | `fromWasm`: a Rust machine behind the TS `Machine` interface | +| `sandbox/rust/` | The sandbox machines in Rust, twins of `sandbox/shared` ones | +| `crates/jsi/` | Bridge: Rust machines for React Native over JSI (uniffi today; facade planned) | +| `sandbox/jsi/` | The turbo module uniffi-bindgen-react-native generates from it | ## The map @@ -159,8 +159,8 @@ This is the target shape of the repo; the boxes marked _planned_ do not exist ye v v +---------------------------+ +---------------------------+ | TS engine | | Rust engine | -| packages/core | | crates/core, crates/macros| -| @dunky.dev/state-machine | | dunky-state-machine | +| packages/core | | crates/core (+ its macros)| +| @dunky.dev/state-machine | | dunky-state-machine | +---------------------------+ +---------------------------+ | | | authors | authors @@ -174,9 +174,9 @@ This is the target shape of the repo; the boxes marked _planned_ do not exist ye | web: wasm32 | | native: lib | Rust app | v v | | +----------------+ +----------------+ | - | | crates/wasm | | crates/uniffi | | + | | crates/wasm | | crates/jsi | | | | export_machine!| | push protocol | | - | | wasm-bindgen | | ubrn turbo mod | | + | | wasm-bindgen | | uniffi today | | | +----------------+ +----------------+ | | | | | | notify(state, fields), timers, field reads | @@ -226,7 +226,7 @@ How to read it: - **A machine is authored against one engine, with its connector next to it.** TS machines against the TS engine; Rust machines against the Rust engine, which is the only place they run. The sandbox palette exists in both (`sandbox/shared`, - `crates/sandbox`), as a worked example of the twin rule. + `sandbox/rust`), as a worked example of the twin rule. - **The TS engine is the default in every JS runtime**, React Native included. A machine is written in Rust when it is wanted in Rust too (a Rust app) or shared across the web, React Native and Rust from one source. @@ -234,11 +234,16 @@ How to read it: engine: they map names to numbers, keep a JS mirror of the context, re-read only the fields each notify names, and fan changes out to subscribers. `packages/wasm` (`fromWasm`) does it over `crates/wasm`; the React Native facade does the same over - `crates/uniffi`, which speaks the same push protocol (one `notify` per change, timers + `crates/jsi`, which speaks the same push protocol (one `notify` per change, timers as host calls). -- **One binding per substrate.** `crates/wasm` for JS runtimes with wasm (web, Node, - Bun, so OpenTUI too). `crates/uniffi` for React Native over JSI, generated into a - turbo module by uniffi-bindgen-react-native (`sandbox/uniffi`). +- **One bridge per substrate, one protocol.** `crates/wasm` for JS runtimes with wasm + (web, Node, Bun, so OpenTUI too). `crates/jsi` for React Native over JSI: today a + uniffi crate, generated into a turbo module by uniffi-bindgen-react-native + (`sandbox/jsi`). The contract is the push protocol (`crates/wasm/SPEC.md`), not the + tool: a bridge built another way sits behind the same facade. "Bridges" cross a + runtime boundary; "bindings" (`packages/shared/bindings`, and its Rust twin to come) + are the a11y and event vocabulary targets consume. The two words are kept apart on + purpose. - **Targets are engine-blind.** They take a `Machine` through `toMachine` and `connect`, so react, solid, native and opentui work unchanged whichever engine built it. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e949811..2ab0cb4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ The Rust engine (`crates/`) is a plain Cargo workspace; run cargo at the repo ro | `cargo fmt` / `--check` | `rustfmt` | | `cargo bench -p dunky-benchmark` | The Rust engine's benchmark (`benchmark/rust`) | -`pnpm build:wasm` builds the sandbox machines written in Rust to wasm (`crates/sandbox/pkg`), +`pnpm build:wasm` builds the sandbox machines written in Rust to wasm (`sandbox/rust/pkg`), with their TS types: the sandbox's `?machine=rust` mode and its Rust tests need it. It needs the `wasm32-unknown-unknown` target and `wasm-bindgen-cli` at the version `Cargo.toml` pins. diff --git a/Cargo.toml b/Cargo.toml index ccfb476..87b223c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ # `cargo test --workspace` for the others. [workspace] resolver = "3" -members = ["crates/core", "crates/macros", "crates/wasm", "crates/sandbox", "benchmark/rust"] -# Built by its own pipeline (uniffi-bindgen-react-native) for iOS/Android. -exclude = ["crates/uniffi"] +members = ["crates/core", "crates/core/macros", "crates/wasm", "sandbox/rust", "benchmark/rust"] +# The React Native bridge: built by its own pipeline (uniffi-bindgen-react-native today). +exclude = ["crates/jsi"] [workspace.package] version = "0.1.0" @@ -17,9 +17,9 @@ rust-version = "1.85" [workspace.dependencies] dunky-state-machine = { path = "crates/core" } -dunky-state-machine-macros = { path = "crates/macros" } +dunky-state-machine-macros = { path = "crates/core/macros" } dunky-state-machine-wasm = { path = "crates/wasm" } -dunky-sandbox = { path = "crates/sandbox" } +dunky-sandbox = { path = "sandbox/rust" } serde = { version = "1", features = ["derive"] } serde_json = "1" # Must match the installed wasm-bindgen CLI exactly (wasm-bindgen --version). diff --git a/README.md b/README.md index 4efef44..8f329f9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ every target takes it like any other machine. | | | +---------+---------+ | | | - | wasm, uniffi Rust apps + | wasm, jsi Rust apps | v (iced, planned) | fromWasm: the TS Machine interface | over a Rust machine (packages/wasm) @@ -94,7 +94,7 @@ elements — the left two are agnostic, the right three are per-target: ([`packages/core`](./packages/core)) and in Rust ([`crates/core`](./crates/core)), two engines on one spec. A machine written in Rust reaches TypeScript through wasm ([`crates/wasm`](./crates/wasm) + [`packages/wasm`](./packages/wasm)) and - React Native through uniffi ([`crates/uniffi`](./crates/uniffi), in progress). + React Native over JSI ([`crates/jsi`](./crates/jsi), uniffi today, in progress). - **connector** — turns machine state into agnostic _bindings_ and keeps that view in sync as the machine changes. - **normalize** — per target, translates those bindings into real props diff --git a/TODO.md b/TODO.md index 8ae1058..2c9a2f4 100644 --- a/TODO.md +++ b/TODO.md @@ -6,11 +6,11 @@ ran on the Rust engine, is on the local branch `rust-engine-under-ts` (head `4e4 ## Rust machines on React Native (JSI) Decided on 2026-10-09. The TS engine stays the default on React Native. A machine -written in Rust (e.g. a dropdown) is consumed as wasm on the web and over uniffi on React +written in Rust (e.g. a dropdown) is consumed as wasm on the web and over JSI on React Native, behind a facade that gives it the TS `Machine` interface, as `fromWasm` does. -Spiked in PR #76: `crates/uniffi` runs in the Expo sandbox through -uniffi-bindgen-react-native (`sandbox/uniffi`, a turbo module Expo autolinks into a dev +Spiked in PR #76: `crates/jsi` runs in the Expo sandbox through +uniffi-bindgen-react-native (`sandbox/jsi`, a turbo module Expo autolinks into a dev build), in push mode (`attach(host)`: a `notify(state, lo, hi)` per change and timers as host calls, as `crates/wasm` speaks) next to the pull mode. The sandbox app times both against the TS engine (`sandbox/native/rust-bench.tsx`). Hermes, Release build, iPhone 17 @@ -30,7 +30,7 @@ simulator, per event: dropdown sends a handful. Too slow for a hot path (many machines updated per frame, or field reads on every render): the facade must keep the hot path on the JS side. -- [ ] The RN facade over `crates/uniffi` (`fromUniffi`, the twin of `fromWasm`): push +- [ ] The RN facade over `crates/jsi` (`fromNative`, the twin of `fromWasm`): push mode, a JS context mirror re-read only on the fields a notify names, renders read the mirror. Reuse `RustMachine` from `packages/wasm`; only the handle differs. - [ ] Typed payloads instead of JSON strings: uniffi records and enums as arguments diff --git a/crates/core/SPEC.md b/crates/core/SPEC.md index 88f4de2..0378f93 100644 --- a/crates/core/SPEC.md +++ b/crates/core/SPEC.md @@ -42,7 +42,7 @@ reporting. - **No IO, no clock, no platform.** The engine never reads time and never schedules work. A host does. -- **No JS.** Bindings live in their own crates (`crates/uniffi` for React Native). +- **No JS.** Bindings live in their own crates (`crates/jsi` for React Native). - **One thread.** A machine and what it shares stay on one thread. A binding that crosses threads enforces that itself. - **At most 64 context fields** per machine: each field is one bit of the change mask. diff --git a/crates/macros/Cargo.toml b/crates/core/macros/Cargo.toml similarity index 100% rename from crates/macros/Cargo.toml rename to crates/core/macros/Cargo.toml diff --git a/crates/macros/src/lib.rs b/crates/core/macros/src/lib.rs similarity index 100% rename from crates/macros/src/lib.rs rename to crates/core/macros/src/lib.rs diff --git a/crates/macros/src/ts.rs b/crates/core/macros/src/ts.rs similarity index 100% rename from crates/macros/src/ts.rs rename to crates/core/macros/src/ts.rs diff --git a/crates/uniffi/.gitignore b/crates/jsi/.gitignore similarity index 100% rename from crates/uniffi/.gitignore rename to crates/jsi/.gitignore diff --git a/crates/uniffi/Cargo.lock b/crates/jsi/Cargo.lock similarity index 99% rename from crates/uniffi/Cargo.lock rename to crates/jsi/Cargo.lock index d39d62e..ee0ab82 100644 --- a/crates/uniffi/Cargo.lock +++ b/crates/jsi/Cargo.lock @@ -132,22 +132,22 @@ dependencies = [ ] [[package]] -name = "dunky-state-machine-macros" +name = "dunky-state-machine-jsi" version = "0.1.0" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", + "dunky-sandbox", + "dunky-state-machine", + "serde_json", + "uniffi", ] [[package]] -name = "dunky-state-machine-uniffi" +name = "dunky-state-machine-macros" version = "0.1.0" dependencies = [ - "dunky-sandbox", - "dunky-state-machine", - "serde_json", - "uniffi", + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] diff --git a/crates/uniffi/Cargo.toml b/crates/jsi/Cargo.toml similarity index 88% rename from crates/uniffi/Cargo.toml rename to crates/jsi/Cargo.toml index 278884d..01ef372 100644 --- a/crates/uniffi/Cargo.toml +++ b/crates/jsi/Cargo.toml @@ -5,7 +5,7 @@ [workspace] [package] -name = "dunky-state-machine-uniffi" +name = "dunky-state-machine-jsi" version = "0.1.0" edition = "2024" license = "MIT" @@ -13,14 +13,14 @@ repository = "https://github.com/dunky-dev/state-machine" publish = false [lib] -name = "dunky_state_machine_uniffi" +name = "dunky_state_machine_jsi" # staticlib: what ubrn links into the iOS xcframework and the Android turbo-module. # cdylib: what ubrn's `jsi2` flavour loads instead, keeping that path open. crate-type = ["cdylib", "staticlib"] [dependencies] dunky-state-machine = { path = "../core" } -dunky-sandbox = { path = "../sandbox" } +dunky-sandbox = { path = "../../sandbox/rust" } serde_json = "1" # Must stay on the uniffi minor that uniffi-bindgen-react-native is built with # (ubrn 0.31.0-6 -> uniffi 0.31): the generated C++/TS check the contract version. diff --git a/crates/uniffi/src/lib.rs b/crates/jsi/src/lib.rs similarity index 100% rename from crates/uniffi/src/lib.rs rename to crates/jsi/src/lib.rs diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 248aa64..4344312 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -238,6 +238,21 @@ importers: specifier: workspace:* version: link:../core + sandbox/jsi: + dependencies: + '@ubjs/core': + specifier: 0.31.0-6 + version: 0.31.0-6 + react: + specifier: '*' + version: 19.2.6 + react-native: + specifier: '*' + version: 0.81.4(@babel/core@7.29.7)(@types/react@19.2.15)(react@19.2.6) + uniffi-bindgen-react-native: + specifier: 0.31.0-6 + version: 0.31.0-6 + sandbox/native: dependencies: '@dunky.dev/native-state-machine': @@ -258,9 +273,9 @@ importers: '@sandbox/cmdk-core': specifier: workspace:^ version: link:../shared - '@sandbox/uniffi': + '@sandbox/jsi': specifier: workspace:^ - version: link:../uniffi + version: link:../jsi expo: specifier: ^54.0.0 version: 54.0.35(@babel/core@7.29.7)(react-native@0.81.4(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(typescript@7.0.2) @@ -410,21 +425,6 @@ importers: specifier: ^3.0.0-next.27 version: 3.0.0-next.27(@solidjs/web@2.0.0-rc.1(solid-js@2.0.0-rc.1))(solid-js@2.0.0-rc.1)(vite@8.0.14(@types/node@24.13.2)(esbuild@0.28.0)(jiti@2.7.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)) - sandbox/uniffi: - dependencies: - '@ubjs/core': - specifier: 0.31.0-6 - version: 0.31.0-6 - react: - specifier: '*' - version: 19.2.6 - react-native: - specifier: '*' - version: 0.81.4(@babel/core@7.29.7)(@types/react@19.2.15)(react@19.2.6) - uniffi-bindgen-react-native: - specifier: 0.31.0-6 - version: 0.31.0-6 - website: dependencies: '@astrojs/starlight': diff --git a/sandbox/README.md b/sandbox/README.md index 0880db2..96c4d3e 100644 --- a/sandbox/README.md +++ b/sandbox/README.md @@ -48,7 +48,7 @@ All four consume the workspace packages straight from their TypeScript `src/` Both palettes run on the Rust engine. By default the sandboxes run the one written in TypeScript (`shared/src/machine.ts`); each web and terminal sandbox can also run its -Rust twin (`crates/sandbox`, wrapped with `fromWasm` from `@dunky.dev/state-machine-wasm`) — same `connect()`, same markup, +Rust twin (`sandbox/rust`, wrapped with `fromWasm` from `@dunky.dev/state-machine-wasm`) — same `connect()`, same markup, only the machine's source language differs: ```bash diff --git a/sandbox/uniffi/.gitignore b/sandbox/jsi/.gitignore similarity index 100% rename from sandbox/uniffi/.gitignore rename to sandbox/jsi/.gitignore diff --git a/sandbox/uniffi/README.md b/sandbox/jsi/README.md similarity index 69% rename from sandbox/uniffi/README.md rename to sandbox/jsi/README.md index d5cf6bb..1599a51 100644 --- a/sandbox/uniffi/README.md +++ b/sandbox/jsi/README.md @@ -1,13 +1,13 @@ -# @sandbox/uniffi +# @sandbox/jsi -The sandbox palette written in Rust (`crates/uniffi`) as a React Native turbo module, +The sandbox palette written in Rust (`crates/jsi`) as a React Native turbo module, generated by [uniffi-bindgen-react-native](https://github.com/jhugman/uniffi-bindgen-react-native) (ubrn). `sandbox/native` depends on it; Expo autolinks it into the dev build. Only `package.json` and `ubrn.config.yaml` are source. Everything else is generated: ```bash -pnpm --filter @sandbox/uniffi ubrn:ios # Rust for the iOS simulator + C++/TS glue +pnpm --filter @sandbox/jsi ubrn:ios # Rust for the iOS simulator + C++/TS glue cd sandbox/native && pnpm ios # an Expo dev build (not Expo Go) ``` diff --git a/sandbox/uniffi/package.json b/sandbox/jsi/package.json similarity index 82% rename from sandbox/uniffi/package.json rename to sandbox/jsi/package.json index 3491922..3dfc2ae 100644 --- a/sandbox/uniffi/package.json +++ b/sandbox/jsi/package.json @@ -1,8 +1,8 @@ { - "name": "@sandbox/uniffi", + "name": "@sandbox/jsi", "version": "0.0.0", "private": true, - "description": "The sandbox palette in Rust (crates/uniffi) as a React Native turbo module, generated by uniffi-bindgen-react-native.", + "description": "The sandbox palette in Rust (crates/jsi) as a React Native turbo module, generated by uniffi-bindgen-react-native.", "homepage": "https://github.com/dunky-dev/state-machine", "license": "MIT", "author": "Dunky", diff --git a/sandbox/uniffi/ubrn.config.yaml b/sandbox/jsi/ubrn.config.yaml similarity index 50% rename from sandbox/uniffi/ubrn.config.yaml rename to sandbox/jsi/ubrn.config.yaml index 2f38a00..05374a2 100644 --- a/sandbox/uniffi/ubrn.config.yaml +++ b/sandbox/jsi/ubrn.config.yaml @@ -1,7 +1,7 @@ -# uniffi-bindgen-react-native: the Rust crate is crates/uniffi; the simulator build is +# uniffi-bindgen-react-native: the Rust crate is crates/jsi; the simulator build is # all the sandbox needs (`pnpm ubrn:ios`). rust: - directory: ../../crates/uniffi + directory: ../../crates/jsi manifestPath: Cargo.toml ios: targets: diff --git a/sandbox/native/package.json b/sandbox/native/package.json index 7155a96..ced6184 100644 --- a/sandbox/native/package.json +++ b/sandbox/native/package.json @@ -15,7 +15,7 @@ "@dunky.dev/state-machine-bindings": "workspace:^", "@dunky.dev/state-machine-utils": "workspace:^", "@sandbox/cmdk-core": "workspace:^", - "@sandbox/uniffi": "workspace:^", + "@sandbox/jsi": "workspace:^", "expo": "^54.0.0", "expo-status-bar": "~3.0.0", "react": "19.1.0", diff --git a/sandbox/native/rust-bench.tsx b/sandbox/native/rust-bench.tsx index b58b4b4..784fb72 100644 --- a/sandbox/native/rust-bench.tsx +++ b/sandbox/native/rust-bench.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { Pressable, StyleSheet, Text, View } from 'react-native' import { toMachine } from '@dunky.dev/state-machine' import { commandPaletteMachineConfig, DEMO_COMMANDS } from '@sandbox/cmdk-core' -import { type MachineHost, PaletteMachine } from '@sandbox/uniffi' +import { type MachineHost, PaletteMachine } from '@sandbox/jsi' // The uniffi spike: what one event costs in Hermes on each path, side by side. // diff --git a/sandbox/opentui/src/index.tsx b/sandbox/opentui/src/index.tsx index bc7f11d..75d5f17 100644 --- a/sandbox/opentui/src/index.tsx +++ b/sandbox/opentui/src/index.tsx @@ -3,7 +3,7 @@ import { createRoot } from '@opentui/react' import { commandPaletteMachineConfig, type CommandPaletteSource } from '@sandbox/cmdk-core' import { App, type Language } from './app' -// `--machine=rust` runs the palette written in Rust (crates/sandbox → wasm) instead +// `--machine=rust` runs the palette written in Rust (sandbox/rust → wasm) instead // of the one written in TS. Build the wasm first: `pnpm build:wasm`. const language: Language = process.argv.includes('--machine=rust') ? 'rust' : 'ts' diff --git a/sandbox/react/src/main.tsx b/sandbox/react/src/main.tsx index d1c148f..80ddbe4 100644 --- a/sandbox/react/src/main.tsx +++ b/sandbox/react/src/main.tsx @@ -9,7 +9,7 @@ import '../../shared/src/styles.css' const root = document.getElementById('root') if (!root) throw new Error('missing #root') -// `?machine=rust` runs the palette written in Rust (crates/sandbox → wasm) instead +// `?machine=rust` runs the palette written in Rust (sandbox/rust → wasm) instead // of the one written in TS. Build the wasm first: `pnpm build:wasm`. const language: Language = new URLSearchParams(location.search).get('machine') === 'rust' ? 'rust' : 'ts' diff --git a/crates/sandbox/Cargo.toml b/sandbox/rust/Cargo.toml similarity index 91% rename from crates/sandbox/Cargo.toml rename to sandbox/rust/Cargo.toml index f5cbbfd..4c392ec 100644 --- a/crates/sandbox/Cargo.toml +++ b/sandbox/rust/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"] [features] # The JS classes (export_machine!), on for the wasm build (pnpm build:wasm) only, so -# crates reusing the machines (crates/uniffi) never link the JS exports. +# crates reusing the machines (crates/jsi) never link the JS exports. wasm = ["dep:dunky-state-machine-wasm", "dep:wasm-bindgen"] [dependencies] diff --git a/crates/sandbox/examples/typescript.rs b/sandbox/rust/examples/typescript.rs similarity index 100% rename from crates/sandbox/examples/typescript.rs rename to sandbox/rust/examples/typescript.rs diff --git a/crates/sandbox/src/dialog.rs b/sandbox/rust/src/dialog.rs similarity index 100% rename from crates/sandbox/src/dialog.rs rename to sandbox/rust/src/dialog.rs diff --git a/crates/sandbox/src/lib.rs b/sandbox/rust/src/lib.rs similarity index 93% rename from crates/sandbox/src/lib.rs rename to sandbox/rust/src/lib.rs index cb3a44c..96b871e 100644 --- a/crates/sandbox/src/lib.rs +++ b/sandbox/rust/src/lib.rs @@ -5,8 +5,8 @@ //! (computed filtering, logical navigation). //! - [`dialog`] — open / closing / closed with a context-driven exit delay (`after`). //! -//! `crates/uniffi` runs the palette on React Native. With the `wasm` feature, both are JS -//! classes too (`pnpm build:wasm` builds them to `crates/sandbox/pkg`): every sandbox +//! `crates/jsi` runs the palette on React Native. With the `wasm` feature, both are JS +//! classes too (`pnpm build:wasm` builds them to `sandbox/rust/pkg`): every sandbox //! runs the palette on them with `?machine=rust` (`--machine=rust` in the terminal). pub mod dialog; diff --git a/crates/sandbox/src/palette.rs b/sandbox/rust/src/palette.rs similarity index 100% rename from crates/sandbox/src/palette.rs rename to sandbox/rust/src/palette.rs diff --git a/sandbox/shared/src/rust-node.ts b/sandbox/shared/src/rust-node.ts index 6fc58b9..91e1d8d 100644 --- a/sandbox/shared/src/rust-node.ts +++ b/sandbox/shared/src/rust-node.ts @@ -4,7 +4,7 @@ import { loadRustSync } from './rust' /** Load the sandbox wasm in Node or Bun from the built package. */ export function loadRustNode(): void { loadRustSync( - readFileSync(new URL('../../../crates/sandbox/pkg/dunky_sandbox_bg.wasm', import.meta.url)), + readFileSync(new URL('../../../sandbox/rust/pkg/dunky_sandbox_bg.wasm', import.meta.url)), ) } diff --git a/sandbox/shared/src/rust.ts b/sandbox/shared/src/rust.ts index 0c55acb..6664d04 100644 --- a/sandbox/shared/src/rust.ts +++ b/sandbox/shared/src/rust.ts @@ -1,5 +1,5 @@ /** - * The sandbox machines written in Rust (crates/sandbox), wrapped with `fromWasm`. + * The sandbox machines written in Rust (sandbox/rust), wrapped with `fromWasm`. * Build the wasm first: `pnpm build:wasm`. Load it once — `await loadRust()` in a * browser, `loadRustSync(bytes)` (or `./rust-node`) in Node and Bun — then build * machines with the factories below. Their types come from the Rust types: the build @@ -10,7 +10,7 @@ import init, { DialogMachine, initSync, PaletteMachine, -} from '../../../crates/sandbox/pkg/dunky_sandbox.js' +} from '../../../sandbox/rust/pkg/dunky_sandbox.js' import type { CommandPaletteMachine } from './machine' import type { Command, diff --git a/sandbox/solid/src/main.tsx b/sandbox/solid/src/main.tsx index b346627..166dbea 100644 --- a/sandbox/solid/src/main.tsx +++ b/sandbox/solid/src/main.tsx @@ -8,7 +8,7 @@ import '../../shared/src/styles.css' const root = document.getElementById('root') if (!root) throw new Error('missing #root') -// `?machine=rust` runs the palette written in Rust (crates/sandbox → wasm) instead +// `?machine=rust` runs the palette written in Rust (sandbox/rust → wasm) instead // of the one written in TS. Build the wasm first: `pnpm build:wasm`. const language: Language = new URLSearchParams(location.search).get('machine') === 'rust' ? 'rust' : 'ts' diff --git a/scripts/build-wasm.sh b/scripts/build-wasm.sh index c20a725..fd973fc 100755 --- a/scripts/build-wasm.sh +++ b/scripts/build-wasm.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Build the wasm of the sandbox machines written in Rust (crates/sandbox -> its pkg/), +# Build the wasm of the sandbox machines written in Rust (sandbox/rust -> its pkg/), # with their TS types: what `?machine=rust` and the sandbox's Rust tests load. # Needs: rustup target wasm32-unknown-unknown, wasm-bindgen-cli matching the crate # version (see Cargo.toml), and optionally wasm-opt (binaryen) for the size pass. @@ -39,5 +39,5 @@ types() { cargo run -q -p "$crate" --example typescript "$@" >> "$dts" } -build dunky-sandbox dunky_sandbox crates/sandbox/pkg --features wasm -types dunky-sandbox crates/sandbox/pkg/dunky_sandbox.d.ts --features wasm +build dunky-sandbox dunky_sandbox sandbox/rust/pkg --features wasm +types dunky-sandbox sandbox/rust/pkg/dunky_sandbox.d.ts --features wasm diff --git a/website/src/content/docs/rust/index.mdx b/website/src/content/docs/rust/index.mdx index f3d50b8..024fb99 100644 --- a/website/src/content/docs/rust/index.mdx +++ b/website/src/content/docs/rust/index.mdx @@ -28,16 +28,16 @@ JS and every target Rust programs, native bindings, `dunky-state-machine`. The same states, events, guards, effects, timers and computed values. - **Sharing one machine between Rust and JS**: write it in Rust, and use it from TypeScript through wasm with [`fromWasm`](/rust/typescript) on the web, or over - uniffi on React Native (in progress). The machine still runs on the Rust engine. + JSI on React Native (in progress). The machine still runs on the Rust engine. ## The crates -| Crate | What it is | -| ---------------------------- | --------------------------------------------------------------------------- | -| `dunky-state-machine` | The Rust engine: machines, config builder, timers as commands, observers. | -| `dunky-state-machine-macros` | `#[derive(State, Event, Context)]`, re-exported by `dunky-state-machine`. | -| `dunky-state-machine-wasm` | `export_machine!`: a Rust machine as a JS class, in your own wasm module. | -| `dunky-state-machine-uniffi` | Rust machines on React Native, a turbo module through uniffi (in progress). | +| Crate | What it is | +| ---------------------------- | ---------------------------------------------------------------------------------- | +| `dunky-state-machine` | The Rust engine: machines, config builder, timers as commands, observers. | +| `dunky-state-machine-macros` | `#[derive(State, Event, Context)]`, re-exported by `dunky-state-machine`. | +| `dunky-state-machine-wasm` | `export_machine!`: a Rust machine as a JS class, in your own wasm module. | +| `dunky-state-machine-jsi` | Rust machines on React Native over JSI (a uniffi turbo module today; in progress). | The crates are not on crates.io yet. Use them from the repository: