Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/svelte-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@dunky.dev/state-machine-svelte': minor
---

Add Svelte 5 bindings: `@dunky.dev/state-machine-svelte`. A thin, runes-based edge layer mirroring the React package — `useMachine` (build-once bridge + lifecycle + prop-scoped substrate effects, returning a reactive `{ api, machine }`), `useSelector` (fine-grained leaf subscription returning `{ current }`), and `normalize`/`mergeProps` for Svelte's DOM idiom (lowercase `on*` event props, `class`/`style` string merge). The package ships its `src` uncompiled so the consumer's Svelte compiler processes its `.svelte.ts` runes modules.
1 change: 0 additions & 1 deletion benchmark/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
The suite measures `@dunky.dev/state-machine`'s hot paths in isolation and compares the runnable parts against XState and Zag. It runs in one Node process with `--expose-gc` (Node 24, Apple Silicon for the published figures); each section is also exported as a `run*()` function for isolation.

Fairness rules that define what the numbers mean:

- **Synchronous-only comparison in ops/sec loops.** Dunky and XState `send` synchronously and are measured everywhere; Zag's `send` is microtask-batched (async), so it appears only where it runs synchronously — construction, memory (headless `VanillaMachine`), and React rendering (`@zag-js/react`). A tight `tinybench` loop can't fairly time an async engine, so those cells are marked `n/a ᵃ`. Missing first-class primitives (e.g. XState has no lazy/memoized `computed`) are marked `n/a ᶠ`.
- **Shared module-level config across instances.** All engines reuse one `const` config — matching how a real app declares a machine once and instantiates it many times — so construction/memory loops time machine construction, not config-literal allocation.
- **Two XState variants** in fine-grain tables: `xstate` (subscribe + hand-written value diff, matching Dunky's built-in dedup) and `xstate-raw` (stock subscribe, fires on every snapshot).
Expand Down
1 change: 0 additions & 1 deletion benchmark/demo/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
The demo makes the ops/sec tables **visible**: four panels (Dunky, XState, Zag, and a Plain JS control), each a grid of real per-cell state machines fed one ramping change stream under an equal per-frame budget. Run it with `pnpm benchmark:demo`; it's idle until you press **Start**.

It is deliberately **engine-bound, not DOM-bound** — a naive "render a grid" demo would measure React's render cost (which dominates and ties every engine), so the design isolates engine cost:

- **Every cell is a real machine** doing the work the suite measures per update: a guarded transition (guard fallthrough) that writes context feeding a computed/derived value, then reads it back.
- **Paint is off the hot path.** Each panel is a `<canvas>` heatmap on a throttled ~10fps tick — one cheap fill per cell, no per-cell React — so paint cost is tiny and identical across panels; what differs is the engine.
- **Equal time budget.** Each frame, every panel gets the same few ms to drain its queue; a cheaper-per-update engine clears more and its backlog stays near zero, a costlier one falls behind.
Expand Down
12 changes: 6 additions & 6 deletions benchmark/demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ nothing. This demo is deliberately **engine-bound**, not DOM-bound:

## What you're watching

| Panel | Per-cell model |
| ---------------------- | ------------------------------------------------------------- |
| **Dunky** | machine per cell · guarded transition + memoized computed |
| **XState** | actor per cell · guarded transition + assign-derived field |
| **Zag** | VanillaMachine per cell · guarded transition + bindable cells |
| **Vanilla** (control) | no engine — the same guard walk + derive as plain JS |
| Panel | Per-cell model |
| --------------------- | ------------------------------------------------------------- |
| **Dunky** | machine per cell · guarded transition + memoized computed |
| **XState** | actor per cell · guarded transition + assign-derived field |
| **Zag** | VanillaMachine per cell · guarded transition + bindable cells |
| **Vanilla** (control) | no engine — the same guard walk + derive as plain JS |

> **One asymmetry, disclosed:** Dunky's derived value is a **lazy/memoized
> `computed`** (recomputes only when its input changes); XState and Zag recompute
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"devDependencies": {
"@changesets/changelog-github": "^0.7.0",
"@changesets/cli": "^2.31.0",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@testing-library/svelte": "^5.4.2",
"@types/node": "^22.10.2",
"husky": "^9.1.7",
"knip": "^6.15.0",
Expand Down
10 changes: 5 additions & 5 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
**⚡️ Blazing fast.** Design systems and complex UIs can run hundreds of live machines at once. Dunky is tuned for exactly that load. [See the benchmark →](https://github.com/dunky-dev/state-machine/tree/main/benchmark#readme)

```ts
import { setup } from "@dunky.dev/state-machine";
import { setup } from '@dunky.dev/state-machine'

const toggle = setup({
initial: "off",
initial: 'off',
states: {
off: { on: { TOGGLE: "on" } },
on: { on: { TOGGLE: "off" } },
off: { on: { TOGGLE: 'on' } },
on: { on: { TOGGLE: 'off' } },
},
});
})
```

This is our first public release (`0.1.0`). The engine is stable and tested; the target bridges are early and evolving. Come kick the tires, watch the live benchmark, and tell us where it breaks.
Expand Down
11 changes: 5 additions & 6 deletions packages/native/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
### Minor Changes

- [`d0e20a5`](https://github.com/dunky-dev/state-machine/commit/d0e20a5ac3ca953c923e05819034e420394af83b) Thanks [@ivanbanov](https://github.com/ivanbanov)! - Rename the framework adapters to the `state-machine-*` suffix convention so the whole scope is consistent (`state-machine`, `state-machine-react`, `state-machine-native`, `state-machine-opentui`, `state-machine-utils`, `state-machine-bindings`).

- `@dunky.dev/react-state-machine` → `@dunky.dev/state-machine-react`
- `@dunky.dev/native-state-machine` → `@dunky.dev/state-machine-native`
- `@dunky.dev/opentui-state-machine` → `@dunky.dev/state-machine-opentui`
Expand Down Expand Up @@ -36,15 +35,15 @@
**⚡️ Blazing fast.** Design systems and complex UIs can run hundreds of live machines at once. Dunky is tuned for exactly that load. [See the benchmark →](https://github.com/dunky-dev/state-machine/tree/main/benchmark#readme)

```ts
import { setup } from "@dunky.dev/state-machine";
import { setup } from '@dunky.dev/state-machine'

const toggle = setup({
initial: "off",
initial: 'off',
states: {
off: { on: { TOGGLE: "on" } },
on: { on: { TOGGLE: "off" } },
off: { on: { TOGGLE: 'on' } },
on: { on: { TOGGLE: 'off' } },
},
});
})
```

This is our first public release (`0.1.0`). The engine is stable and tested; the target bridges are early and evolving. Come kick the tires, watch the live benchmark, and tell us where it breaks.
Expand Down
11 changes: 5 additions & 6 deletions packages/opentui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
### Minor Changes

- [`d0e20a5`](https://github.com/dunky-dev/state-machine/commit/d0e20a5ac3ca953c923e05819034e420394af83b) Thanks [@ivanbanov](https://github.com/ivanbanov)! - Rename the framework adapters to the `state-machine-*` suffix convention so the whole scope is consistent (`state-machine`, `state-machine-react`, `state-machine-native`, `state-machine-opentui`, `state-machine-utils`, `state-machine-bindings`).

- `@dunky.dev/react-state-machine` → `@dunky.dev/state-machine-react`
- `@dunky.dev/native-state-machine` → `@dunky.dev/state-machine-native`
- `@dunky.dev/opentui-state-machine` → `@dunky.dev/state-machine-opentui`
Expand Down Expand Up @@ -34,15 +33,15 @@
**⚡️ Blazing fast.** Design systems and complex UIs can run hundreds of live machines at once. Dunky is tuned for exactly that load. [See the benchmark →](https://github.com/dunky-dev/state-machine/tree/main/benchmark#readme)

```ts
import { setup } from "@dunky.dev/state-machine";
import { setup } from '@dunky.dev/state-machine'

const toggle = setup({
initial: "off",
initial: 'off',
states: {
off: { on: { TOGGLE: "on" } },
on: { on: { TOGGLE: "off" } },
off: { on: { TOGGLE: 'on' } },
on: { on: { TOGGLE: 'off' } },
},
});
})
```

This is our first public release (`0.1.0`). The engine is stable and tested; the target bridges are early and evolving. Come kick the tires, watch the live benchmark, and tell us where it breaks.
Expand Down
11 changes: 5 additions & 6 deletions packages/react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
### Minor Changes

- [`d0e20a5`](https://github.com/dunky-dev/state-machine/commit/d0e20a5ac3ca953c923e05819034e420394af83b) Thanks [@ivanbanov](https://github.com/ivanbanov)! - Rename the framework adapters to the `state-machine-*` suffix convention so the whole scope is consistent (`state-machine`, `state-machine-react`, `state-machine-native`, `state-machine-opentui`, `state-machine-utils`, `state-machine-bindings`).

- `@dunky.dev/react-state-machine` → `@dunky.dev/state-machine-react`
- `@dunky.dev/native-state-machine` → `@dunky.dev/state-machine-native`
- `@dunky.dev/opentui-state-machine` → `@dunky.dev/state-machine-opentui`
Expand Down Expand Up @@ -35,15 +34,15 @@
**⚡️ Blazing fast.** Design systems and complex UIs can run hundreds of live machines at once. Dunky is tuned for exactly that load. [See the benchmark →](https://github.com/dunky-dev/state-machine/tree/main/benchmark#readme)

```ts
import { setup } from "@dunky.dev/state-machine";
import { setup } from '@dunky.dev/state-machine'

const toggle = setup({
initial: "off",
initial: 'off',
states: {
off: { on: { TOGGLE: "on" } },
on: { on: { TOGGLE: "off" } },
off: { on: { TOGGLE: 'on' } },
on: { on: { TOGGLE: 'off' } },
},
});
})
```

This is our first public release (`0.1.0`). The engine is stable and tested; the target bridges are early and evolving. Come kick the tires, watch the live benchmark, and tell us where it breaks.
Expand Down
10 changes: 5 additions & 5 deletions packages/shared/bindings/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
**⚡️ Blazing fast.** Design systems and complex UIs can run hundreds of live machines at once. Dunky is tuned for exactly that load. [See the benchmark →](https://github.com/dunky-dev/state-machine/tree/main/benchmark#readme)

```ts
import { setup } from "@dunky.dev/state-machine";
import { setup } from '@dunky.dev/state-machine'

const toggle = setup({
initial: "off",
initial: 'off',
states: {
off: { on: { TOGGLE: "on" } },
on: { on: { TOGGLE: "off" } },
off: { on: { TOGGLE: 'on' } },
on: { on: { TOGGLE: 'off' } },
},
});
})
```

This is our first public release (`0.1.0`). The engine is stable and tested; the target bridges are early and evolving. Come kick the tires, watch the live benchmark, and tell us where it breaks.
Expand Down
10 changes: 5 additions & 5 deletions packages/shared/utils/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
**⚡️ Blazing fast.** Design systems and complex UIs can run hundreds of live machines at once. Dunky is tuned for exactly that load. [See the benchmark →](https://github.com/dunky-dev/state-machine/tree/main/benchmark#readme)

```ts
import { setup } from "@dunky.dev/state-machine";
import { setup } from '@dunky.dev/state-machine'

const toggle = setup({
initial: "off",
initial: 'off',
states: {
off: { on: { TOGGLE: "on" } },
on: { on: { TOGGLE: "off" } },
off: { on: { TOGGLE: 'on' } },
on: { on: { TOGGLE: 'off' } },
},
});
})
```

This is our first public release (`0.1.0`). The engine is stable and tested; the target bridges are early and evolving. Come kick the tires, watch the live benchmark, and tell us where it breaks.
Expand Down
21 changes: 21 additions & 0 deletions packages/svelte/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Ivan Banov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading