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: