diff --git a/.agents/skills/effect-machine-pr/SKILL.md b/.agents/skills/effect-machine-pr/SKILL.md
index 3a25da2..c34a8e8 100644
--- a/.agents/skills/effect-machine-pr/SKILL.md
+++ b/.agents/skills/effect-machine-pr/SKILL.md
@@ -17,27 +17,26 @@ Ship the requested change through a green, merged pull request. Treat invocation
## Implement the complete change
-1. Inspect the relevant public API, internals, tests, documentation, examples, and analogous Effect implementation before editing.
+1. Inspect the relevant public API, internals, tests, documentation, and analogous Effect implementation before editing.
2. Implement the long-term design, including generic and optimized semantics where applicable. Follow the Cluster ownership boundary in `AGENTS.md`.
-3. Add observable runtime tests, typetests, differential tests, JSDocs, README or guide updates, and example changes required to make the feature complete.
+3. Add observable runtime tests, typetests, differential tests, JSDocs, README, and guide updates required to make the feature complete.
4. Add a changeset when required. Use minor for a public addition or breaking change and patch for a compatible fix or implementation improvement. Never use major before 1.0.
5. Keep the changeset user-facing: explain the resulting API and direct migration, not implementation history or external inspiration.
## Validate locally
1. Run `pnpm check`.
-2. Run `pnpm check` in every affected package directly below `examples/`.
-3. Run `pnpm perf:types` for changes that affect the public TypeScript API or inference.
-4. Always run `pnpm perf:runtime` before publishing this full-lifecycle workflow. Treat the local run as a functional smoke test; the CI base-versus-PR comparison is authoritative.
-5. Fix every failure and repeat all affected checks. Treat correctness, type safety, runtime performance, and memory regressions as blockers.
-6. Audit the final diff, formatting, generated files, public terminology, changeset level, and working-tree scope.
+2. Run `pnpm perf:types` for changes that affect the public TypeScript API or inference.
+3. Always run `pnpm perf:runtime` before publishing this full-lifecycle workflow. Treat the local run as a functional smoke test; the CI base-versus-PR comparison is authoritative.
+4. Fix every failure and repeat all affected checks. Treat correctness, type safety, runtime performance, and memory regressions as blockers.
+5. Audit the final diff, formatting, generated files, public terminology, changeset level, and working-tree scope.
## Publish and merge
1. Stage only the intended files, commit with a concise outcome-oriented message, and confirm the worktree is clean.
2. Fetch the remote base again before pushing. If it advanced materially, integrate it safely and repeat affected validation.
3. Push the branch and open a ready pull request using the repository template. Record the changeset decision and validation without copying noisy local benchmark tables.
-4. Monitor every required check, example job, and type/runtime performance workflow. Inspect the base-versus-PR reports, not only the aggregate green status.
+4. Monitor every required check and type/runtime performance workflow. Inspect the base-versus-PR reports, not only the aggregate green status.
5. Diagnose failures from their logs, implement in-scope fixes, rerun relevant local checks, push, and continue monitoring. Never weaken semantics, inference, tests, or thresholds to make CI pass.
6. Merge only when the pull request is current, mergeable, and every required check is green. Use the repository's normal squash-and-branch-cleanup convention unless instructed otherwise.
7. Verify the pull request's remote state even if the merge command reports a local worktree checkout error. Confirm the merge commit is present on `origin/ ` and the remote feature branch was removed.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 752c404..ea6e6e9 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -10,7 +10,6 @@
## Validation
- [ ] `pnpm check`
-- [ ] Relevant example checks, when examples changed
- [ ] Automated type-performance measurement passed or was not required
- [ ] Automated runtime- and memory-performance measurement passed or was not required
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b0bb2f8..d775148 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -43,68 +43,3 @@ jobs:
github.head_ref == 'changeset-release/main' &&
github.event.pull_request.user.login == 'github-actions[bot]'
run: echo "Changesets release pull requests consume their changeset files."
-
- discover-examples:
- runs-on: ubuntu-latest
- outputs:
- matrix: ${{ steps.examples.outputs.examples }}
- required: ${{ steps.examples.outputs.examples_required }}
- steps:
- - uses: actions/checkout@v7
- with:
- fetch-depth: 0
- - id: examples
- run: >-
- node scripts/ci-changes.mjs
- --base ${{ github.event.pull_request.base.sha }}
- --head ${{ github.event.pull_request.head.sha }}
- --github-output "$GITHUB_OUTPUT"
-
- example:
- needs: discover-examples
- if: needs.discover-examples.outputs.required == 'true'
- strategy:
- fail-fast: false
- matrix:
- include: ${{ fromJSON(needs.discover-examples.outputs.matrix) }}
- name: example (${{ matrix.example }})
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: ${{ matrix.directory }}
- steps:
- - uses: actions/checkout@v7
- - uses: pnpm/action-setup@v6
- with:
- package_json_file: ${{ matrix.directory }}/package.json
- - uses: actions/setup-node@v7
- with:
- node-version: 24
- cache: pnpm
- cache-dependency-path: |
- pnpm-lock.yaml
- ${{ matrix.directory }}/pnpm-lock.yaml
- - run: pnpm --dir ../.. install --frozen-lockfile
- - run: pnpm --dir ../.. build
- - run: pnpm install --frozen-lockfile
- - run: pnpm check
-
- examples:
- if: always()
- needs: [discover-examples, example]
- name: examples
- runs-on: ubuntu-latest
- steps:
- - name: Require every example check to pass
- env:
- DISCOVERY_RESULT: ${{ needs.discover-examples.result }}
- EXAMPLES_REQUIRED: ${{ needs.discover-examples.outputs.required }}
- EXAMPLE_RESULT: ${{ needs.example.result }}
- run: |
- test "$DISCOVERY_RESULT" = "success"
- if test "$EXAMPLES_REQUIRED" = "true"; then
- test "$EXAMPLE_RESULT" = "success"
- else
- test "$EXAMPLE_RESULT" = "skipped"
- echo "Examples are not affected by this pull request." >> "$GITHUB_STEP_SUMMARY"
- fi
diff --git a/AGENTS.md b/AGENTS.md
index 726d5fe..4dbef30 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -61,14 +61,6 @@ pnpm perf:runtime
Use the pull request performance workflows to compare against the base branch; do not draw conclusions from a single noisy benchmark run. Record the relevant checks and performance result in the pull request.
-When an example changes, run its own check from the example directory:
-
-```sh
-pnpm check
-```
-
-Every package directly below `examples/` must have a `check` script and a committed lockfile.
-
## Pull request conventions
- Add or update a changeset for changes under `src/` or changes to `package.json`, following the changelog-writing guide in `.changeset/README.md`.
diff --git a/README.md b/README.md
index 6439473..215e148 100644
--- a/README.md
+++ b/README.md
@@ -679,20 +679,6 @@ import { MachineTest } from "@typeonce/effect-machine/testing"
Each ESM entrypoint is independent and tree-shakeable.
-## Examples
-
-Every package directly under [`examples/`](./examples) has its own lockfile and
-`check` script.
-
-| Example | What it demonstrates |
-| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| [Playground](./examples/playground) | Five focused React examples: atomic turnstile commands, state-scoped traffic-light timers, hierarchical microwave safety, a resource-owned media player, and a worker-hosted machine synchronized across tabs |
-| [Pokémon](./examples/pokemon) | Compound workflow states, invoked child machines, typed emissions, Atom reactivity, and a live Effect service |
-| [Platformer](./examples/platformer) | Nested parallel statecharts, typed deep history, raised events, state-scoped timers, deterministic model tests, and a playable SVG adapter |
-
-The playground is the shortest path from one concept to working code. The
-standalone examples show larger composition and ownership boundaries.
-
## Reference and development
- [API reference](https://effect-machine.typeonce.dev)
diff --git a/examples/platformer/.gitignore b/examples/platformer/.gitignore
deleted file mode 100644
index de4d1f0..0000000
--- a/examples/platformer/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-dist
-node_modules
diff --git a/examples/platformer/README.md b/examples/platformer/README.md
deleted file mode 100644
index e6bc449..0000000
--- a/examples/platformer/README.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Platformer statechart example
-
-A small, standalone SVG and Vite demo in which `@typeonce/effect-machine` owns
-a platformer character's legal behavior. One compact adapter provides keyboard
-input, gravity, a floor, and visible SVG transforms.
-
-```sh
-pnpm install --frozen-lockfile
-pnpm dev
-```
-
-Run the production verification with:
-
-```sh
-pnpm check
-```
-
-## Controls
-
-- **A/D** or **arrow keys** — move
-- **W**, **up**, or **Space** — jump; press again once in the air for a double jump
-- Touch either wall and jump — turn and kick away; repeat after returning to a wall
-- **S** or **down** — duck while grounded; dive while airborne
-- **P** — pause and resume the exact playable configuration through deep history
-- **R** — reset
-
-## Statechart
-
-`Character` is parallel: `locomotion`, `facing`, and `contact` update
-independently. The locomotion region switches between `Playing` and `Paused`.
-Inside `Playing`, `Grounded` and `Airborne` are mutually exclusive. Each branch
-is compound again:
-
-```text
-Character (parallel)
-├─ locomotion
-│ ├─ Playing
-│ │ ├─ Grounded: Standing | Running | Ducking | Landing
-│ │ ├─ Airborne (parallel)
-│ │ │ ├─ motion: Jumping | Falling | Diving
-│ │ │ └─ airJump: GroundLock | WallLock | Ready | Spent
-│ │ └─ resume (deep history)
-│ └─ Paused
-├─ facing: Left | Right
-└─ contact: NoWall | LeftWall | RightWall
-```
-
-`Pause` exits `Playing`, which records its current deep configuration. Physics
-stops while `Paused`. `Resume` targets `Playing.resume`, restoring both the
-active descendants and their typed values: for example, an airborne wall jump
-returns with its `originY`, `startedAt`, `push`, jump kind, and air-jump lock.
-This is one saved configuration, not an undo stack; pausing again replaces the
-previous history. The history implementation also supplies a typed default
-`Playing` snapshot for the case where the history node is targeted before the
-region has ever been exited.
-
-State-scoped invocations follow normal statechart entry/exit semantics. Pausing
-cancels an active landing or air-jump timer, and restoring that state starts its
-invocation again. History restores state configuration and values, not elapsed
-wall-clock time or the adapter's past events.
-
-State payloads live only where they are valid: `Landing` owns impact and resume
-direction, while `Airborne` owns only the jump origin. Air-jump availability is
-modeled entirely as state: lock states own cancellable readiness timers,
-`Ready` is the only state that authorizes a double jump, and `Spent` makes a
-second one unrepresentable. Entering `Airborne` exercises a complete nested
-parallel target by selecting both `motion` and `airJump` regions.
-
-Wall contact is an independent top-level region, so the live chart can show
-`Grounded + LeftWall` at a floor corner without confusing that combination with
-an airborne wall jump. The `Grounded` handler always produces an ordinary jump;
-only `Airborne` interprets the wall sample as a wall jump. It turns and pushes
-away, refreshes the air jump through `WallLock`, and the same wall may be used
-again after physically returning to it. Movement phases own their timestamps,
-and both landing and capability locks demonstrate state-scoped
-inline `invoke: (from) => from.timer(...)` chains.
-
-Keyboard commands and physics facts share a typed `Schema.TaggedUnion`
-protocol. The adapter executes velocity and floor collision, then reports
-`ApexReached`, `Landed`, and `WallContact`. `JumpPressed` includes the current
-wall sample, but the active `Grounded` or `Airborne` branch decides its meaning.
-Typed internal events coordinate orthogonal regions: `TryAirJump` is accepted
-only by `Ready`, while `DoubleJump` and `WallJump` update motion, capability,
-and facing without shared flags. The SVG box only reflects the active snapshot;
-it never decides behavior.
-
-## Visuals
-
-The character is a few inline SVG shapes. Each state maps to one typed transform
-and body color in `src/game.ts`; after the double jump is spent, a purple accent
-persists across falling and diving. This keeps the example focused on the
-machine rather than an art or rendering pipeline.
diff --git a/examples/platformer/index.html b/examples/platformer/index.html
deleted file mode 100644
index 5315be9..0000000
--- a/examples/platformer/index.html
+++ /dev/null
@@ -1,162 +0,0 @@
-
-
-
-
-
-
- Orbit Courier — Effect Machine Platformer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PAUSED
- P resumes from deep history
-
-
-
- A D move
- W /Space jump ×2
- S duck / dive
- P pause / resume
- R reset
-
-
-
-
-
- live statechart
-
-
-
-
Character parallel
-
-
- locomotion
-
- Playing
- Paused
- resume · H*
-
-
-
-
Grounded
-
- Standing
- Running
- Ducking
- Landing
-
-
-
-
Airborne
-
parallel regions
-
-
motion
-
- Jumping
- Falling
- Diving
-
-
-
-
air jump
-
- ground lock
- wall lock
- ready
- spent
-
-
-
-
-
-
- facing
-
- Left
- Right
-
-
-
- wall contact
-
- No wall
- Left wall
- Right wall
-
-
-
-
-
-
-
-
active
- Standing · NoWall · Right
-
-
-
state-local data
- {}
-
-
-
last protocol event
- machine started
-
-
-
-
-
-
- A small adapter owns coordinates and gravity. The machine owns legal behavior, and the box simply transforms to
- show its active state. Pause exits Playing; resume targets its deep-history node and restores the
- exact grounded or airborne configuration, including state-local values.
-
-
-
-
-
diff --git a/examples/platformer/package.json b/examples/platformer/package.json
deleted file mode 100644
index b939cf3..0000000
--- a/examples/platformer/package.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "name": "@typeonce/effect-machine-example-platformer",
- "version": "0.0.0",
- "private": true,
- "description": "Playable platformer statechart example using @typeonce/effect-machine",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "test": "vitest run",
- "typecheck": "tsc --noEmit",
- "build": "pnpm typecheck && vite build",
- "preview": "vite preview",
- "check": "pnpm test && pnpm build"
- },
- "dependencies": {
- "@typeonce/effect-machine": "file:../..",
- "effect": "4.0.0-rc.111"
- },
- "devDependencies": {
- "typescript": "6.0.3",
- "vite": "8.1.5",
- "vitest": "4.1.10"
- },
- "packageManager": "pnpm@10.17.1",
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
-}
diff --git a/examples/platformer/pnpm-lock.yaml b/examples/platformer/pnpm-lock.yaml
deleted file mode 100644
index 47d7591..0000000
--- a/examples/platformer/pnpm-lock.yaml
+++ /dev/null
@@ -1,899 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-overrides:
- effect: 4.0.0-rc.111
-
-importers:
-
- .:
- dependencies:
- '@typeonce/effect-machine':
- specifier: file:../..
- version: file:../..(effect@4.0.0-rc.111)
- effect:
- specifier: 4.0.0-rc.111
- version: 4.0.0-rc.111
- devDependencies:
- typescript:
- specifier: 6.0.3
- version: 6.0.3
- vite:
- specifier: 8.1.5
- version: 8.1.5(yaml@2.9.0)
- vitest:
- specifier: 4.1.10
- version: 4.1.10(vite@8.1.5(yaml@2.9.0))
-
-packages:
-
- '@emnapi/core@1.11.1':
- resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
-
- '@emnapi/runtime@1.11.1':
- resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
-
- '@emnapi/wasi-threads@1.2.2':
- resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
-
- '@jridgewell/sourcemap-codec@1.5.5':
- resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
-
- '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4':
- resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==}
- cpu: [arm64]
- os: [darwin]
-
- '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4':
- resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==}
- cpu: [x64]
- os: [darwin]
-
- '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4':
- resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==}
- cpu: [arm64]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4':
- resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==}
- cpu: [arm]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4':
- resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==}
- cpu: [x64]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4':
- resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==}
- cpu: [x64]
- os: [win32]
-
- '@napi-rs/wasm-runtime@1.2.1':
- resolution: {integrity: sha512-KjZdi8Q1wh89gsVmghvbrMgWl6ZWmRmHV6wjB7/g4Zf0dyO+hH3neZUtuDNPO00qq5YE5RITVWvrIZKRaAmzGQ==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0}
- peerDependencies:
- '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3
- '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3
-
- '@oxc-project/types@0.139.0':
- resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==}
-
- '@rolldown/binding-android-arm64@1.1.5':
- resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [android]
-
- '@rolldown/binding-darwin-arm64@1.1.5':
- resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [darwin]
-
- '@rolldown/binding-darwin-x64@1.1.5':
- resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [darwin]
-
- '@rolldown/binding-freebsd-x64@1.1.5':
- resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [freebsd]
-
- '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
- resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm]
- os: [linux]
-
- '@rolldown/binding-linux-arm64-gnu@1.1.5':
- resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-arm64-musl@1.1.5':
- resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
- '@rolldown/binding-linux-ppc64-gnu@1.1.5':
- resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [ppc64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-s390x-gnu@1.1.5':
- resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [s390x]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-x64-gnu@1.1.5':
- resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-x64-musl@1.1.5':
- resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [linux]
- libc: [musl]
-
- '@rolldown/binding-openharmony-arm64@1.1.5':
- resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [openharmony]
-
- '@rolldown/binding-wasm32-wasi@1.1.5':
- resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [wasm32]
-
- '@rolldown/binding-win32-arm64-msvc@1.1.5':
- resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [win32]
-
- '@rolldown/binding-win32-x64-msvc@1.1.5':
- resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [win32]
-
- '@rolldown/pluginutils@1.0.1':
- resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
-
- '@standard-schema/spec@1.1.0':
- resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
-
- '@tybys/wasm-util@0.10.3':
- resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
-
- '@typeonce/effect-machine@file:../..':
- resolution: {directory: ../.., type: directory}
- engines: {node: '>=20'}
- peerDependencies:
- effect: 4.0.0-rc.111
-
- '@types/chai@5.2.3':
- resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
-
- '@types/deep-eql@4.0.2':
- resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
-
- '@types/estree@1.0.9':
- resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
-
- '@vitest/expect@4.1.10':
- resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==}
-
- '@vitest/mocker@4.1.10':
- resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==}
- peerDependencies:
- msw: ^2.4.9
- vite: ^6.0.0 || ^7.0.0 || ^8.0.0
- peerDependenciesMeta:
- msw:
- optional: true
- vite:
- optional: true
-
- '@vitest/pretty-format@4.1.10':
- resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==}
-
- '@vitest/runner@4.1.10':
- resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==}
-
- '@vitest/snapshot@4.1.10':
- resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==}
-
- '@vitest/spy@4.1.10':
- resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==}
-
- '@vitest/utils@4.1.10':
- resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==}
-
- assertion-error@2.0.1:
- resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
- engines: {node: '>=12'}
-
- chai@6.2.2:
- resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
- engines: {node: '>=18'}
-
- convert-source-map@2.0.0:
- resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
-
- detect-libc@2.1.2:
- resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
- engines: {node: '>=8'}
-
- effect@4.0.0-rc.111:
- resolution: {integrity: sha512-ASd5L58EIR0CUNueZNKKjSsyOCd+2alxOAIaTcHaqkJkPsaYSsw5Cg/cfANk5K4Jr2YsX756xvX11shzqsreWA==}
-
- es-module-lexer@2.3.1:
- resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==}
-
- estree-walker@3.0.3:
- resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
-
- expect-type@1.4.0:
- resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
- engines: {node: '>=12.0.0'}
-
- fast-check@4.9.0:
- resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==}
- engines: {node: '>=12.17.0'}
-
- fdir@6.5.0:
- resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
- engines: {node: '>=12.0.0'}
- peerDependencies:
- picomatch: ^3 || ^4
- peerDependenciesMeta:
- picomatch:
- optional: true
-
- fsevents@2.3.3:
- resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
- lightningcss-android-arm64@1.33.0:
- resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [android]
-
- lightningcss-darwin-arm64@1.33.0:
- resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [darwin]
-
- lightningcss-darwin-x64@1.33.0:
- resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [darwin]
-
- lightningcss-freebsd-x64@1.33.0:
- resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [freebsd]
-
- lightningcss-linux-arm-gnueabihf@1.33.0:
- resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm]
- os: [linux]
-
- lightningcss-linux-arm64-gnu@1.33.0:
- resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
- lightningcss-linux-arm64-musl@1.33.0:
- resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
- lightningcss-linux-x64-gnu@1.33.0:
- resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [linux]
- libc: [glibc]
-
- lightningcss-linux-x64-musl@1.33.0:
- resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [linux]
- libc: [musl]
-
- lightningcss-win32-arm64-msvc@1.33.0:
- resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [win32]
-
- lightningcss-win32-x64-msvc@1.33.0:
- resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [win32]
-
- lightningcss@1.33.0:
- resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
- engines: {node: '>= 12.0.0'}
-
- magic-string@0.30.21:
- resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
-
- msgpackr-extract@3.0.4:
- resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==}
- hasBin: true
-
- msgpackr@2.0.5:
- resolution: {integrity: sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==}
-
- nanoid@3.3.16:
- resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- node-gyp-build-optional-packages@5.2.2:
- resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
- hasBin: true
-
- obug@2.1.4:
- resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==}
- engines: {node: '>=12.20.0'}
-
- pathe@2.0.3:
- resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
-
- picocolors@1.1.1:
- resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
-
- picomatch@4.0.5:
- resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
- engines: {node: '>=12'}
-
- postcss@8.5.25:
- resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==}
- engines: {node: ^10 || ^12 || >=14}
-
- pure-rand@8.4.2:
- resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==}
-
- rolldown@1.1.5:
- resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- hasBin: true
-
- siginfo@2.0.0:
- resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
-
- source-map-js@1.2.1:
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
- engines: {node: '>=0.10.0'}
-
- stackback@0.0.2:
- resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
-
- std-env@4.2.0:
- resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
-
- tinybench@2.9.0:
- resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
-
- tinyexec@1.2.4:
- resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
- engines: {node: '>=18'}
-
- tinyglobby@0.2.17:
- resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
- engines: {node: '>=12.0.0'}
-
- tinyrainbow@3.1.1:
- resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==}
- engines: {node: '>=14.0.0'}
-
- tslib@2.8.1:
- resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
-
- typescript@6.0.3:
- resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
- engines: {node: '>=14.17'}
- hasBin: true
-
- vite@8.1.5:
- resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- hasBin: true
- peerDependencies:
- '@types/node': ^20.19.0 || >=22.12.0
- '@vitejs/devtools': ^0.3.0
- esbuild: ^0.27.0 || ^0.28.0
- jiti: '>=1.21.0'
- less: ^4.0.0
- sass: ^1.70.0
- sass-embedded: ^1.70.0
- stylus: '>=0.54.8'
- sugarss: ^5.0.0
- terser: ^5.16.0
- tsx: ^4.8.1
- yaml: ^2.4.2
- peerDependenciesMeta:
- '@types/node':
- optional: true
- '@vitejs/devtools':
- optional: true
- esbuild:
- optional: true
- jiti:
- optional: true
- less:
- optional: true
- sass:
- optional: true
- sass-embedded:
- optional: true
- stylus:
- optional: true
- sugarss:
- optional: true
- terser:
- optional: true
- tsx:
- optional: true
- yaml:
- optional: true
-
- vitest@4.1.10:
- resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==}
- engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
- hasBin: true
- peerDependencies:
- '@edge-runtime/vm': '*'
- '@opentelemetry/api': ^1.9.0
- '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
- '@vitest/browser-playwright': 4.1.10
- '@vitest/browser-preview': 4.1.10
- '@vitest/browser-webdriverio': 4.1.10
- '@vitest/coverage-istanbul': 4.1.10
- '@vitest/coverage-v8': 4.1.10
- '@vitest/ui': 4.1.10
- happy-dom: '*'
- jsdom: '*'
- vite: ^6.0.0 || ^7.0.0 || ^8.0.0
- peerDependenciesMeta:
- '@edge-runtime/vm':
- optional: true
- '@opentelemetry/api':
- optional: true
- '@types/node':
- optional: true
- '@vitest/browser-playwright':
- optional: true
- '@vitest/browser-preview':
- optional: true
- '@vitest/browser-webdriverio':
- optional: true
- '@vitest/coverage-istanbul':
- optional: true
- '@vitest/coverage-v8':
- optional: true
- '@vitest/ui':
- optional: true
- happy-dom:
- optional: true
- jsdom:
- optional: true
-
- why-is-node-running@2.3.0:
- resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
- engines: {node: '>=8'}
- hasBin: true
-
- yaml@2.9.0:
- resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
- engines: {node: '>= 14.6'}
- hasBin: true
-
-snapshots:
-
- '@emnapi/core@1.11.1':
- dependencies:
- '@emnapi/wasi-threads': 1.2.2
- tslib: 2.8.1
- optional: true
-
- '@emnapi/runtime@1.11.1':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@emnapi/wasi-threads@1.2.2':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@jridgewell/sourcemap-codec@1.5.5': {}
-
- '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4':
- optional: true
-
- '@napi-rs/wasm-runtime@1.2.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
- dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
- '@tybys/wasm-util': 0.10.3
- optional: true
-
- '@oxc-project/types@0.139.0': {}
-
- '@rolldown/binding-android-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-darwin-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-darwin-x64@1.1.5':
- optional: true
-
- '@rolldown/binding-freebsd-x64@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm64-musl@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-ppc64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-s390x-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-x64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-x64-musl@1.1.5':
- optional: true
-
- '@rolldown/binding-openharmony-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-wasm32-wasi@1.1.5':
- dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
- '@napi-rs/wasm-runtime': 1.2.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
- optional: true
-
- '@rolldown/binding-win32-arm64-msvc@1.1.5':
- optional: true
-
- '@rolldown/binding-win32-x64-msvc@1.1.5':
- optional: true
-
- '@rolldown/pluginutils@1.0.1': {}
-
- '@standard-schema/spec@1.1.0': {}
-
- '@tybys/wasm-util@0.10.3':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@typeonce/effect-machine@file:../..(effect@4.0.0-rc.111)':
- dependencies:
- effect: 4.0.0-rc.111
-
- '@types/chai@5.2.3':
- dependencies:
- '@types/deep-eql': 4.0.2
- assertion-error: 2.0.1
-
- '@types/deep-eql@4.0.2': {}
-
- '@types/estree@1.0.9': {}
-
- '@vitest/expect@4.1.10':
- dependencies:
- '@standard-schema/spec': 1.1.0
- '@types/chai': 5.2.3
- '@vitest/spy': 4.1.10
- '@vitest/utils': 4.1.10
- chai: 6.2.2
- tinyrainbow: 3.1.1
-
- '@vitest/mocker@4.1.10(vite@8.1.5(yaml@2.9.0))':
- dependencies:
- '@vitest/spy': 4.1.10
- estree-walker: 3.0.3
- magic-string: 0.30.21
- optionalDependencies:
- vite: 8.1.5(yaml@2.9.0)
-
- '@vitest/pretty-format@4.1.10':
- dependencies:
- tinyrainbow: 3.1.1
-
- '@vitest/runner@4.1.10':
- dependencies:
- '@vitest/utils': 4.1.10
- pathe: 2.0.3
-
- '@vitest/snapshot@4.1.10':
- dependencies:
- '@vitest/pretty-format': 4.1.10
- '@vitest/utils': 4.1.10
- magic-string: 0.30.21
- pathe: 2.0.3
-
- '@vitest/spy@4.1.10': {}
-
- '@vitest/utils@4.1.10':
- dependencies:
- '@vitest/pretty-format': 4.1.10
- convert-source-map: 2.0.0
- tinyrainbow: 3.1.1
-
- assertion-error@2.0.1: {}
-
- chai@6.2.2: {}
-
- convert-source-map@2.0.0: {}
-
- detect-libc@2.1.2: {}
-
- effect@4.0.0-rc.111:
- dependencies:
- '@standard-schema/spec': 1.1.0
- fast-check: 4.9.0
- msgpackr: 2.0.5
-
- es-module-lexer@2.3.1: {}
-
- estree-walker@3.0.3:
- dependencies:
- '@types/estree': 1.0.9
-
- expect-type@1.4.0: {}
-
- fast-check@4.9.0:
- dependencies:
- pure-rand: 8.4.2
-
- fdir@6.5.0(picomatch@4.0.5):
- optionalDependencies:
- picomatch: 4.0.5
-
- fsevents@2.3.3:
- optional: true
-
- lightningcss-android-arm64@1.33.0:
- optional: true
-
- lightningcss-darwin-arm64@1.33.0:
- optional: true
-
- lightningcss-darwin-x64@1.33.0:
- optional: true
-
- lightningcss-freebsd-x64@1.33.0:
- optional: true
-
- lightningcss-linux-arm-gnueabihf@1.33.0:
- optional: true
-
- lightningcss-linux-arm64-gnu@1.33.0:
- optional: true
-
- lightningcss-linux-arm64-musl@1.33.0:
- optional: true
-
- lightningcss-linux-x64-gnu@1.33.0:
- optional: true
-
- lightningcss-linux-x64-musl@1.33.0:
- optional: true
-
- lightningcss-win32-arm64-msvc@1.33.0:
- optional: true
-
- lightningcss-win32-x64-msvc@1.33.0:
- optional: true
-
- lightningcss@1.33.0:
- dependencies:
- detect-libc: 2.1.2
- optionalDependencies:
- lightningcss-android-arm64: 1.33.0
- lightningcss-darwin-arm64: 1.33.0
- lightningcss-darwin-x64: 1.33.0
- lightningcss-freebsd-x64: 1.33.0
- lightningcss-linux-arm-gnueabihf: 1.33.0
- lightningcss-linux-arm64-gnu: 1.33.0
- lightningcss-linux-arm64-musl: 1.33.0
- lightningcss-linux-x64-gnu: 1.33.0
- lightningcss-linux-x64-musl: 1.33.0
- lightningcss-win32-arm64-msvc: 1.33.0
- lightningcss-win32-x64-msvc: 1.33.0
-
- magic-string@0.30.21:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.5.5
-
- msgpackr-extract@3.0.4:
- dependencies:
- node-gyp-build-optional-packages: 5.2.2
- optionalDependencies:
- '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4
- optional: true
-
- msgpackr@2.0.5:
- optionalDependencies:
- msgpackr-extract: 3.0.4
-
- nanoid@3.3.16: {}
-
- node-gyp-build-optional-packages@5.2.2:
- dependencies:
- detect-libc: 2.1.2
- optional: true
-
- obug@2.1.4: {}
-
- pathe@2.0.3: {}
-
- picocolors@1.1.1: {}
-
- picomatch@4.0.5: {}
-
- postcss@8.5.25:
- dependencies:
- nanoid: 3.3.16
- picocolors: 1.1.1
- source-map-js: 1.2.1
-
- pure-rand@8.4.2: {}
-
- rolldown@1.1.5:
- dependencies:
- '@oxc-project/types': 0.139.0
- '@rolldown/pluginutils': 1.0.1
- optionalDependencies:
- '@rolldown/binding-android-arm64': 1.1.5
- '@rolldown/binding-darwin-arm64': 1.1.5
- '@rolldown/binding-darwin-x64': 1.1.5
- '@rolldown/binding-freebsd-x64': 1.1.5
- '@rolldown/binding-linux-arm-gnueabihf': 1.1.5
- '@rolldown/binding-linux-arm64-gnu': 1.1.5
- '@rolldown/binding-linux-arm64-musl': 1.1.5
- '@rolldown/binding-linux-ppc64-gnu': 1.1.5
- '@rolldown/binding-linux-s390x-gnu': 1.1.5
- '@rolldown/binding-linux-x64-gnu': 1.1.5
- '@rolldown/binding-linux-x64-musl': 1.1.5
- '@rolldown/binding-openharmony-arm64': 1.1.5
- '@rolldown/binding-wasm32-wasi': 1.1.5
- '@rolldown/binding-win32-arm64-msvc': 1.1.5
- '@rolldown/binding-win32-x64-msvc': 1.1.5
-
- siginfo@2.0.0: {}
-
- source-map-js@1.2.1: {}
-
- stackback@0.0.2: {}
-
- std-env@4.2.0: {}
-
- tinybench@2.9.0: {}
-
- tinyexec@1.2.4: {}
-
- tinyglobby@0.2.17:
- dependencies:
- fdir: 6.5.0(picomatch@4.0.5)
- picomatch: 4.0.5
-
- tinyrainbow@3.1.1: {}
-
- tslib@2.8.1:
- optional: true
-
- typescript@6.0.3: {}
-
- vite@8.1.5(yaml@2.9.0):
- dependencies:
- lightningcss: 1.33.0
- picomatch: 4.0.5
- postcss: 8.5.25
- rolldown: 1.1.5
- tinyglobby: 0.2.17
- optionalDependencies:
- fsevents: 2.3.3
- yaml: 2.9.0
-
- vitest@4.1.10(vite@8.1.5(yaml@2.9.0)):
- dependencies:
- '@vitest/expect': 4.1.10
- '@vitest/mocker': 4.1.10(vite@8.1.5(yaml@2.9.0))
- '@vitest/pretty-format': 4.1.10
- '@vitest/runner': 4.1.10
- '@vitest/snapshot': 4.1.10
- '@vitest/spy': 4.1.10
- '@vitest/utils': 4.1.10
- es-module-lexer: 2.3.1
- expect-type: 1.4.0
- magic-string: 0.30.21
- obug: 2.1.4
- pathe: 2.0.3
- picomatch: 4.0.5
- std-env: 4.2.0
- tinybench: 2.9.0
- tinyexec: 1.2.4
- tinyglobby: 0.2.17
- tinyrainbow: 3.1.1
- vite: 8.1.5(yaml@2.9.0)
- why-is-node-running: 2.3.0
- transitivePeerDependencies:
- - msw
-
- why-is-node-running@2.3.0:
- dependencies:
- siginfo: 2.0.0
- stackback: 0.0.2
-
- yaml@2.9.0:
- optional: true
diff --git a/examples/platformer/pnpm-workspace.yaml b/examples/platformer/pnpm-workspace.yaml
deleted file mode 100644
index e9d7426..0000000
--- a/examples/platformer/pnpm-workspace.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-packages:
- - "."
-
-overrides:
- effect: 4.0.0-rc.111
-
-minimumReleaseAge: 1440
-minimumReleaseAgeExclude:
- - effect
- - vite@8.1.5
diff --git a/examples/platformer/src/game.ts b/examples/platformer/src/game.ts
deleted file mode 100644
index b6544db..0000000
--- a/examples/platformer/src/game.ts
+++ /dev/null
@@ -1,181 +0,0 @@
-import type { Axis, CharacterEvent, CharacterSnapshot, LocomotionMode } from "./machine.ts"
-import { airJumpMode, facingDirection, isPaused, locomotionState } from "./machine.ts"
-
-const FLOOR = 324
-const SIZE = 30
-
-const pose = {
- Standing: "translate(0 0)",
- Running: "rotate(6 15 30)",
- Jumping: "rotate(-12 15 15)",
- Falling: "rotate(12 15 15)",
- Ducking: "translate(0 15) scale(1 .5)",
- Diving: "rotate(90 15 15) scale(.8 1.15)",
- Landing: "translate(-3 12) scale(1.2 .6)",
- Paused: "translate(0 0)"
-} as const satisfies Record
-
-export class GameAdapter {
- private readonly position = { x: 84, y: FLOOR - SIZE, vy: 0 }
- private readonly held = new Set()
- private snapshot: CharacterSnapshot | undefined
- private previousMode: LocomotionMode = "Standing"
- private lastJumpAt = -1
- private wallPushSpeed = 0
- private wallPushUntil = 0
- private reportedWall: Axis | undefined
- private grounded = true
- private apexReported = false
-
- constructor(
- private readonly send: (event: CharacterEvent) => void,
- private readonly player: SVGGElement,
- private readonly playerPose: SVGGElement
- ) {
- window.addEventListener("keydown", this.onKeyDown)
- window.addEventListener("keyup", this.onKeyUp)
- }
-
- setSnapshot(snapshot: CharacterSnapshot) {
- this.snapshot = snapshot
- }
-
- step(seconds: number) {
- if (this.snapshot === undefined) return
- const dt = Math.min(seconds, 1 / 30)
- const state = locomotionState(this.snapshot)
-
- if (state._tag === "Paused") {
- this.player.dataset.paused = "true"
- return
- }
-
- delete this.player.dataset.paused
- const mode = state._tag
- const p = this.position
-
- if (state._tag === "Jumping" && state.startedAt !== this.lastJumpAt) {
- p.vy = -430
- this.wallPushSpeed = state.push * 330
- this.wallPushUntil = state.push === 0 ? 0 : state.startedAt + 240
- this.grounded = false
- this.apexReported = false
- this.lastJumpAt = state.startedAt
- }
- if (mode === "Diving" && this.previousMode !== "Diving") p.vy = 610
-
- const inputSpeed = mode === "Ducking" || mode === "Landing" ? 0 : this.axis * (this.grounded ? 190 : 135)
- const speed = performance.now() < this.wallPushUntil ? this.wallPushSpeed : inputSpeed
- if (!this.grounded) p.vy += 1_180 * dt
- p.x = Math.max(0, Math.min(640 - SIZE, p.x + speed * dt))
- p.y += p.vy * dt
-
- if (this.jumpWall !== this.reportedWall) {
- this.reportedWall = this.jumpWall
- this.send({ _tag: "WallContact", wall: this.reportedWall })
- }
-
- if (!this.apexReported && p.vy >= 0 && mode === "Jumping") {
- this.apexReported = true
- this.send({ _tag: "ApexReached", y: Math.round(p.y) })
- }
- if (!this.grounded && p.y >= FLOOR - SIZE) {
- const impact = Math.round(p.vy)
- Object.assign(p, { y: FLOOR - SIZE, vy: 0 })
- this.grounded = true
- this.send({ _tag: "Landed", impact, axis: this.axis, at: performance.now() })
- }
-
- const x = facingDirection(this.snapshot) === "Left" ? p.x + SIZE : p.x
- const flip = facingDirection(this.snapshot) === "Left" ? " scale(-1 1)" : ""
- this.player.setAttribute("transform", `translate(${x.toFixed(1)} ${p.y.toFixed(1)})${flip}`)
- this.playerPose.setAttribute("transform", pose[mode])
- this.player.dataset.mode = mode
- this.player.dataset.airJump = airJumpMode(this.snapshot) === "AirJumpSpent" ? "spent" : "ready"
- if (state._tag === "Jumping") this.player.dataset.jumpKind = state.kind
- else delete this.player.dataset.jumpKind
- this.previousMode = mode
- }
-
- reset() {
- Object.assign(this.position, { x: 84, y: FLOOR - SIZE, vy: 0 })
- this.held.clear()
- this.grounded = true
- this.previousMode = "Standing"
- this.lastJumpAt = -1
- this.wallPushUntil = 0
- this.reportedWall = undefined
- }
-
- destroy() {
- window.removeEventListener("keydown", this.onKeyDown)
- window.removeEventListener("keyup", this.onKeyUp)
- }
-
- private get axis(): Axis {
- const left = this.held.has("KeyA") || this.held.has("ArrowLeft")
- const right = this.held.has("KeyD") || this.held.has("ArrowRight")
- return left === right ? 0 : left ? -1 : 1
- }
-
- private get wall(): Axis {
- return this.position.x <= 1 ? -1 : this.position.x >= 640 - SIZE - 1 ? 1 : 0
- }
-
- private get jumpWall(): Axis {
- return performance.now() < this.wallPushUntil ? 0 : this.wall
- }
-
- private readonly onKeyDown = (event: KeyboardEvent) => {
- if (!this.isGameKey(event.code)) return
- event.preventDefault()
-
- if (event.code === "KeyP") {
- if (event.repeat || this.snapshot === undefined) return
- this.send(isPaused(this.snapshot) ? { _tag: "Resume" } : { _tag: "Pause", at: performance.now() })
- return
- }
-
- if (this.snapshot !== undefined && isPaused(this.snapshot) && event.code !== "KeyR") return
-
- const previousAxis = this.axis
- this.held.add(event.code)
- if (previousAxis !== this.axis) this.send({ _tag: "Move", axis: this.axis, at: performance.now() })
- if (event.repeat) return
-
- if (event.code === "Space" || event.code === "KeyW" || event.code === "ArrowUp") {
- this.send({ _tag: "JumpPressed", at: performance.now(), y: this.position.y, wall: this.jumpWall })
- } else if (event.code === "KeyS" || event.code === "ArrowDown") {
- this.send({ _tag: "DownPressed", at: performance.now() })
- } else if (event.code === "KeyR") {
- this.reset()
- this.send({ _tag: "Reset" })
- }
- }
-
- private readonly onKeyUp = (event: KeyboardEvent) => {
- const previousAxis = this.axis
- this.held.delete(event.code)
- if (this.snapshot !== undefined && isPaused(this.snapshot)) return
- if (previousAxis !== this.axis) this.send({ _tag: "Move", axis: this.axis, at: performance.now() })
- if (event.code === "KeyS" || event.code === "ArrowDown") {
- this.send({ _tag: "DownReleased", axis: this.axis, at: performance.now() })
- }
- }
-
- private isGameKey(code: string) {
- return [
- "KeyA",
- "KeyD",
- "KeyW",
- "KeyS",
- "KeyP",
- "KeyR",
- "Space",
- "ArrowLeft",
- "ArrowRight",
- "ArrowUp",
- "ArrowDown"
- ].includes(code)
- }
-}
diff --git a/examples/platformer/src/machine.test.ts b/examples/platformer/src/machine.test.ts
deleted file mode 100644
index 502ceeb..0000000
--- a/examples/platformer/src/machine.test.ts
+++ /dev/null
@@ -1,310 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { MachineTest } from "@typeonce/effect-machine/testing"
-import { Effect } from "effect"
-import { describe, expect, it } from "vitest"
-import { makeMermaidRenderer } from "../../../test/machine/visualization/mermaid.ts"
-import { makeTextRenderer } from "../../../test/machine/visualization/text.ts"
-import {
- airJumpMode,
- type CharacterEvent,
- CharacterEvents,
- CharacterMachine,
- type CharacterSnapshot,
- facingDirection,
- locomotionMode,
- wallContact
-} from "./machine.ts"
-
-const renderMachine = makeTextRenderer(Machine)
-const renderMermaid = makeMermaidRenderer(Machine)
-
-const playingSnapshot = (snapshot: CharacterSnapshot) => {
- const locomotion = snapshot.states.locomotion.state
- if (locomotion.path !== "Character.locomotion.Playing") {
- throw new Error(`Expected Playing, received ${locomotion.path}`)
- }
- return locomotion
-}
-
-const runPlan = (effect: Effect.Effect ) =>
- Effect.runPromise(effect as unknown as Effect.Effect )
-
-const same = (left: unknown, right: unknown) => JSON.stringify(left) === JSON.stringify(right)
-
-const invariant = MachineTest.invariants(CharacterMachine)
-
-const pauseAndResumePreserveOrthogonalRegions = invariant.step(
- "pause and resume only replace the locomotion region",
- ({ after, before, event }) =>
- event._tag !== "Pause" && event._tag !== "Resume" ||
- same(before.states.facing, after.states.facing) && same(before.states.contact, after.states.contact) ||
- `${event._tag} changed facing or wall contact`
-)
-
-const resetIsCanonical = invariant.step(
- "reset restores the canonical character configuration",
- ({ after, event }) =>
- event._tag !== "Reset" ||
- locomotionMode(after) === "Standing" &&
- facingDirection(after) === "Right" &&
- wallContact(after) === "NoWall" ||
- "reset did not restore Standing + Right + NoWall"
-)
-
-const wallJumpFacesItsPush = invariant.state(
- "wall jumps face and push away from the sampled wall",
- ({ snapshot }) => {
- const locomotion = snapshot.states.locomotion.state
- if (
- locomotion.path !== "Character.locomotion.Playing" ||
- locomotion.state.path !== "Character.locomotion.Playing.Airborne" ||
- locomotion.state.states.motion.state.path !== "Character.locomotion.Playing.Airborne.motion.Jumping" ||
- locomotion.state.states.motion.state.value.kind !== "Wall"
- ) return true
-
- const push = locomotion.state.states.motion.state.value.push
- const facing = facingDirection(snapshot)
- return push === 1 && facing === "Right" || push === -1 && facing === "Left" ||
- `wall jump push ${push} disagrees with facing ${facing}`
- }
-)
-
-const resumeRestoresDeepHistory = invariant.trace(
- "resume restores the exact locomotion snapshot captured by pause",
- ({ trace }) => {
- for (const step of trace.steps) {
- if (
- step.event._tag !== "Resume" ||
- step.before.states.locomotion.state.path !== "Character.locomotion.Paused" ||
- step.after.states.locomotion.state.path !== "Character.locomotion.Playing"
- ) continue
-
- const pause = trace.steps
- .slice(0, step.index)
- .reverse()
- .find((candidate) =>
- candidate.event._tag === "Pause" &&
- candidate.before.states.locomotion.state.path === "Character.locomotion.Playing" &&
- candidate.after.states.locomotion.state.path === "Character.locomotion.Paused"
- )
- if (pause === undefined) return "resume had no matching pause transition"
- if (!same(pause.before.states.locomotion.state, step.after.states.locomotion.state)) {
- return "resume changed the deep locomotion state captured by pause"
- }
- }
- return true
- }
-)
-
-const laws = [
- pauseAndResumePreserveOrthogonalRegions,
- resetIsCanonical,
- wallJumpFacesItsPush,
- resumeRestoresDeepHistory
-]
-
-// Exploration scenarios retain decoded events for trace inspection.
-const EventValue = {
- Resume: (): CharacterEvent => ({ _tag: "Resume" }),
- Pause: (fields: { readonly at: number }): CharacterEvent => ({ _tag: "Pause", ...fields }),
- Reset: (): CharacterEvent => ({ _tag: "Reset" }),
- JumpPressed: (
- fields: { readonly at: number; readonly y: number; readonly wall: -1 | 0 | 1 }
- ) => ({ _tag: "JumpPressed", ...fields } as const),
- Landed: (
- fields: { readonly impact: number; readonly axis: -1 | 0 | 1; readonly at: number }
- ) => ({ _tag: "Landed", ...fields } as const),
- ApexReached: (fields: { readonly y: number }) => ({ _tag: "ApexReached", ...fields } as const),
- DownPressed: (fields: { readonly at: number }) => ({ _tag: "DownPressed", ...fields } as const)
-}
-
-const explorationEvents = ({ snapshot }: MachineTest.ExplorationStateContext) => {
- const locomotion = snapshot.states.locomotion.state
- if (locomotion.path === "Character.locomotion.Paused") {
- return [EventValue.Resume()]
- }
-
- const pauseAndReset = [EventValue.Pause({ at: 70 }), EventValue.Reset()]
- if (locomotion.state.path === "Character.locomotion.Playing.Grounded") {
- return [
- EventValue.JumpPressed({ at: 20, y: 100, wall: -1 }),
- EventValue.JumpPressed({ at: 20, y: 100, wall: 0 }),
- EventValue.JumpPressed({ at: 20, y: 100, wall: 1 }),
- ...pauseAndReset
- ]
- }
-
- const motion = locomotion.state.states.motion.state
- const airborne = [
- EventValue.JumpPressed({ at: 30, y: 80, wall: -1 }),
- EventValue.JumpPressed({ at: 30, y: 80, wall: 1 }),
- EventValue.Landed({ impact: 12, axis: 0, at: 60 }),
- ...pauseAndReset
- ]
- return motion.path === "Character.locomotion.Playing.Airborne.motion.Jumping"
- ? [
- EventValue.ApexReached({ y: 50 }),
- EventValue.DownPressed({ at: 40 }),
- ...airborne
- ]
- : motion.path === "Character.locomotion.Playing.Airborne.motion.Falling"
- ? [EventValue.DownPressed({ at: 40 }), ...airborne]
- : airborne
-}
-
-describe("platformer history integration", () => {
- it("renders registered and candidate events separately from states", async () => {
- await runPlan(Effect.gen(function*() {
- const initial = yield* Machine.planInitial(CharacterMachine)
- const definitions = Machine.transitionDefinitions(CharacterMachine)
- const rendered = renderMachine(CharacterMachine, initial.state)
- const mermaid = renderMermaid(CharacterMachine, initial.state)
-
- expect(definitions).toHaveLength(28)
- expect(definitions).toContainEqual({
- source: "Character.locomotion.Playing.Airborne.airJump",
- trigger: { type: "event", event: "WallJump" },
- reenter: true,
- acceptance: "required",
- branches: [{
- type: "direct",
- target: "Character.locomotion.Playing.Airborne.airJump.AirJumpWallLock",
- selection: {
- path: "Character.locomotion.Playing.Airborne.airJump.AirJumpWallLock",
- kind: "state",
- scope: "local"
- }
- }]
- })
- expect(definitions.every(({ branches }) => branches.length > 0)).toBe(true)
- expect(rendered).toContain("◇ on: WallJump [reenter]")
- expect(rendered).toContain("└┄ → AirJumpWallLock")
- expect(rendered).toContain(
- "Candidate events: Move, DownPressed, JumpPressed, Pause, WallJump, WallContact, Reset"
- )
- expect(rendered).not.toContain("Observed event samples")
- expect(mermaid).toMatch(/^stateDiagram-v2\n direction LR/)
- expect(mermaid).toContain("state_13 --> state_15: WallJump [reenter]")
- expect(mermaid).toContain("state_23 --> state_25: WallContact [Left wall]")
- expect(mermaid).toContain("state_23 --> state_24: WallContact [No wall]")
- expect(mermaid).not.toContain("∅")
- }))
- })
-
- it("resumes the exact grounded leaf and its state-local value", async () => {
- await runPlan(Effect.gen(function*() {
- const initial = yield* Machine.planInitial(CharacterMachine)
- const ducking = yield* Machine.plan(
- CharacterMachine,
- initial.state,
- CharacterEvents.DownPressed({ at: 10 })
- )
- const beforePause = playingSnapshot(ducking.next)
-
- const paused = yield* Machine.plan(
- CharacterMachine,
- ducking.next,
- CharacterEvents.Pause({ at: 20 })
- )
- const pausedLocomotion = paused.next.states.locomotion.state
- expect(pausedLocomotion.path).toBe("Character.locomotion.Paused")
- expect(pausedLocomotion.value).toEqual({ _tag: "Paused", pausedAt: 20 })
-
- const resumed = yield* Machine.plan(CharacterMachine, paused.next, CharacterEvents.Resume())
- expect(playingSnapshot(resumed.next)).toEqual(beforePause)
- expect(resumed.next.states.facing.state.path).toBe("Character.facing.Right")
- expect(resumed.next.states.contact.state.path).toBe("Character.contact.NoWall")
- }))
- })
-
- it("resumes both airborne parallel regions and all nested values", async () => {
- await runPlan(Effect.gen(function*() {
- const initial = yield* Machine.planInitial(CharacterMachine)
- const airborne = yield* Machine.plan(
- CharacterMachine,
- initial.state,
- CharacterEvents.JumpPressed({ at: 100, y: 207, wall: -1 })
- )
- const falling = yield* Machine.plan(
- CharacterMachine,
- airborne.next,
- CharacterEvents.ApexReached({ y: 91 })
- )
- const beforePause = playingSnapshot(falling.next)
-
- const paused = yield* Machine.plan(
- CharacterMachine,
- falling.next,
- CharacterEvents.Pause({ at: 180 })
- )
- expect(paused.next.history?.["Character.locomotion.Playing.resume"]?.active).toEqual([
- "Character",
- "Character.locomotion",
- "Character.locomotion.Playing",
- "Character.locomotion.Playing.Airborne",
- "Character.locomotion.Playing.Airborne.motion",
- "Character.locomotion.Playing.Airborne.motion.Falling",
- "Character.locomotion.Playing.Airborne.airJump",
- "Character.locomotion.Playing.Airborne.airJump.AirJumpGroundLock"
- ])
-
- const resumed = yield* Machine.plan(CharacterMachine, paused.next, CharacterEvents.Resume())
- const restored = playingSnapshot(resumed.next)
- expect(restored).toEqual(beforePause)
-
- if (restored.state.path !== "Character.locomotion.Playing.Airborne") {
- throw new Error(`Expected Airborne, received ${restored.state.path}`)
- }
- expect(restored.state.value).toEqual({ _tag: "Airborne", originY: 207 })
- expect(restored.state.states.motion.state.value).toEqual({ _tag: "Falling", apexY: 91 })
- expect(restored.state.states.airJump.state.path).toBe(
- "Character.locomotion.Playing.Airborne.airJump.AirJumpGroundLock"
- )
- }))
- })
-})
-
-describe("platformer semantic exploration", () => {
- it("checks history and orthogonal-region laws across the public state space", async () => {
- await runPlan(Effect.gen(function*() {
- const explored = yield* MachineTest.explore(CharacterMachine, {
- events: explorationEvents,
- stateKey: ({ snapshot }) => JSON.stringify(snapshot),
- invariants: laws
- })
-
- expect(explored.completeness).toEqual({ _tag: "Complete" })
-
- const wallJump = yield* MachineTest.assertReachable(
- explored,
- "an airborne wall jump",
- ({ snapshot }) => {
- const locomotion = snapshot.states.locomotion.state
- return locomotion.path === "Character.locomotion.Playing" &&
- locomotion.state.path === "Character.locomotion.Playing.Airborne" &&
- locomotion.state.states.motion.state.path ===
- "Character.locomotion.Playing.Airborne.motion.Jumping" &&
- locomotion.state.states.motion.state.value.kind === "Wall"
- }
- )
- expect(wallJump.trace.scenario.events).toHaveLength(2)
-
- const pausedAirborne = yield* MachineTest.assertReachable(
- explored,
- "paused airborne deep history",
- ({ snapshot }) =>
- snapshot.states.locomotion.state.path === "Character.locomotion.Paused" &&
- snapshot.history?.["Character.locomotion.Playing.resume"]?.active.includes(
- "Character.locomotion.Playing.Airborne"
- ) === true
- )
- expect(pausedAirborne.trace.scenario.events).toHaveLength(2)
-
- yield* MachineTest.assertUnreachable(
- explored,
- "AirJumpReady without the internal timer event",
- ({ snapshot }) => airJumpMode(snapshot) === "AirJumpReady"
- )
- }))
- })
-})
diff --git a/examples/platformer/src/machine.ts b/examples/platformer/src/machine.ts
deleted file mode 100644
index d9b2147..0000000
--- a/examples/platformer/src/machine.ts
+++ /dev/null
@@ -1,492 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Schema } from "effect"
-
-// Domain schemas are shared by state payloads and the public physics protocol.
-export const Axis = Schema.Literals([-1, 0, 1])
-export type Axis = typeof Axis.Type
-const JumpKind = Schema.Literals(["Ground", "Double", "Wall"])
-
-const State = Schema.TaggedUnion({
- Paused: { pausedAt: Schema.Number },
- Running: { startedAt: Schema.Number },
- Ducking: { startedAt: Schema.Number },
- Landing: { impact: Schema.Number, resumeAxis: Axis, landedAt: Schema.Number },
- Airborne: { originY: Schema.Number },
- Jumping: { startedAt: Schema.Number, push: Axis, kind: JumpKind },
- Falling: { apexY: Schema.Number },
- Diving: { startedAt: Schema.Number }
-})
-
-// Inputs and physics facts are one runtime-decoded, statically typed protocol.
-export const CharacterEvents = Machine.events(
- Schema.TaggedUnion({
- Move: { axis: Axis, at: Schema.Number },
- JumpPressed: { at: Schema.Number, y: Schema.Number, wall: Axis },
- WallContact: { wall: Axis },
- DownPressed: { at: Schema.Number },
- DownReleased: { axis: Axis, at: Schema.Number },
- ApexReached: { y: Schema.Number },
- Landed: { impact: Schema.Number, axis: Axis, at: Schema.Number },
- Pause: { at: Schema.Number },
- Resume: {},
- Reset: {}
- })
-)
-
-const InternalEvents = Machine.internalEvents(
- Schema.TaggedUnion({
- LandingSettled: {},
- AirJumpUnlocked: {},
- TryAirJump: { at: Schema.Number },
- DoubleJump: { at: Schema.Number },
- WallJump: { at: Schema.Number, push: Axis }
- })
-)
-
-const awayFrom = (wall: Axis): Axis => (wall === -1 ? 1 : wall === 1 ? -1 : 0)
-
-export const CharacterStates = Machine.states({
- Character: {
- type: "parallel",
- states: {
- locomotion: {
- initial: "Playing",
- states: {
- Playing: {
- initial: "Grounded",
- states: {
- Grounded: {
- initial: "Standing",
- states: {
- Standing: {},
- Running: State.cases.Running,
- Ducking: State.cases.Ducking,
- Landing: State.cases.Landing
- }
- },
- Airborne: {
- schema: State.cases.Airborne,
- type: "parallel",
- states: {
- motion: {
- initial: "Jumping",
- states: {
- Jumping: State.cases.Jumping,
- Falling: State.cases.Falling,
- Diving: State.cases.Diving
- }
- },
- airJump: {
- initial: "AirJumpGroundLock",
- states: {
- AirJumpGroundLock: {},
- AirJumpWallLock: {},
- AirJumpReady: {},
- AirJumpSpent: {}
- }
- }
- }
- },
- resume: {
- type: "history",
- history: "deep"
- }
- }
- },
- Paused: State.cases.Paused
- }
- },
- facing: {
- initial: "Right",
- states: {
- Left: {},
- Right: {}
- }
- },
- contact: {
- initial: "NoWall",
- states: {
- NoWall: {},
- LeftWall: {},
- RightWall: {}
- }
- }
- }
- }
-})
-
-export const CharacterMachine = Machine.make({
- id: "PlatformerCharacter",
- states: CharacterStates.states,
- events: CharacterEvents,
- internalEvents: InternalEvents,
- initial: (to) =>
- to.Character.initial.resolve(({ target }) =>
- target.from((character) =>
- character
- .locomotion.from((locomotion) =>
- locomotion.Playing.from((playing) => playing.Grounded.from((grounded) => grounded.Standing.from()))
- )
- .facing.from((facing) => facing.Right.from())
- .contact.from((contact) => contact.NoWall.from())
- )
- )
-}).handle({
- Character: {
- on: {
- Reset: (to) =>
- to.full.Character().resolve(({ target }) =>
- target.from((character) =>
- character
- .locomotion.from((locomotion) =>
- locomotion.Playing.from((playing) => playing.Grounded.from((grounded) => grounded.Standing.from()))
- )
- .facing.from((facing) => facing.Right.from())
- .contact.from((contact) => contact.NoWall.from())
- )
- )
- },
- states: {
- locomotion: {
- states: {
- Playing: {
- history: {
- resume: {
- default: ({ target }) =>
- target.Character.from((character) =>
- character
- .locomotion.from((locomotion) =>
- locomotion.Playing.from((playing) =>
- playing.Grounded.from((grounded) => grounded.Standing.from())
- )
- )
- .facing.from((facing) => facing.Right.from())
- .contact.from((contact) => contact.NoWall.from())
- )
- }
- },
- on: {
- Pause: (to) =>
- to.branch.Character.locomotion.Paused().resolve(({ event, target }) =>
- target.from({ pausedAt: event.at })
- )
- },
- states: {
- Grounded: {
- on: {
- JumpPressed: (to) =>
- to.full.Character().resolve(({ event, target }) =>
- target.from((character) =>
- character
- .locomotion.from((locomotion) =>
- locomotion.Playing.from((playing) =>
- playing.Airborne.from({ originY: event.y }, (airborne) =>
- airborne
- .motion.from((motion) =>
- motion.Jumping.from({ startedAt: event.at, push: 0, kind: "Ground" })
- )
- .airJump.from((airJump) => airJump.AirJumpGroundLock.from()))
- )
- )
- .facing.from((facing) => facing.Right.from())
- .contact.from((contact) =>
- event.wall === -1
- ? contact.LeftWall.from()
- : event.wall === 1
- ? contact.RightWall.from()
- : contact.NoWall.from()
- )
- )
- )
- },
- states: {
- Standing: {
- on: {
- Move: (to) =>
- to.branches({
- moving: { target: to.local.Running() },
- unchanged: { target: to.none }
- }).resolve(({ event, select }) =>
- event.axis === 0
- ? select.unchanged()
- : select.moving.from({ startedAt: event.at })
- ),
- DownPressed: (to) =>
- to.local.Ducking().resolve(({ event, target }) => target.from({ startedAt: event.at }))
- }
- },
- Running: {
- on: {
- Move: (to) =>
- to.branches({
- stopped: { target: to.local.Standing() },
- unchanged: { target: to.none }
- }).resolve(({ event, select }) =>
- event.axis === 0
- ? select.stopped.from()
- : select.unchanged()
- ),
- DownPressed: (to) =>
- to.local.Ducking().resolve(({ event, target }) => target.from({ startedAt: event.at }))
- }
- },
- Ducking: {
- on: {
- DownReleased: (to) =>
- to.branches({
- stopped: { target: to.local.Standing() },
- running: { target: to.local.Running() }
- }).resolve(({ event, select }) =>
- event.axis === 0
- ? select.stopped.from()
- : select.running.from({ startedAt: event.at })
- )
- }
- },
- Landing: {
- invoke: (from) =>
- from.timer("landing-settle", "140 millis").onDone((to) =>
- to.none.resolve((_, enqueue) => {
- enqueue.raise(InternalEvents.LandingSettled())
- return undefined
- })
- ),
- on: {
- LandingSettled: (to) =>
- to.branches({
- stopped: { target: to.local.Standing() },
- running: { target: to.local.Running() }
- }).resolve(({ state, select }) =>
- state.resumeAxis === 0
- ? select.stopped.from()
- : select.running.from({ startedAt: state.landedAt + 140 })
- ),
- Move: (to) =>
- to.local.Landing().resolve(({ event, state, target }) => {
- const { _tag: _, ...fields } = state
- return target.from({ ...fields, resumeAxis: event.axis })
- })
- }
- }
- }
- },
- Airborne: {
- on: {
- JumpPressed: (to) =>
- to.none.resolve(({ event }, enqueue) => {
- const push = awayFrom(event.wall)
- enqueue.raise(
- push === 0
- ? InternalEvents.TryAirJump({ at: event.at })
- : InternalEvents.WallJump({ at: event.at, push })
- )
- return undefined
- }),
- Landed: (to) =>
- to.branch.Character.locomotion.Playing.Grounded().resolve(({ event, target }) =>
- target.from((grounded) =>
- grounded.Landing.from({
- impact: event.impact,
- resumeAxis: event.axis,
- landedAt: event.at
- })
- )
- )
- },
- states: {
- motion: {
- on: {
- DoubleJump: (to) =>
- to.local.Jumping().resolve(({ event, target }) =>
- target.from({ startedAt: event.at, push: 0, kind: "Double" })
- ),
- WallJump: (to) =>
- to.local.Jumping().resolve(({ event, target }) =>
- target.from({ startedAt: event.at, push: event.push, kind: "Wall" })
- )
- },
- states: {
- Jumping: {
- on: {
- ApexReached: (to) =>
- to.local.Falling().resolve(({ event, target }) => target.from({ apexY: event.y })),
- DownPressed: (to) =>
- to.local.Diving().resolve(({ event, target }) => target.from({ startedAt: event.at }))
- }
- },
- Falling: {
- on: {
- DownPressed: (to) =>
- to.local.Diving().resolve(({ event, target }) => target.from({ startedAt: event.at }))
- }
- },
- Diving: {}
- }
- },
- airJump: {
- on: {
- WallJump: (to) =>
- to.local.AirJumpWallLock().resolve(({ target }) => target.from(), { reenter: true })
- },
- states: {
- AirJumpGroundLock: {
- invoke: (from) =>
- from.timer("ground-air-jump-unlock", "120 millis").onDone((to) => to.local.AirJumpReady()),
- on: {}
- },
- AirJumpWallLock: {
- invoke: (from) =>
- from.timer("wall-air-jump-unlock", "240 millis").onDone((to) => to.local.AirJumpReady()),
- on: {}
- },
- AirJumpReady: {
- on: {
- TryAirJump: (to) =>
- to.local.AirJumpSpent().resolve(({ event, target }, enqueue) => {
- enqueue.raise(InternalEvents.DoubleJump({ at: event.at }))
- return target.from()
- })
- }
- },
- AirJumpSpent: {}
- }
- }
- }
- }
- }
- },
- Paused: {
- on: {
- Resume: (to) => to.history.Character.locomotion.Playing.resume
- }
- }
- }
- },
- facing: {
- states: {
- Left: {
- on: {
- Move: (to) =>
- to.branches({ right: { target: to.local.Right() }, unchanged: { target: to.none } }).resolve((
- { event, select }
- ) => event.axis === 1 ? select.right.from() : select.unchanged()),
- WallJump: (to) =>
- to.branches({ right: { target: to.local.Right() }, unchanged: { target: to.none } }).resolve((
- { event, select }
- ) => event.push === 1 ? select.right.from() : select.unchanged())
- }
- },
- Right: {
- on: {
- Move: (to) =>
- to.branches({ left: { target: to.local.Left() }, unchanged: { target: to.none } }).resolve((
- { event, select }
- ) => event.axis === -1 ? select.left.from() : select.unchanged()),
- WallJump: (to) =>
- to.branches({ left: { target: to.local.Left() }, unchanged: { target: to.none } }).resolve((
- { event, select }
- ) => event.push === -1 ? select.left.from() : select.unchanged())
- }
- }
- }
- },
- contact: {
- on: {
- WallContact: (to) =>
- to.branches({
- leftWall: { title: "Left wall", target: to.local.LeftWall() },
- rightWall: { title: "Right wall", target: to.local.RightWall() },
- noWall: { title: "No wall", target: to.local.NoWall() }
- }).resolve(({ event, select }) =>
- event.wall === -1
- ? select.leftWall.from()
- : event.wall === 1
- ? select.rightWall.from()
- : select.noWall.from()
- )
- },
- states: {
- NoWall: {},
- LeftWall: {},
- RightWall: {}
- }
- }
- }
- }
-})
-
-export type CharacterSnapshot = Machine.Machine.Snapshot
-export type CharacterEvent = Machine.Machine.InputEvent
-
-export const isPaused = (snapshot: CharacterSnapshot) =>
- snapshot.states.locomotion.state.path === "Character.locomotion.Paused"
-
-export const locomotionState = (snapshot: CharacterSnapshot) => {
- const locomotion = snapshot.states.locomotion.state
- if (locomotion.path === "Character.locomotion.Paused") {
- return locomotion.value
- }
-
- const playing = locomotion.state
- if (playing.path === "Character.locomotion.Playing.Airborne") {
- return playing.states.motion.state.value
- }
- return playing.state.path === "Character.locomotion.Playing.Grounded.Standing"
- ? { _tag: "Standing" as const }
- : playing.state.value
-}
-
-export type LocomotionMode = ReturnType["_tag"]
-
-export const locomotionMode = (snapshot: CharacterSnapshot): LocomotionMode => locomotionState(snapshot)._tag
-
-export const locomotionBranch = (snapshot: CharacterSnapshot) => {
- const locomotion = snapshot.states.locomotion.state
- if (locomotion.path === "Character.locomotion.Paused") return "Paused" as const
- return locomotion.state.path === "Character.locomotion.Playing.Grounded" ? "Grounded" as const : "Airborne" as const
-}
-
-export const airJumpMode = (snapshot: CharacterSnapshot) => {
- const locomotion = snapshot.states.locomotion.state
- if (
- locomotion.path !== "Character.locomotion.Playing" ||
- locomotion.state.path !== "Character.locomotion.Playing.Airborne"
- ) return undefined
-
- const path = locomotion.state.states.airJump.state.path
- if (path === "Character.locomotion.Playing.Airborne.airJump.AirJumpGroundLock") return "AirJumpGroundLock" as const
- if (path === "Character.locomotion.Playing.Airborne.airJump.AirJumpWallLock") return "AirJumpWallLock" as const
- if (path === "Character.locomotion.Playing.Airborne.airJump.AirJumpReady") return "AirJumpReady" as const
- return "AirJumpSpent" as const
-}
-
-export const wallContact = (snapshot: CharacterSnapshot) => {
- const path = snapshot.states.contact.state.path
- if (path === "Character.contact.NoWall") return "NoWall" as const
- return path === "Character.contact.LeftWall" ? "LeftWall" as const : "RightWall" as const
-}
-
-export const facingDirection = (snapshot: CharacterSnapshot) =>
- snapshot.states.facing.state.path === "Character.facing.Left" ? "Left" as const : "Right" as const
-
-export const activeStateData = (snapshot: CharacterSnapshot) => {
- const locomotion = snapshot.states.locomotion.state
- if (locomotion.path === "Character.locomotion.Paused") {
- const { _tag: _paused, ...pausedData } = locomotion.value
- return pausedData
- }
-
- const playing = locomotion.state
- if (playing.path === "Character.locomotion.Playing.Grounded") {
- if (playing.state.path === "Character.locomotion.Playing.Grounded.Standing") return {}
- const { _tag: _leaf, ...leafData } = playing.state.value
- return leafData
- }
- const { _tag: _branch, ...branchData } = playing.value
- const { _tag: _motion, ...motionData } = playing.states.motion.state.value
- return {
- ...branchData,
- ...motionData,
- airJump: airJumpMode(snapshot)
- }
-}
diff --git a/examples/platformer/src/main.ts b/examples/platformer/src/main.ts
deleted file mode 100644
index 4598149..0000000
--- a/examples/platformer/src/main.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import "./styles.css"
-import { Machine } from "@typeonce/effect-machine"
-import { Effect, Fiber, Stream } from "effect"
-import { GameAdapter } from "./game.ts"
-import {
- activeStateData,
- airJumpMode,
- type CharacterEvent,
- CharacterMachine,
- type CharacterSnapshot,
- facingDirection,
- isPaused,
- locomotionBranch,
- locomotionMode,
- wallContact
-} from "./machine.ts"
-
-const requiredElement = (selector: string) => {
- const element = document.querySelector(selector)
- if (element === null) throw new Error(`Missing element: ${selector}`)
- return element
-}
-
-const modeLabel = requiredElement("#active-mode")
-const stateData = requiredElement("#state-data")
-const lastEvent = requiredElement("#last-event")
-const gameView = requiredElement("#game")
-
-const showEvent = (event: CharacterEvent) => {
- const { _tag, ...payload } = event
- const detail = Object.keys(payload).length === 0 ? "" : ` ${JSON.stringify(payload)}`
- lastEvent.textContent = `${_tag}${detail}`
-}
-
-let deliver: ((event: CharacterEvent) => void) | undefined
-const pending: Array = []
-const send = (event: CharacterEvent) => {
- showEvent(event)
- deliver === undefined ? pending.push(event) : deliver(event)
-}
-
-const game = new GameAdapter(
- send,
- requiredElement("#player"),
- requiredElement("#player-pose")
-)
-
-const publish = (next: CharacterSnapshot) => {
- game.setSnapshot(next)
- const mode = locomotionMode(next)
- const facing = facingDirection(next)
- const contact = wallContact(next)
- const airJump = airJumpMode(next)
- const paused = isPaused(next)
- modeLabel.textContent = [mode, airJump, contact, facing].filter(Boolean).join(" · ")
- stateData.textContent = JSON.stringify(activeStateData(next))
- gameView.classList.toggle("is-paused", paused)
-
- const active = new Set([mode, facing, contact, locomotionBranch(next)])
- if (!paused) active.add("Playing")
- if (airJump !== undefined) active.add(airJump)
- document.querySelectorAll("[data-node]").forEach((node) => {
- node.classList.toggle("is-active", active.has(node.dataset.node ?? ""))
- })
-}
-
-const program = Effect.gen(function*() {
- const ref = yield* Machine.start(CharacterMachine)
- deliver = (event) => Effect.runFork(ref.send(event).pipe(Effect.catchTag("StoppedError", () => Effect.void)))
- publish(yield* ref.state)
- for (const event of pending.splice(0)) yield* ref.send(event)
- yield* Stream.runForEach(ref.changes, ({ state }) => Effect.sync(() => publish(state)))
-})
-
-const fiber = Effect.runFork(program)
-
-let previous = performance.now()
-
-const frame = (now: number) => {
- game.step((now - previous) / 1_000)
- previous = now
- requestAnimationFrame(frame)
-}
-
-requestAnimationFrame(frame)
-window.addEventListener("beforeunload", () => {
- game.destroy()
- Effect.runFork(Fiber.interrupt(fiber))
-})
diff --git a/examples/platformer/src/styles.css b/examples/platformer/src/styles.css
deleted file mode 100644
index 40bc109..0000000
--- a/examples/platformer/src/styles.css
+++ /dev/null
@@ -1,411 +0,0 @@
-:root {
- color: #e9fff9;
- background: #0a1021;
- font-family:
- Inter,
- ui-sans-serif,
- system-ui,
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- sans-serif;
- font-synthesis: none;
-}
-
-* {
- box-sizing: border-box;
-}
-
-body {
- margin: 0;
- min-width: 320px;
- min-height: 100vh;
- background:
- radial-gradient(circle at 12% 0%, rgb(46 196 182 / 16%), transparent 32rem),
- radial-gradient(circle at 92% 18%, rgb(255 107 107 / 10%), transparent 28rem), #0a1021;
-}
-
-.app-shell {
- width: min(1180px, calc(100% - 32px));
- margin: 0 auto;
- padding: 36px 0 50px;
-}
-.hero {
- display: flex;
- align-items: end;
- justify-content: space-between;
- gap: 24px;
- margin-bottom: 22px;
-}
-.eyebrow {
- margin: 0 0 8px;
- color: #55d6be;
- font:
- 700 11px/1.2 ui-monospace,
- monospace;
- letter-spacing: 0.12em;
- text-transform: uppercase;
-}
-h1 {
- margin: 0;
- font-size: clamp(42px, 7vw, 76px);
- line-height: 0.9;
- letter-spacing: -0.065em;
-}
-.lede {
- margin: 14px 0 0;
- color: #9aabc1;
- font-size: 15px;
-}
-.status-light {
- display: flex;
- gap: 8px;
- align-items: center;
- color: #92a6bc;
- font:
- 650 11px ui-monospace,
- monospace;
- text-transform: uppercase;
-}
-.status-light span {
- width: 7px;
- height: 7px;
- border-radius: 50%;
- background: #55d6be;
- box-shadow: 0 0 14px #55d6be;
-}
-
-.demo-grid {
- display: grid;
- grid-template-columns: minmax(0, 1.65fr) minmax(310px, 0.85fr);
- gap: 16px;
-}
-.game-card,
-.debug-card {
- overflow: hidden;
- border: 1px solid #243452;
- border-radius: 18px;
- background: rgb(16 24 46 / 88%);
- box-shadow: 0 24px 70px rgb(0 0 0 / 28%);
-}
-#game {
- display: block;
- width: 100%;
- aspect-ratio: 16 / 9;
- background: #10182e;
-}
-.stars {
- opacity: 0.75;
-}
-.skyline {
- fill: #263d5d;
-}
-.ground {
- fill: #273a54;
-}
-.ground-top {
- fill: #55d6be;
-}
-.wall {
- fill: #55d6be;
- opacity: 0.75;
-}
-#player-pose {
- transition: transform 80ms ease-out;
-}
-#player .body {
- fill: #2ec4b6;
- stroke: #102641;
- stroke-width: 3;
-}
-#player .face {
- fill: #fff1c1;
-}
-#player .eye {
- fill: #14213d;
-}
-#player .scarf {
- fill: #ff6b6b;
-}
-#player[data-mode="Jumping"] .body {
- fill: #63e6d6;
-}
-#player[data-jump-kind="Double"] .body {
- fill: #b388ff;
-}
-#player[data-mode="Falling"] .body {
- fill: #70a9ff;
-}
-#player[data-mode="Diving"] .body {
- fill: #ff8b7f;
-}
-#player[data-mode="Landing"] .body {
- fill: #ffd166;
-}
-#player[data-air-jump="spent"] .scarf {
- fill: #c77dff;
- filter: drop-shadow(0 0 3px #c77dff);
-}
-#player[data-paused="true"] {
- opacity: 0.55;
-}
-.pause-overlay {
- pointer-events: none;
- opacity: 0;
- transition: opacity 140ms ease;
-}
-.pause-overlay rect {
- fill: rgb(10 16 33 / 72%);
-}
-.pause-overlay text {
- text-anchor: middle;
- font-family: ui-monospace, monospace;
-}
-.pause-title {
- fill: #e9fff9;
- font-size: 30px;
- font-weight: 800;
- letter-spacing: 0.12em;
-}
-.pause-copy {
- fill: #55d6be;
- font-size: 11px;
-}
-#game.is-paused .pause-overlay {
- opacity: 1;
-}
-.controls {
- display: flex;
- flex-wrap: wrap;
- gap: 14px 20px;
- padding: 13px 16px;
- border-top: 1px solid #243452;
- color: #8fa2b9;
- font-size: 11px;
-}
-.controls span {
- display: flex;
- align-items: center;
- gap: 5px;
-}
-kbd {
- min-width: 22px;
- padding: 4px 6px;
- border: 1px solid #3b506e;
- border-bottom-width: 2px;
- border-radius: 5px;
- color: #d8fff6;
- background: #1b2942;
- font:
- 700 10px ui-monospace,
- monospace;
- text-align: center;
-}
-
-.debug-card {
- padding: 16px;
-}
-.panel-heading {
- display: flex;
- justify-content: space-between;
- margin-bottom: 14px;
- color: #8196ad;
- font:
- 700 10px ui-monospace,
- monospace;
- letter-spacing: 0.1em;
- text-transform: uppercase;
-}
-.chart {
- padding: 12px;
- border: 1px solid #273a58;
- border-radius: 12px;
- background: #0d162b;
-}
-.chart-root {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 8px 10px;
- border: 1px solid #3e5775;
- border-radius: 7px;
- color: #e9fff9;
- background: #182742;
- font:
- 700 12px ui-monospace,
- monospace;
-}
-.chart-root small {
- color: #55d6be;
- font-size: 9px;
- text-transform: uppercase;
-}
-.regions {
- display: grid;
- gap: 9px;
- margin-top: 9px;
-}
-.regions section {
- padding: 9px;
- border-left: 2px solid #334865;
- background: #111d33;
-}
-.regions h2 {
- margin: 0 0 8px;
- color: #7389a1;
- font:
- 700 9px ui-monospace,
- monospace;
- letter-spacing: 0.1em;
- text-transform: uppercase;
-}
-.branch + .branch {
- margin-top: 7px;
-}
-.play-state-row {
- margin-bottom: 7px;
-}
-.playing-region {
- padding-left: 7px;
- border-left: 1px solid #304766;
-}
-.branch h3 {
- display: inline-block;
- margin: 0 0 5px;
- padding: 3px 6px;
- border-radius: 4px;
- color: #7890a9;
- font:
- 700 9px ui-monospace,
- monospace;
-}
-.parallel-label {
- display: inline-block;
- margin-left: 4px;
- color: #55d6be;
- font:
- 700 8px ui-monospace,
- monospace;
- letter-spacing: 0.06em;
- text-transform: uppercase;
-}
-.nested-region {
- padding: 6px;
- border-left: 1px solid #304766;
- background: #0e192d;
-}
-.nested-region + .nested-region {
- margin-top: 4px;
-}
-.nested-region small {
- display: block;
- margin-bottom: 4px;
- color: #637c98;
- font:
- 700 8px ui-monospace,
- monospace;
- letter-spacing: 0.08em;
- text-transform: uppercase;
-}
-.state-row {
- display: flex;
- flex-wrap: wrap;
- gap: 4px;
-}
-.state-row span {
- padding: 5px 6px;
- border: 1px solid #2b3c59;
- border-radius: 5px;
- color: #70869e;
- background: #131f35;
- font:
- 650 9px ui-monospace,
- monospace;
- transition: 120ms ease;
-}
-.state-row .history-node {
- border-style: dashed;
- border-color: #8a6bc4;
- color: #b89de8;
- background: rgb(138 107 196 / 8%);
-}
-[data-node].is-active {
- border-color: #55d6be !important;
- color: #dffff8 !important;
- background: rgb(46 196 182 / 16%) !important;
- box-shadow: 0 0 0 2px rgb(46 196 182 / 8%);
-}
-.facing-row {
- display: grid;
- grid-template-columns: 1fr 1fr;
-}
-.facing-row span {
- text-align: center;
-}
-
-.telemetry {
- display: grid;
- gap: 0;
- margin: 14px 0 0;
-}
-.telemetry div {
- display: grid;
- grid-template-columns: 105px 1fr;
- gap: 12px;
- padding: 9px 2px;
- border-top: 1px solid #23334f;
-}
-.telemetry dt {
- color: #6f849c;
- font:
- 700 9px ui-monospace,
- monospace;
- letter-spacing: 0.04em;
- text-transform: uppercase;
-}
-.telemetry dd {
- min-width: 0;
- margin: 0;
- overflow-wrap: anywhere;
- color: #c8d8e7;
- font:
- 600 10px/1.4 ui-monospace,
- monospace;
-}
-code {
- color: #ff9292;
- font-family: ui-monospace, monospace;
-}
-.note {
- max-width: 790px;
- margin: 18px 4px 0;
- color: #72879f;
- font-size: 12px;
- line-height: 1.7;
-}
-
-@media (max-width: 850px) {
- .demo-grid {
- grid-template-columns: 1fr;
- }
- .debug-card {
- min-height: 0;
- }
-}
-
-@media (max-width: 540px) {
- .app-shell {
- width: min(100% - 16px, 1180px);
- padding-top: 24px;
- }
- .hero {
- align-items: start;
- flex-direction: column;
- }
- .controls {
- gap: 10px;
- }
- .telemetry div {
- grid-template-columns: 90px 1fr;
- }
-}
diff --git a/examples/platformer/src/vite-env.d.ts b/examples/platformer/src/vite-env.d.ts
deleted file mode 100644
index 11f02fe..0000000
--- a/examples/platformer/src/vite-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/examples/platformer/tsconfig.json b/examples/platformer/tsconfig.json
deleted file mode 100644
index ed48e42..0000000
--- a/examples/platformer/tsconfig.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2022",
- "useDefineForClassFields": true,
- "module": "ESNext",
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
- "skipLibCheck": true,
- "moduleResolution": "Bundler",
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": true,
- "isolatedModules": true,
- "moduleDetection": "force",
- "noEmit": true,
- "strict": true,
- "noUncheckedIndexedAccess": true
- },
- "include": ["src"]
-}
diff --git a/examples/platformer/vite.config.ts b/examples/platformer/vite.config.ts
deleted file mode 100644
index 20ed627..0000000
--- a/examples/platformer/vite.config.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { defineConfig } from "vite"
-
-export default defineConfig({
- server: {
- port: 5174
- }
-})
diff --git a/examples/playground/.gitignore b/examples/playground/.gitignore
deleted file mode 100644
index 1502ec2..0000000
--- a/examples/playground/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-dist
-node_modules
-
diff --git a/examples/playground/README.md b/examples/playground/README.md
deleted file mode 100644
index 2111dd3..0000000
--- a/examples/playground/README.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Effect Machine playground
-
-Five complete React examples, ordered from a two-state machine to browser
-transport integration.
-
-## Run
-
-```sh
-pnpm install --frozen-lockfile
-pnpm dev
-```
-
-Validate tests, types, and the production bundle with:
-
-```sh
-pnpm check
-```
-
-## Examples
-
-| Route | Machine concept | Integration concept |
-| ---------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------- |
-| `/turnstile` | Atomic states, typed commands, ignored events | Service-free `AtomMachine` |
-| `/traffic-light` | Internal events, cancellable state-scoped timers, re-entry | Reactive timer-driven rendering |
-| `/microwave` | Compound hierarchy and unrepresentable invalid states | Safety-oriented controls |
-| `/media-player` | Resource-owned compound states and an independent settings region | Shared Atom runtime, DOM audio, Web Audio service |
-| `/worker-tabs` | A machine hosted outside the UI thread | Schema-validated worker messages and `BroadcastChannel` synchronization |
-
-Each route keeps its machine, adapter, and supporting protocol beside the page.
-The machines own legal behavior; components project snapshots and send typed
-public commands.
-
-## Notable boundaries
-
-- The traffic light exposes `Reset` publicly while timer deliveries stay in
- `internalEvents`.
-- The microwave stores elapsed time only on `Cooking`, where it is valid.
- `Cooking` is nested below `Closed`, so opening the door exits and interrupts
- cooking and `Cooking + Open` cannot be represented.
-- The media player keeps browser APIs behind an Effect service. Its invoked
- transport is nested below the registered audio session that it requires,
- while sound settings remain an independent parallel region. Invoked work
- returns typed internal events to the deterministic transition core.
-- The worker validates unknown incoming messages with Effect Schema before
- forwarding public events. Tabs replicate commands and exchange a typed
- synchronization state when a tab joins.
-
-`src/examples/examples.test.ts` covers the smaller machines, including virtual
-clock advancement for inline invocation timers. The media player keeps focused model and
-property coverage in its own directory.
diff --git a/examples/playground/index.html b/examples/playground/index.html
deleted file mode 100644
index 1817520..0000000
--- a/examples/playground/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
- Effect Machine examples
-
-
-
-
-
-
-
diff --git a/examples/playground/package.json b/examples/playground/package.json
deleted file mode 100644
index 97d1260..0000000
--- a/examples/playground/package.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- "name": "@typeonce/effect-machine-example-playground",
- "version": "0.0.0",
- "private": true,
- "description": "React playground for building @typeonce/effect-machine examples",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "test": "vitest run",
- "typecheck": "tsc --noEmit",
- "build": "pnpm typecheck && vite build",
- "preview": "vite preview",
- "check": "pnpm test && pnpm build"
- },
- "dependencies": {
- "@effect/atom-react": "4.0.0-rc.111",
- "@tanstack/react-router": "1.170.18",
- "@typeonce/effect-machine": "file:../..",
- "effect": "4.0.0-rc.111",
- "react": "19.2.7",
- "react-dom": "19.2.7",
- "scheduler": "0.27.0"
- },
- "devDependencies": {
- "@effect/vitest": "4.0.0-rc.111",
- "@types/react": "19.2.17",
- "@types/react-dom": "19.2.2",
- "@vitejs/plugin-react": "6.0.4",
- "typescript": "6.0.3",
- "vite": "8.1.5",
- "vitest": "4.1.10"
- },
- "packageManager": "pnpm@10.17.1",
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
-}
diff --git a/examples/playground/pnpm-lock.yaml b/examples/playground/pnpm-lock.yaml
deleted file mode 100644
index 63ddef3..0000000
--- a/examples/playground/pnpm-lock.yaml
+++ /dev/null
@@ -1,1099 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-overrides:
- effect: 4.0.0-rc.111
- '@effect/atom-react': 4.0.0-rc.111
- '@effect/vitest': 4.0.0-rc.111
-
-importers:
-
- .:
- dependencies:
- '@effect/atom-react':
- specifier: 4.0.0-rc.111
- version: 4.0.0-rc.111(effect@4.0.0-rc.111)(react@19.2.7)(scheduler@0.27.0)
- '@tanstack/react-router':
- specifier: 1.170.18
- version: 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
- '@typeonce/effect-machine':
- specifier: file:../..
- version: file:../..(effect@4.0.0-rc.111)
- effect:
- specifier: 4.0.0-rc.111
- version: 4.0.0-rc.111
- react:
- specifier: 19.2.7
- version: 19.2.7
- react-dom:
- specifier: 19.2.7
- version: 19.2.7(react@19.2.7)
- scheduler:
- specifier: 0.27.0
- version: 0.27.0
- devDependencies:
- '@effect/vitest':
- specifier: 4.0.0-rc.111
- version: 4.0.0-rc.111(effect@4.0.0-rc.111)(vitest@4.1.10(vite@8.1.5(yaml@2.9.0)))
- '@types/react':
- specifier: 19.2.17
- version: 19.2.17
- '@types/react-dom':
- specifier: 19.2.2
- version: 19.2.2(@types/react@19.2.17)
- '@vitejs/plugin-react':
- specifier: 6.0.4
- version: 6.0.4(vite@8.1.5(yaml@2.9.0))
- typescript:
- specifier: 6.0.3
- version: 6.0.3
- vite:
- specifier: 8.1.5
- version: 8.1.5(yaml@2.9.0)
- vitest:
- specifier: 4.1.10
- version: 4.1.10(vite@8.1.5(yaml@2.9.0))
-
-packages:
-
- '@effect/atom-react@4.0.0-rc.111':
- resolution: {integrity: sha512-MQgX2+ayms/J686g19vV2AkLSocMaubZmFf2VFrWN3fwPg60XXsaPtlQKdcsIy/McxVGZYgYUPXBVVtcmUjjSA==}
- peerDependencies:
- effect: 4.0.0-rc.111
- react: '>=19.2.7 <20.0.0'
- scheduler: '>=0.27.0 <0.28.0'
-
- '@effect/vitest@4.0.0-rc.111':
- resolution: {integrity: sha512-YDaEVT+grREBVMzykRNFtJwGxy02achzT0WXZYwMJA4ukzuB9krkgQ5roc3N6zhC3NQq/j4IyM32/Pcov7AhHw==}
- peerDependencies:
- effect: 4.0.0-rc.111
- vitest: '>=4.1.0 <5.0.0'
-
- '@emnapi/core@1.11.1':
- resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
-
- '@emnapi/runtime@1.11.1':
- resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
-
- '@emnapi/wasi-threads@1.2.2':
- resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
-
- '@jridgewell/sourcemap-codec@1.5.5':
- resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
-
- '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4':
- resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==}
- cpu: [arm64]
- os: [darwin]
-
- '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4':
- resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==}
- cpu: [x64]
- os: [darwin]
-
- '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4':
- resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==}
- cpu: [arm64]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4':
- resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==}
- cpu: [arm]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4':
- resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==}
- cpu: [x64]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4':
- resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==}
- cpu: [x64]
- os: [win32]
-
- '@napi-rs/wasm-runtime@1.2.2':
- resolution: {integrity: sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==}
- engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0}
- peerDependencies:
- '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3
- '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3
-
- '@oxc-project/types@0.139.0':
- resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==}
-
- '@rolldown/binding-android-arm64@1.1.5':
- resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [android]
-
- '@rolldown/binding-darwin-arm64@1.1.5':
- resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [darwin]
-
- '@rolldown/binding-darwin-x64@1.1.5':
- resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [darwin]
-
- '@rolldown/binding-freebsd-x64@1.1.5':
- resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [freebsd]
-
- '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
- resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm]
- os: [linux]
-
- '@rolldown/binding-linux-arm64-gnu@1.1.5':
- resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-arm64-musl@1.1.5':
- resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
- '@rolldown/binding-linux-ppc64-gnu@1.1.5':
- resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [ppc64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-s390x-gnu@1.1.5':
- resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [s390x]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-x64-gnu@1.1.5':
- resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-x64-musl@1.1.5':
- resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [linux]
- libc: [musl]
-
- '@rolldown/binding-openharmony-arm64@1.1.5':
- resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [openharmony]
-
- '@rolldown/binding-wasm32-wasi@1.1.5':
- resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [wasm32]
-
- '@rolldown/binding-win32-arm64-msvc@1.1.5':
- resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [win32]
-
- '@rolldown/binding-win32-x64-msvc@1.1.5':
- resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [win32]
-
- '@rolldown/pluginutils@1.0.1':
- resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
-
- '@standard-schema/spec@1.1.0':
- resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
-
- '@tanstack/history@1.162.0':
- resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==}
- engines: {node: '>=20.19'}
-
- '@tanstack/react-router@1.170.18':
- resolution: {integrity: sha512-wpbGYZEp/fmz1q4bn7BD8VZ+/VZ7GBqSJv5V969pU+chP8y7dquWDmKTFMohvUegb9lg12m1uPVvD6kB2wORvQ==}
- engines: {node: '>=20.19'}
- peerDependencies:
- react: '>=18.0.0 || >=19.0.0'
- react-dom: '>=18.0.0 || >=19.0.0'
-
- '@tanstack/react-store@0.9.3':
- resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==}
- peerDependencies:
- react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
-
- '@tanstack/router-core@1.171.15':
- resolution: {integrity: sha512-IILCDcLaItMZQ2jEmCABHY1Nhjjn5XUvwpQp3e4Nmu+vfg0BgYFuu/QASz2SwE2ZNbVMrvt8X/wxa+Gg5aErxA==}
- engines: {node: '>=20.19'}
-
- '@tanstack/store@0.9.3':
- resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==}
-
- '@tybys/wasm-util@0.10.3':
- resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
-
- '@typeonce/effect-machine@file:../..':
- resolution: {directory: ../.., type: directory}
- engines: {node: '>=20'}
- peerDependencies:
- effect: 4.0.0-rc.111
-
- '@types/chai@5.2.3':
- resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
-
- '@types/deep-eql@4.0.2':
- resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
-
- '@types/estree@1.0.9':
- resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
-
- '@types/react-dom@19.2.2':
- resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==}
- peerDependencies:
- '@types/react': ^19.2.0
-
- '@types/react@19.2.17':
- resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}
-
- '@vitejs/plugin-react@6.0.4':
- resolution: {integrity: sha512-XcCQz0TBpBgljhj0gMuuDj49i6Ytqh5q1osT/Gp5uAVJUCTWxyskk/l1jwYYiu2xcNHHipdMz40EGfM1VdamVg==}
- engines: {node: ^20.19.0 || >=22.12.0}
- peerDependencies:
- '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0
- babel-plugin-react-compiler: ^1.0.0
- vite: ^8.0.0
- peerDependenciesMeta:
- '@rolldown/plugin-babel':
- optional: true
- babel-plugin-react-compiler:
- optional: true
-
- '@vitest/expect@4.1.10':
- resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==}
-
- '@vitest/mocker@4.1.10':
- resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==}
- peerDependencies:
- msw: ^2.4.9
- vite: ^6.0.0 || ^7.0.0 || ^8.0.0
- peerDependenciesMeta:
- msw:
- optional: true
- vite:
- optional: true
-
- '@vitest/pretty-format@4.1.10':
- resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==}
-
- '@vitest/runner@4.1.10':
- resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==}
-
- '@vitest/snapshot@4.1.10':
- resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==}
-
- '@vitest/spy@4.1.10':
- resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==}
-
- '@vitest/utils@4.1.10':
- resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==}
-
- assertion-error@2.0.1:
- resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
- engines: {node: '>=12'}
-
- chai@6.2.2:
- resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
- engines: {node: '>=18'}
-
- convert-source-map@2.0.0:
- resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
-
- cookie-es@3.1.1:
- resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==}
-
- csstype@3.2.3:
- resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
-
- detect-libc@2.1.2:
- resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
- engines: {node: '>=8'}
-
- effect@4.0.0-rc.111:
- resolution: {integrity: sha512-ASd5L58EIR0CUNueZNKKjSsyOCd+2alxOAIaTcHaqkJkPsaYSsw5Cg/cfANk5K4Jr2YsX756xvX11shzqsreWA==}
-
- es-module-lexer@2.3.1:
- resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==}
-
- estree-walker@3.0.3:
- resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
-
- expect-type@1.4.0:
- resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
- engines: {node: '>=12.0.0'}
-
- fast-check@4.9.0:
- resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==}
- engines: {node: '>=12.17.0'}
-
- fdir@6.5.0:
- resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
- engines: {node: '>=12.0.0'}
- peerDependencies:
- picomatch: ^3 || ^4
- peerDependenciesMeta:
- picomatch:
- optional: true
-
- fsevents@2.3.3:
- resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
- isbot@5.2.1:
- resolution: {integrity: sha512-dJ+LpKyClQZ7NG+j3OensC/mAZkGpukE9YUrgPYvAZj2doVL0edfDgywTUh5CXa0o+nW9a1V9e5+CJTX8+SxRw==}
- engines: {node: '>=18'}
-
- lightningcss-android-arm64@1.33.0:
- resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [android]
-
- lightningcss-darwin-arm64@1.33.0:
- resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [darwin]
-
- lightningcss-darwin-x64@1.33.0:
- resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [darwin]
-
- lightningcss-freebsd-x64@1.33.0:
- resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [freebsd]
-
- lightningcss-linux-arm-gnueabihf@1.33.0:
- resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm]
- os: [linux]
-
- lightningcss-linux-arm64-gnu@1.33.0:
- resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
- lightningcss-linux-arm64-musl@1.33.0:
- resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
- lightningcss-linux-x64-gnu@1.33.0:
- resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [linux]
- libc: [glibc]
-
- lightningcss-linux-x64-musl@1.33.0:
- resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [linux]
- libc: [musl]
-
- lightningcss-win32-arm64-msvc@1.33.0:
- resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [win32]
-
- lightningcss-win32-x64-msvc@1.33.0:
- resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [win32]
-
- lightningcss@1.33.0:
- resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
- engines: {node: '>= 12.0.0'}
-
- magic-string@0.30.21:
- resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
-
- msgpackr-extract@3.0.4:
- resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==}
- hasBin: true
-
- msgpackr@2.0.5:
- resolution: {integrity: sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==}
-
- nanoid@3.3.16:
- resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- node-gyp-build-optional-packages@5.2.2:
- resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
- hasBin: true
-
- obug@2.1.4:
- resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==}
- engines: {node: '>=12.20.0'}
-
- pathe@2.0.3:
- resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
-
- picocolors@1.1.1:
- resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
-
- picomatch@4.0.5:
- resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
- engines: {node: '>=12'}
-
- postcss@8.5.25:
- resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==}
- engines: {node: ^10 || ^12 || >=14}
-
- pure-rand@8.4.2:
- resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==}
-
- react-dom@19.2.7:
- resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==}
- peerDependencies:
- react: ^19.2.7
-
- react@19.2.7:
- resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}
- engines: {node: '>=0.10.0'}
-
- rolldown@1.1.5:
- resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- hasBin: true
-
- scheduler@0.27.0:
- resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
-
- seroval-plugins@1.6.0:
- resolution: {integrity: sha512-CbR5DP5DPicpd9RwRUzka7hi4x1577eGFXJVBW409LXqqJNst99JSUTZ8CXcnskQX7laPOWpmzliq0gBZEGlrQ==}
- engines: {node: '>=10'}
- peerDependencies:
- seroval: ^1.0
-
- seroval@1.6.0:
- resolution: {integrity: sha512-TBwwKfscTEgnBEWmYKKeCcmCGmrJi0LV6qNUY//WBA3MDesh/zfn+KOMq/ckpxM4gZ0ouAE706A1eenekM2sug==}
- engines: {node: '>=10'}
-
- siginfo@2.0.0:
- resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
-
- source-map-js@1.2.1:
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
- engines: {node: '>=0.10.0'}
-
- stackback@0.0.2:
- resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
-
- std-env@4.2.0:
- resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
-
- tinybench@2.9.0:
- resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
-
- tinyexec@1.3.0:
- resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==}
- engines: {node: '>=18'}
-
- tinyglobby@0.2.17:
- resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
- engines: {node: '>=12.0.0'}
-
- tinyrainbow@3.1.1:
- resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==}
- engines: {node: '>=14.0.0'}
-
- tslib@2.8.1:
- resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
-
- typescript@6.0.3:
- resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
- engines: {node: '>=14.17'}
- hasBin: true
-
- use-sync-external-store@1.6.0:
- resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
- peerDependencies:
- react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
-
- vite@8.1.5:
- resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- hasBin: true
- peerDependencies:
- '@types/node': ^20.19.0 || >=22.12.0
- '@vitejs/devtools': ^0.3.0
- esbuild: ^0.27.0 || ^0.28.0
- jiti: '>=1.21.0'
- less: ^4.0.0
- sass: ^1.70.0
- sass-embedded: ^1.70.0
- stylus: '>=0.54.8'
- sugarss: ^5.0.0
- terser: ^5.16.0
- tsx: ^4.8.1
- yaml: ^2.4.2
- peerDependenciesMeta:
- '@types/node':
- optional: true
- '@vitejs/devtools':
- optional: true
- esbuild:
- optional: true
- jiti:
- optional: true
- less:
- optional: true
- sass:
- optional: true
- sass-embedded:
- optional: true
- stylus:
- optional: true
- sugarss:
- optional: true
- terser:
- optional: true
- tsx:
- optional: true
- yaml:
- optional: true
-
- vitest@4.1.10:
- resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==}
- engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
- hasBin: true
- peerDependencies:
- '@edge-runtime/vm': '*'
- '@opentelemetry/api': ^1.9.0
- '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
- '@vitest/browser-playwright': 4.1.10
- '@vitest/browser-preview': 4.1.10
- '@vitest/browser-webdriverio': 4.1.10
- '@vitest/coverage-istanbul': 4.1.10
- '@vitest/coverage-v8': 4.1.10
- '@vitest/ui': 4.1.10
- happy-dom: '*'
- jsdom: '*'
- vite: ^6.0.0 || ^7.0.0 || ^8.0.0
- peerDependenciesMeta:
- '@edge-runtime/vm':
- optional: true
- '@opentelemetry/api':
- optional: true
- '@types/node':
- optional: true
- '@vitest/browser-playwright':
- optional: true
- '@vitest/browser-preview':
- optional: true
- '@vitest/browser-webdriverio':
- optional: true
- '@vitest/coverage-istanbul':
- optional: true
- '@vitest/coverage-v8':
- optional: true
- '@vitest/ui':
- optional: true
- happy-dom:
- optional: true
- jsdom:
- optional: true
-
- why-is-node-running@2.3.0:
- resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
- engines: {node: '>=8'}
- hasBin: true
-
- yaml@2.9.0:
- resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
- engines: {node: '>= 14.6'}
- hasBin: true
-
-snapshots:
-
- '@effect/atom-react@4.0.0-rc.111(effect@4.0.0-rc.111)(react@19.2.7)(scheduler@0.27.0)':
- dependencies:
- effect: 4.0.0-rc.111
- react: 19.2.7
- scheduler: 0.27.0
-
- '@effect/vitest@4.0.0-rc.111(effect@4.0.0-rc.111)(vitest@4.1.10(vite@8.1.5(yaml@2.9.0)))':
- dependencies:
- effect: 4.0.0-rc.111
- vitest: 4.1.10(vite@8.1.5(yaml@2.9.0))
-
- '@emnapi/core@1.11.1':
- dependencies:
- '@emnapi/wasi-threads': 1.2.2
- tslib: 2.8.1
- optional: true
-
- '@emnapi/runtime@1.11.1':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@emnapi/wasi-threads@1.2.2':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@jridgewell/sourcemap-codec@1.5.5': {}
-
- '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4':
- optional: true
-
- '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
- dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
- '@tybys/wasm-util': 0.10.3
- optional: true
-
- '@oxc-project/types@0.139.0': {}
-
- '@rolldown/binding-android-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-darwin-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-darwin-x64@1.1.5':
- optional: true
-
- '@rolldown/binding-freebsd-x64@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm64-musl@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-ppc64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-s390x-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-x64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-x64-musl@1.1.5':
- optional: true
-
- '@rolldown/binding-openharmony-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-wasm32-wasi@1.1.5':
- dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
- '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
- optional: true
-
- '@rolldown/binding-win32-arm64-msvc@1.1.5':
- optional: true
-
- '@rolldown/binding-win32-x64-msvc@1.1.5':
- optional: true
-
- '@rolldown/pluginutils@1.0.1': {}
-
- '@standard-schema/spec@1.1.0': {}
-
- '@tanstack/history@1.162.0': {}
-
- '@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
- dependencies:
- '@tanstack/history': 1.162.0
- '@tanstack/react-store': 0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
- '@tanstack/router-core': 1.171.15
- isbot: 5.2.1
- react: 19.2.7
- react-dom: 19.2.7(react@19.2.7)
-
- '@tanstack/react-store@0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
- dependencies:
- '@tanstack/store': 0.9.3
- react: 19.2.7
- react-dom: 19.2.7(react@19.2.7)
- use-sync-external-store: 1.6.0(react@19.2.7)
-
- '@tanstack/router-core@1.171.15':
- dependencies:
- '@tanstack/history': 1.162.0
- cookie-es: 3.1.1
- seroval: 1.6.0
- seroval-plugins: 1.6.0(seroval@1.6.0)
-
- '@tanstack/store@0.9.3': {}
-
- '@tybys/wasm-util@0.10.3':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@typeonce/effect-machine@file:../..(effect@4.0.0-rc.111)':
- dependencies:
- effect: 4.0.0-rc.111
-
- '@types/chai@5.2.3':
- dependencies:
- '@types/deep-eql': 4.0.2
- assertion-error: 2.0.1
-
- '@types/deep-eql@4.0.2': {}
-
- '@types/estree@1.0.9': {}
-
- '@types/react-dom@19.2.2(@types/react@19.2.17)':
- dependencies:
- '@types/react': 19.2.17
-
- '@types/react@19.2.17':
- dependencies:
- csstype: 3.2.3
-
- '@vitejs/plugin-react@6.0.4(vite@8.1.5(yaml@2.9.0))':
- dependencies:
- '@rolldown/pluginutils': 1.0.1
- vite: 8.1.5(yaml@2.9.0)
-
- '@vitest/expect@4.1.10':
- dependencies:
- '@standard-schema/spec': 1.1.0
- '@types/chai': 5.2.3
- '@vitest/spy': 4.1.10
- '@vitest/utils': 4.1.10
- chai: 6.2.2
- tinyrainbow: 3.1.1
-
- '@vitest/mocker@4.1.10(vite@8.1.5(yaml@2.9.0))':
- dependencies:
- '@vitest/spy': 4.1.10
- estree-walker: 3.0.3
- magic-string: 0.30.21
- optionalDependencies:
- vite: 8.1.5(yaml@2.9.0)
-
- '@vitest/pretty-format@4.1.10':
- dependencies:
- tinyrainbow: 3.1.1
-
- '@vitest/runner@4.1.10':
- dependencies:
- '@vitest/utils': 4.1.10
- pathe: 2.0.3
-
- '@vitest/snapshot@4.1.10':
- dependencies:
- '@vitest/pretty-format': 4.1.10
- '@vitest/utils': 4.1.10
- magic-string: 0.30.21
- pathe: 2.0.3
-
- '@vitest/spy@4.1.10': {}
-
- '@vitest/utils@4.1.10':
- dependencies:
- '@vitest/pretty-format': 4.1.10
- convert-source-map: 2.0.0
- tinyrainbow: 3.1.1
-
- assertion-error@2.0.1: {}
-
- chai@6.2.2: {}
-
- convert-source-map@2.0.0: {}
-
- cookie-es@3.1.1: {}
-
- csstype@3.2.3: {}
-
- detect-libc@2.1.2: {}
-
- effect@4.0.0-rc.111:
- dependencies:
- '@standard-schema/spec': 1.1.0
- fast-check: 4.9.0
- msgpackr: 2.0.5
-
- es-module-lexer@2.3.1: {}
-
- estree-walker@3.0.3:
- dependencies:
- '@types/estree': 1.0.9
-
- expect-type@1.4.0: {}
-
- fast-check@4.9.0:
- dependencies:
- pure-rand: 8.4.2
-
- fdir@6.5.0(picomatch@4.0.5):
- optionalDependencies:
- picomatch: 4.0.5
-
- fsevents@2.3.3:
- optional: true
-
- isbot@5.2.1: {}
-
- lightningcss-android-arm64@1.33.0:
- optional: true
-
- lightningcss-darwin-arm64@1.33.0:
- optional: true
-
- lightningcss-darwin-x64@1.33.0:
- optional: true
-
- lightningcss-freebsd-x64@1.33.0:
- optional: true
-
- lightningcss-linux-arm-gnueabihf@1.33.0:
- optional: true
-
- lightningcss-linux-arm64-gnu@1.33.0:
- optional: true
-
- lightningcss-linux-arm64-musl@1.33.0:
- optional: true
-
- lightningcss-linux-x64-gnu@1.33.0:
- optional: true
-
- lightningcss-linux-x64-musl@1.33.0:
- optional: true
-
- lightningcss-win32-arm64-msvc@1.33.0:
- optional: true
-
- lightningcss-win32-x64-msvc@1.33.0:
- optional: true
-
- lightningcss@1.33.0:
- dependencies:
- detect-libc: 2.1.2
- optionalDependencies:
- lightningcss-android-arm64: 1.33.0
- lightningcss-darwin-arm64: 1.33.0
- lightningcss-darwin-x64: 1.33.0
- lightningcss-freebsd-x64: 1.33.0
- lightningcss-linux-arm-gnueabihf: 1.33.0
- lightningcss-linux-arm64-gnu: 1.33.0
- lightningcss-linux-arm64-musl: 1.33.0
- lightningcss-linux-x64-gnu: 1.33.0
- lightningcss-linux-x64-musl: 1.33.0
- lightningcss-win32-arm64-msvc: 1.33.0
- lightningcss-win32-x64-msvc: 1.33.0
-
- magic-string@0.30.21:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.5.5
-
- msgpackr-extract@3.0.4:
- dependencies:
- node-gyp-build-optional-packages: 5.2.2
- optionalDependencies:
- '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4
- optional: true
-
- msgpackr@2.0.5:
- optionalDependencies:
- msgpackr-extract: 3.0.4
-
- nanoid@3.3.16: {}
-
- node-gyp-build-optional-packages@5.2.2:
- dependencies:
- detect-libc: 2.1.2
- optional: true
-
- obug@2.1.4: {}
-
- pathe@2.0.3: {}
-
- picocolors@1.1.1: {}
-
- picomatch@4.0.5: {}
-
- postcss@8.5.25:
- dependencies:
- nanoid: 3.3.16
- picocolors: 1.1.1
- source-map-js: 1.2.1
-
- pure-rand@8.4.2: {}
-
- react-dom@19.2.7(react@19.2.7):
- dependencies:
- react: 19.2.7
- scheduler: 0.27.0
-
- react@19.2.7: {}
-
- rolldown@1.1.5:
- dependencies:
- '@oxc-project/types': 0.139.0
- '@rolldown/pluginutils': 1.0.1
- optionalDependencies:
- '@rolldown/binding-android-arm64': 1.1.5
- '@rolldown/binding-darwin-arm64': 1.1.5
- '@rolldown/binding-darwin-x64': 1.1.5
- '@rolldown/binding-freebsd-x64': 1.1.5
- '@rolldown/binding-linux-arm-gnueabihf': 1.1.5
- '@rolldown/binding-linux-arm64-gnu': 1.1.5
- '@rolldown/binding-linux-arm64-musl': 1.1.5
- '@rolldown/binding-linux-ppc64-gnu': 1.1.5
- '@rolldown/binding-linux-s390x-gnu': 1.1.5
- '@rolldown/binding-linux-x64-gnu': 1.1.5
- '@rolldown/binding-linux-x64-musl': 1.1.5
- '@rolldown/binding-openharmony-arm64': 1.1.5
- '@rolldown/binding-wasm32-wasi': 1.1.5
- '@rolldown/binding-win32-arm64-msvc': 1.1.5
- '@rolldown/binding-win32-x64-msvc': 1.1.5
-
- scheduler@0.27.0: {}
-
- seroval-plugins@1.6.0(seroval@1.6.0):
- dependencies:
- seroval: 1.6.0
-
- seroval@1.6.0: {}
-
- siginfo@2.0.0: {}
-
- source-map-js@1.2.1: {}
-
- stackback@0.0.2: {}
-
- std-env@4.2.0: {}
-
- tinybench@2.9.0: {}
-
- tinyexec@1.3.0: {}
-
- tinyglobby@0.2.17:
- dependencies:
- fdir: 6.5.0(picomatch@4.0.5)
- picomatch: 4.0.5
-
- tinyrainbow@3.1.1: {}
-
- tslib@2.8.1:
- optional: true
-
- typescript@6.0.3: {}
-
- use-sync-external-store@1.6.0(react@19.2.7):
- dependencies:
- react: 19.2.7
-
- vite@8.1.5(yaml@2.9.0):
- dependencies:
- lightningcss: 1.33.0
- picomatch: 4.0.5
- postcss: 8.5.25
- rolldown: 1.1.5
- tinyglobby: 0.2.17
- optionalDependencies:
- fsevents: 2.3.3
- yaml: 2.9.0
-
- vitest@4.1.10(vite@8.1.5(yaml@2.9.0)):
- dependencies:
- '@vitest/expect': 4.1.10
- '@vitest/mocker': 4.1.10(vite@8.1.5(yaml@2.9.0))
- '@vitest/pretty-format': 4.1.10
- '@vitest/runner': 4.1.10
- '@vitest/snapshot': 4.1.10
- '@vitest/spy': 4.1.10
- '@vitest/utils': 4.1.10
- es-module-lexer: 2.3.1
- expect-type: 1.4.0
- magic-string: 0.30.21
- obug: 2.1.4
- pathe: 2.0.3
- picomatch: 4.0.5
- std-env: 4.2.0
- tinybench: 2.9.0
- tinyexec: 1.3.0
- tinyglobby: 0.2.17
- tinyrainbow: 3.1.1
- vite: 8.1.5(yaml@2.9.0)
- why-is-node-running: 2.3.0
- transitivePeerDependencies:
- - msw
-
- why-is-node-running@2.3.0:
- dependencies:
- siginfo: 2.0.0
- stackback: 0.0.2
-
- yaml@2.9.0:
- optional: true
diff --git a/examples/playground/pnpm-workspace.yaml b/examples/playground/pnpm-workspace.yaml
deleted file mode 100644
index 87dc7f5..0000000
--- a/examples/playground/pnpm-workspace.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-packages:
- - "."
-
-overrides:
- effect: 4.0.0-rc.111
- "@effect/atom-react": 4.0.0-rc.111
- "@effect/vitest": 4.0.0-rc.111
-
-minimumReleaseAge: 1440
-minimumReleaseAgeExclude:
- - effect
- - "@effect/atom-react"
- - "@effect/vitest"
diff --git a/examples/playground/src/components/ExamplePage.tsx b/examples/playground/src/components/ExamplePage.tsx
deleted file mode 100644
index e031dbd..0000000
--- a/examples/playground/src/components/ExamplePage.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import type { ReactNode } from "react"
-
-export function ExamplePage({
- title,
- summary,
- machineFile,
- children
-}: {
- readonly title: string
- readonly summary: string
- readonly machineFile: string
- readonly children: ReactNode
-}) {
- return (
-
-
- Interactive example
- {title}
- {summary}
- {machineFile}
-
- {children}
-
- )
-}
diff --git a/examples/playground/src/examples/examples.test.ts b/examples/playground/src/examples/examples.test.ts
deleted file mode 100644
index 5693d9a..0000000
--- a/examples/playground/src/examples/examples.test.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { assert, describe, it } from "@effect/vitest"
-import { Machine } from "@typeonce/effect-machine"
-import { MachineTest } from "@typeonce/effect-machine/testing"
-import { Effect } from "effect"
-import { MicrowaveMachine } from "./microwave/machine.ts"
-import { TrafficLightMachine } from "./traffic-light/machine.ts"
-import { TurnstileMachine } from "./turnstile/machine.ts"
-import { SharedMachine, SharedTransportEvents } from "./worker-tabs/machine.ts"
-
-describe("playground machines", () => {
- it.effect("accepts only the command enabled by the current turnstile state", () =>
- Effect.gen(function*() {
- const trace = yield* MachineTest.run(TurnstileMachine, {
- events: [
- { _tag: "GatePushed" },
- { _tag: "CoinInserted" },
- { _tag: "CoinInserted" },
- { _tag: "GatePushed" }
- ]
- })
-
- yield* MachineTest.verify(TurnstileMachine, trace)
- assert.deepStrictEqual(trace.steps.map(({ plan }) => plan.microsteps.length > 0), [false, true, false, true])
- assert.strictEqual(trace.final.path, "Locked")
- }))
-
- it.effect("advances state-scoped traffic-light timers with the runtime clock", () =>
- Effect.gen(function*() {
- const ref = yield* Machine.start(TrafficLightMachine)
- const probe = yield* MachineTest.probe(TrafficLightMachine, ref)
- const transcript = yield* MachineTest.verifyCausalCommands(
- probe,
- [MachineTest.advanceCommand(4_000), MachineTest.advanceCommand(1_000)],
- {
- invariants: [],
- await: ({ index }) =>
- probe.await.until((snapshot) => snapshot.state.path === (index === 0 ? "RedYellow" : "Green"))
- }
- )
-
- assert.strictEqual(transcript.final.state.path, "Green")
- yield* ref.stop
- }))
-
- it.effect("makes cooking with an open door unreachable", () =>
- Effect.gen(function*() {
- const trace = yield* MachineTest.run(MicrowaveMachine, {
- events: [
- { _tag: "PowerPressed" },
- { _tag: "DoorOpened" },
- { _tag: "PowerPressed" },
- { _tag: "DoorClosed" },
- { _tag: "PowerPressed" }
- ]
- })
-
- yield* MachineTest.verify(MicrowaveMachine, trace)
- const opened = trace.steps[1]?.after
- assert.strictEqual(opened?.state.path, "Oven.Open")
- assert.deepStrictEqual(trace.steps[2]?.before, trace.steps[2]?.after)
- assert.strictEqual(trace.final.state.path, "Oven.Closed")
- if (trace.final.state.path === "Oven.Closed") {
- assert.strictEqual(trace.final.state.state.path, "Oven.Closed.Cooking")
- }
- }))
-
- it.effect("restores worker state from a tab synchronization command", () =>
- Effect.gen(function*() {
- const trace = yield* MachineTest.run(SharedMachine, {
- events: [
- SharedTransportEvents.Started(),
- SharedTransportEvents.Incremented(),
- SharedTransportEvents.Synchronized({ active: false, count: 12 })
- ]
- })
-
- yield* MachineTest.verify(SharedMachine, trace)
- assert.strictEqual(trace.final.path, "Idle")
- assert.strictEqual(trace.final.value.count, 12)
- }))
-})
diff --git a/examples/playground/src/examples/media-player/MediaPlayerPage.tsx b/examples/playground/src/examples/media-player/MediaPlayerPage.tsx
deleted file mode 100644
index 2ed1419..0000000
--- a/examples/playground/src/examples/media-player/MediaPlayerPage.tsx
+++ /dev/null
@@ -1,215 +0,0 @@
-import { useAtomSet, useAtomValue } from "@effect/atom-react"
-import { Match } from "effect"
-import { useCallback, useEffect, useState } from "react"
-import { ExamplePage } from "../../components/ExamplePage.tsx"
-import { mediaPlayerAtom, mediaPlayerViewAtom, setMediaPlayerElement } from "./atoms.ts"
-import { MediaPlayerEvents } from "./machine.ts"
-
-export function MediaPlayerPage() {
- const viewResult = useAtomValue(mediaPlayerViewAtom)
- const send = useAtomSet(mediaPlayerAtom.send)
- const setAudioElement = useAtomSet(setMediaPlayerElement)
- const [source, setSource] = useState<{ readonly name: string; readonly url: string }>()
-
- const registerAudioElement = useCallback(
- (audioRef: HTMLAudioElement | null) => {
- setAudioElement(audioRef)
- },
- [setAudioElement]
- )
-
- useEffect(
- () => () => {
- if (source !== undefined) URL.revokeObjectURL(source.url)
- },
- [source]
- )
-
- return (
-
- {Match.value(viewResult).pipe(
- Match.tagsExhaustive({
- Initial: () => Starting the media player machine…
,
- Failure: () => The media player machine failed to start.
,
- Success: ({ value: view }) => {
- const { session, settings, status, transport } = view
- const { canPause, canPlay, canRestart, isPlaying, loudness, playback } = transport
- const loudnessLevel = Math.min(100, Math.round((loudness?.rms ?? 0) * 220))
- const peakLevel = Math.min(100, Math.round((loudness?.peak ?? 0) * 100))
-
- return (
-
-
-
-
Effect audio session
-
{source?.name ?? "Choose a local audio track"}
-
-
- {transport.name}
-
- {source === undefined ? "Choose audio" : "Replace audio"}
- {
- const file = event.currentTarget.files?.[0]
- if (file === undefined) return
- const next = { name: file.name, url: URL.createObjectURL(file) }
- setSource(next)
- send(MediaPlayerEvents.SourceSelected({ url: next.url }))
- }}
- />
-
-
-
-
-
-
-
-
-
-
-
- Current time
-
- {Number.isFinite(playback.currentTime)
- ? `${Math.floor(playback.currentTime / 60)}:${
- String(Math.floor(playback.currentTime % 60)).padStart(2, "0")
- }`
- : "0:00"}
-
-
-
-
- send(MediaPlayerEvents.PlayRequested())}
- >
- Play
-
- send(MediaPlayerEvents.PauseRequested())}
- >
- Pause
-
- send(MediaPlayerEvents.RestartRequested())}
- >
- Restart
-
-
-
-
-
- Loudness
- {loudness?.decibels.toFixed(1) ?? "-80.0"} dB
-
-
-
-
-
- RMS {loudnessLevel}% · Peak {peakLevel}%
-
-
-
-
-
-
-
-
- Machine snapshot
- {status}
-
-
- {JSON.stringify({
- status,
- states: {
- session,
- transport: transport.path,
- settings: settings.path
- },
- data: {
- transport: transport.value,
- settings: settings.value
- }
- }, null, 2)}
-
-
-
- {transport.error !== null && {transport.error}
}
-
-
-
- )
- }
- })
- )}
-
- )
-}
diff --git a/examples/playground/src/examples/media-player/atoms.ts b/examples/playground/src/examples/media-player/atoms.ts
deleted file mode 100644
index 81d678c..0000000
--- a/examples/playground/src/examples/media-player/atoms.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-import { AtomMachine } from "@typeonce/effect-machine/reactivity"
-import { Effect, Match } from "effect"
-import { Atom } from "effect/unstable/reactivity"
-import { MediaPlayerEvents, MediaPlayerMachine } from "./machine.ts"
-import { MediaPlayer } from "./service.ts"
-
-const mediaPlayerRuntime = Atom.runtime(MediaPlayer.layer)
-
-export const mediaPlayerAtom = AtomMachine.bind(mediaPlayerRuntime).make(MediaPlayerMachine)
-
-export const setMediaPlayerElement = mediaPlayerRuntime.fn((audioRef: HTMLAudioElement | null, get) =>
- Effect.gen(function*() {
- const mediaPlayer = yield* MediaPlayer
- yield* mediaPlayer.register(audioRef)
- yield* get.setResult(
- mediaPlayerAtom.send,
- audioRef === null ? MediaPlayerEvents.AudioElementUnmounted() : MediaPlayerEvents.AudioElementMounted()
- )
- })
-)
-
-export const mediaPlayerViewAtom = Atom.mapResult(mediaPlayerAtom.snapshot, ({ state, status }) => {
- const transportDefaults = {
- playback: { currentTime: 0 },
- loudness: null,
- error: null,
- isPlaying: false,
- isBuffering: false,
- canPlay: false,
- canPause: false,
- canRestart: false
- } as const
-
- const session = state.states.session.state
-
- const transport = session.path === "Player.session.Registered"
- ? Match.value(session.state).pipe(
- Match.discriminatorsExhaustive("path")({
- "Player.session.Registered.Empty": ({ path, value }) => ({
- ...transportDefaults,
- path,
- name: "Empty" as const,
- value
- }),
- "Player.session.Registered.Loading": ({ path, value }) => ({
- ...transportDefaults,
- path,
- name: "Loading" as const,
- value
- }),
- "Player.session.Registered.Ready": ({ state }) =>
- Match.value(state).pipe(
- Match.discriminatorsExhaustive("path")({
- "Player.session.Registered.Ready.Paused": ({ path, value }) => ({
- ...transportDefaults,
- path,
- name: "Paused" as const,
- value,
- playback: value,
- canPlay: true,
- canRestart: true
- }),
- "Player.session.Registered.Ready.Playing": ({ path, value }) => ({
- ...transportDefaults,
- path,
- name: "Playing" as const,
- value,
- playback: value,
- loudness: value.loudness,
- isPlaying: true,
- canPause: true,
- canRestart: true
- }),
- "Player.session.Registered.Ready.Buffering": ({ path, value }) => ({
- ...transportDefaults,
- path,
- name: "Buffering" as const,
- value,
- playback: value,
- isBuffering: true,
- canPause: true,
- canRestart: true
- }),
- "Player.session.Registered.Ready.Restarting": ({ path, value }) => ({
- ...transportDefaults,
- path,
- name: "Restarting" as const,
- value,
- playback: value
- }),
- "Player.session.Registered.Ready.Ended": ({ path, value }) => ({
- ...transportDefaults,
- path,
- name: "Ended" as const,
- value,
- playback: value,
- canPlay: true,
- canRestart: true
- })
- })
- ),
- "Player.session.Registered.Failed": ({ path, value }) => ({
- ...transportDefaults,
- path,
- name: "Failed" as const,
- value,
- error: value.message
- })
- })
- )
- : {
- ...transportDefaults,
- path: session.path,
- name: "Unavailable" as const,
- value: session.value
- }
-
- return {
- status,
- session: session.path,
- transport,
- settings: Match.value(state.states.settings.state).pipe(
- Match.discriminatorsExhaustive("path")({
- "Player.settings.Audible": ({ path, value }) => ({
- path,
- value,
- volume: value.volume,
- playbackRate: value.playbackRate,
- muted: false
- }),
- "Player.settings.Muted": ({ path, value }) => ({
- path,
- value,
- volume: value.volume,
- playbackRate: value.playbackRate,
- muted: true
- })
- })
- )
- }
-})
diff --git a/examples/playground/src/examples/media-player/machine.test.ts b/examples/playground/src/examples/media-player/machine.test.ts
deleted file mode 100644
index 3f903cc..0000000
--- a/examples/playground/src/examples/media-player/machine.test.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-import { assert, describe, it } from "@effect/vitest"
-import { Machine } from "@typeonce/effect-machine"
-import { MachineTest } from "@typeonce/effect-machine/testing"
-import { Effect, Graph } from "effect"
-import { FastCheck } from "effect/testing"
-import { MediaPlayerMachine } from "./machine.ts"
-
-const everyPublicEvent: ReadonlyArray> = [
- { _tag: "AudioElementMounted" },
- { _tag: "AudioElementUnmounted" },
- { _tag: "SourceSelected", url: "https://example.com/audio.mp3" },
- { _tag: "PlayRequested" },
- { _tag: "PauseRequested" },
- { _tag: "RestartRequested" },
- { _tag: "VolumeChanged", volume: 0.4 },
- { _tag: "PlaybackRateChanged", playbackRate: 1.5 },
- { _tag: "MuteRequested" },
- { _tag: "UnmuteRequested" }
-]
-
-const generated = MachineTest.scenarios(MediaPlayerMachine, {
- eventsArbitrary: FastCheck.array(FastCheck.constantFrom(...everyPublicEvent), { maxLength: 30 })
-})
-
-const same = (left: unknown, right: unknown) => JSON.stringify(left) === JSON.stringify(right)
-
-const invariant = MachineTest.invariants(MediaPlayerMachine)
-
-const parallelRegionsAreIndependent = invariant.step(
- "session and settings commands stay in their own parallel region",
- ({ after, before, event }) => {
- const sessionUnchanged = same(before.states.session.state, after.states.session.state)
- const settingsUnchanged = same(before.states.settings.state, after.states.settings.state)
- if (["VolumeChanged", "PlaybackRateChanged", "MuteRequested", "UnmuteRequested"].includes(event._tag)) {
- return sessionUnchanged || `settings event ${event._tag} changed the session region`
- }
- return settingsUnchanged || `session event ${event._tag} changed the settings region`
- }
-)
-
-const settingsUpdatesAreExact = invariant.step(
- "settings commands preserve unrelated values",
- ({ after, before, event }) => {
- const previous = before.states.settings.state
- const next = after.states.settings.state
- switch (event._tag) {
- case "VolumeChanged":
- return Object.is(next.value.volume, event.volume) &&
- Object.is(next.value.playbackRate, previous.value.playbackRate) ||
- "volume update changed playback rate or stored the wrong volume"
- case "PlaybackRateChanged":
- return Object.is(next.value.playbackRate, event.playbackRate) &&
- Object.is(next.value.volume, previous.value.volume) ||
- "playback-rate update changed volume or stored the wrong rate"
- case "MuteRequested":
- return next.path === "Player.settings.Muted" &&
- Object.is(next.value.volume, previous.value.volume) &&
- Object.is(next.value.playbackRate, previous.value.playbackRate) ||
- "mute did not preserve sound settings"
- case "UnmuteRequested":
- return next.path === "Player.settings.Audible" &&
- Object.is(next.value.volume, previous.value.volume) &&
- Object.is(next.value.playbackRate, previous.value.playbackRate) ||
- "unmute did not preserve sound settings"
- default:
- return true
- }
- }
-)
-
-const laws = [parallelRegionsAreIndependent, settingsUpdatesAreExact]
-
-describe("media-player statechart model", () => {
- it.effect.prop(
- "keeps every schema-generated scenario structurally valid",
- { scenario: generated.arbitrary },
- ({ scenario }) =>
- MachineTest.run(MediaPlayerMachine, scenario).pipe(
- Effect.tap((trace) => MachineTest.verify(MediaPlayerMachine, trace)),
- Effect.tap((trace) => MachineTest.assertInvariants(MediaPlayerMachine, trace, laws)),
- Effect.tap((trace) =>
- Effect.sync(() => {
- assert.strictEqual(trace.final.path, "Player")
- assert.strictEqual(trace.final.states.session.state.path.startsWith("Player.session."), true)
- assert.strictEqual(trace.final.states.settings.state.path.startsWith("Player.settings."), true)
- })
- ),
- Effect.asVoid
- ),
- { fastCheck: { numRuns: 100, seed: 24_061 } }
- )
-
- it.effect("covers the public protocol and its reachable states", () =>
- Effect.gen(function*() {
- const trace = yield* MachineTest.run(MediaPlayerMachine, { events: everyPublicEvent })
- yield* MachineTest.verify(MediaPlayerMachine, trace)
-
- const coverage = MachineTest.coverage(MediaPlayerMachine, trace)
-
- assert.strictEqual(coverage.events.hit, everyPublicEvent.length)
- assert.strictEqual(coverage.events.missing, 0)
- assert.strictEqual(coverage.transitions.definitions.hit > 0, true)
- assert.strictEqual(coverage.transitions.branches.hit > 0, true)
-
- const activated = new Set(coverage.states.activation.hits.map(({ path }) => path))
- assert.strictEqual(activated.has("Player.session.Unregistered"), true)
- assert.strictEqual(activated.has("Player.session.Registered"), true)
- assert.strictEqual(activated.has("Player.session.Registered.Empty"), true)
- assert.strictEqual(activated.has("Player.settings.Audible"), true)
- assert.strictEqual(activated.has("Player.settings.Muted"), true)
- }))
-
- it.effect("records every planned event in the observed graph", () =>
- Effect.gen(function*() {
- const trace = yield* MachineTest.run(MediaPlayerMachine, { events: everyPublicEvent })
- const observed = yield* MachineTest.observedGraph(MediaPlayerMachine, trace)
- const eventEdges = Array.from(Graph.edges(observed.graph), ([, edge]) => edge).filter(
- ({ data }) => data._tag === "Event"
- )
-
- assert.strictEqual(Graph.nodeCount(observed.graph) > 1, true)
- assert.strictEqual(Graph.edgeCount(observed.graph), everyPublicEvent.length + 1)
- assert.strictEqual(eventEdges.length, everyPublicEvent.length)
- assert.strictEqual(observed.starts.length, 1)
- }))
-
- it.effect("explores public behavior and preserves the internal completion boundary", () =>
- Effect.gen(function*() {
- const explored = yield* MachineTest.explore(MediaPlayerMachine, {
- events: () => everyPublicEvent,
- stateKey: ({ snapshot }) =>
- JSON.stringify({
- session: snapshot.states.session.state,
- settings: snapshot.states.settings.state
- }),
- invariants: laws
- })
-
- assert.deepStrictEqual(explored.completeness, { _tag: "Complete" })
-
- const registeredAndMuted = yield* MachineTest.assertReachable(
- explored,
- "registered element while muted",
- ({ configuration }) =>
- configuration.includes("Player.session.Registered") &&
- configuration.includes("Player.settings.Muted")
- )
- assert.deepStrictEqual(registeredAndMuted.trace.scenario.events, [
- everyPublicEvent[0],
- { _tag: "MuteRequested" }
- ])
-
- yield* MachineTest.assertUnreachable(
- explored,
- "transport active while the audio element is unregistered",
- ({ configuration }) =>
- configuration.includes("Player.session.Unregistered") &&
- configuration.some((path) => path.startsWith("Player.session.Registered."))
- )
- }))
-})
diff --git a/examples/playground/src/examples/media-player/machine.ts b/examples/playground/src/examples/media-player/machine.ts
deleted file mode 100644
index c8d982a..0000000
--- a/examples/playground/src/examples/media-player/machine.ts
+++ /dev/null
@@ -1,455 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Effect, Match, Schema, Stream } from "effect"
-import { MediaPlayer } from "./service.ts"
-
-interface PlaybackData {
- readonly currentTime: number
-}
-
-const State = Schema.TaggedUnion({
- Loading: { url: Schema.String },
- Paused: { currentTime: Schema.Number },
- Playing: {
- currentTime: Schema.Number,
- loudness: Schema.NullOr(Schema.Struct({
- rms: Schema.Number,
- peak: Schema.Number,
- decibels: Schema.Number
- }))
- },
- Buffering: { currentTime: Schema.Number },
- Restarting: { currentTime: Schema.Number },
- Ended: { currentTime: Schema.Number },
- Failed: { message: Schema.String },
- Audible: {
- volume: Schema.Number,
- playbackRate: Schema.Number
- },
- Muted: {
- volume: Schema.Number,
- playbackRate: Schema.Number
- }
-})
-
-export const MediaPlayerEvents = Machine.events(
- Schema.TaggedUnion({
- AudioElementMounted: {},
- AudioElementUnmounted: {},
- SourceSelected: { url: Schema.String },
- PlayRequested: {},
- PauseRequested: {},
- RestartRequested: {},
- VolumeChanged: { volume: Schema.Number },
- PlaybackRateChanged: { playbackRate: Schema.Number },
- MuteRequested: {},
- UnmuteRequested: {}
- })
-)
-
-const MediaPlayerInternalEvents = Machine.internalEvents(
- Schema.TaggedUnion({
- LoadSucceeded: {},
- RestartSucceeded: {},
- MediaWaiting: {},
- MediaCanPlay: {},
- PlaybackEnded: { currentTime: Schema.Number },
- TimeUpdated: { currentTime: Schema.Number },
- MediaFailed: { message: Schema.String },
- LoudnessMeasured: {
- rms: Schema.Number,
- peak: Schema.Number,
- decibels: Schema.Number
- },
- OperationFailed: { message: Schema.String }
- })
-)
-
-const MediaPlayerStates = Machine.states({
- Player: {
- type: "parallel",
- states: {
- session: {
- initial: "Unregistered",
- states: {
- Unregistered: {},
- Registered: {
- initial: "Empty",
- states: {
- Empty: {},
- Loading: State.cases.Loading,
- Ready: {
- initial: "Paused",
- states: {
- Paused: State.cases.Paused,
- Playing: State.cases.Playing,
- Buffering: State.cases.Buffering,
- Restarting: State.cases.Restarting,
- Ended: State.cases.Ended
- }
- },
- Failed: State.cases.Failed
- }
- }
- }
- },
- settings: {
- initial: "Audible",
- states: {
- Audible: State.cases.Audible,
- Muted: State.cases.Muted
- }
- }
- }
- }
-})
-
-const updatePlaybackData = (
- state: PlaybackData,
- patch: Partial
-): PlaybackData => ({
- currentTime: patch.currentTime ?? state.currentTime
-})
-
-export const MediaPlayerMachine = Machine.make({
- id: "MediaPlayer",
- states: MediaPlayerStates.states,
- events: MediaPlayerEvents,
- internalEvents: MediaPlayerInternalEvents,
- initial: (to) =>
- to.Player.initial.resolve(({ target }) =>
- target.from((player) =>
- player
- .session.from((session) => session.Unregistered.from())
- .settings.from((settings) =>
- settings.Audible.from({
- volume: 1,
- playbackRate: 1
- })
- )
- )
- )
-}).handle({
- Player: {
- states: {
- session: {
- on: {
- AudioElementMounted: (to) =>
- to.local.Registered.initial.resolve(({ target }) => target.from(), { reenter: true }),
- AudioElementUnmounted: (to) =>
- to.local.Unregistered().resolve(({ target }) => target.from(), { reenter: true })
- },
- states: {
- Unregistered: {},
-
- Registered: {
- invoke: (from) =>
- from.stream("media-element-events", () =>
- Stream.unwrap(
- Effect.map(MediaPlayer, (mediaPlayer) => mediaPlayer.events)
- )).onElement((to) =>
- to.none.resolve(({ element }, enqueue) => {
- Match.value(element).pipe(
- Match.tagsExhaustive({
- Waiting: () => enqueue.raise(MediaPlayerInternalEvents.MediaWaiting()),
- CanPlay: () => enqueue.raise(MediaPlayerInternalEvents.MediaCanPlay()),
- Ended: ({ currentTime }) =>
- enqueue.raise(MediaPlayerInternalEvents.PlaybackEnded({ currentTime })),
- TimeUpdated: ({ currentTime }) =>
- enqueue.raise(MediaPlayerInternalEvents.TimeUpdated({ currentTime })),
- Failed: ({ message }) => enqueue.raise(MediaPlayerInternalEvents.MediaFailed({ message }))
- })
- )
- })
- ).onDone((to) => to.none).onFailure((to) =>
- to.local.Failed().resolve(({ error, target }) => target.from({ message: error.message }))
- ),
- on: {
- SourceSelected: (to) =>
- to.local.Loading().resolve(({ event, target }) => target.from({ url: event.url }), { reenter: true }),
-
- MediaFailed: (to) =>
- to.local.Failed().resolve(({ event, target }) => target.from({ message: event.message })),
-
- OperationFailed: (to) =>
- to.local.Failed().resolve(({ event, target }) => target.from({ message: event.message }))
- },
- states: {
- Empty: {},
-
- Loading: {
- invoke: (from) =>
- from.effect("load-audio", ({ state }) =>
- Effect.gen(function*() {
- const mediaPlayer = yield* MediaPlayer
- yield* mediaPlayer.load(state.url)
- })).onDone((to) =>
- to.none.resolve((_, enqueue) => {
- enqueue.raise(MediaPlayerInternalEvents.LoadSucceeded())
- return undefined
- })
- ).onFailure((to) =>
- to.none.resolve(({ error }, enqueue) => {
- enqueue.raise(MediaPlayerInternalEvents.OperationFailed({ message: error.message }))
- return undefined
- })
- ),
- on: {
- LoadSucceeded: (to) => to.local.Ready.initial
- }
- },
-
- Ready: {
- initialize: ({ builder }) => builder.from({ currentTime: 0 }),
- states: {
- Paused: {
- invoke: (from) =>
- from.effect("pause-audio", () =>
- Effect.gen(function*() {
- const mediaPlayer = yield* MediaPlayer
- yield* mediaPlayer.pause
- })).onDone((to) => to.none).onFailure((to) =>
- to.none.resolve(({ error }, enqueue) => {
- enqueue.raise(MediaPlayerInternalEvents.OperationFailed({ message: error.message }))
- return undefined
- })
- ),
- on: {
- PlayRequested: (to) =>
- to.local.Playing().resolve(({ state, target }) =>
- target.from({
- ...updatePlaybackData(state, {}),
- loudness: null
- })
- ),
-
- RestartRequested: (to) =>
- to.local.Restarting().resolve(({ state, target }) => target.from(updatePlaybackData(state, {})))
- }
- },
-
- Playing: {
- invoke: (
- from
- ) => [
- from.effect("play-audio", () =>
- Effect.gen(function*() {
- const mediaPlayer = yield* MediaPlayer
- yield* mediaPlayer.play
- })).onDone((to) => to.none).onFailure((to) =>
- to.none.resolve(({ error }, enqueue) => {
- enqueue.raise(MediaPlayerInternalEvents.OperationFailed({ message: error.message }))
- return undefined
- })
- ),
- from.stream("analyze-audio", () =>
- Stream.unwrap(
- Effect.map(MediaPlayer, (mediaPlayer) => mediaPlayer.loudness)
- )).onElement((to) =>
- to.none.resolve(({ element }, enqueue) => {
- enqueue.raise(MediaPlayerInternalEvents.LoudnessMeasured(element))
- })
- ).onDone((to) => to.none).onFailure((to) =>
- to.none.resolve(({ error }, enqueue) => {
- enqueue.raise(MediaPlayerInternalEvents.OperationFailed({ message: error.message }))
- })
- )
- ],
- on: {
- PauseRequested: (to) =>
- to.local.Paused().resolve(({ state, target }) => target.from(updatePlaybackData(state, {}))),
-
- RestartRequested: (to) =>
- to.local.Restarting().resolve(({ state, target }) =>
- target.from(updatePlaybackData(state, {}))
- ),
-
- MediaWaiting: (to) =>
- to.local.Buffering().resolve(({ state, target }) => target.from(updatePlaybackData(state, {}))),
-
- PlaybackEnded: (to) =>
- to.local.Ended().resolve(({ event, state, target }) =>
- target.from(updatePlaybackData(state, { currentTime: event.currentTime }))
- ),
-
- TimeUpdated: (to) =>
- to.local.Playing().resolve(({ event, state, target }) =>
- target.from({
- currentTime: event.currentTime,
- loudness: state.loudness
- })
- ),
-
- LoudnessMeasured: (to) =>
- to.local.Playing().resolve(({ event, state, target }) =>
- target.from({
- currentTime: state.currentTime,
- loudness: {
- rms: event.rms,
- peak: event.peak,
- decibels: event.decibels
- }
- })
- )
- }
- },
-
- Buffering: {
- on: {
- MediaCanPlay: (to) =>
- to.local.Playing().resolve(({ state, target }) =>
- target.from({
- ...updatePlaybackData(state, {}),
- loudness: null
- })
- ),
-
- PauseRequested: (to) =>
- to.local.Paused().resolve(({ state, target }) => target.from(updatePlaybackData(state, {}))),
-
- RestartRequested: (to) =>
- to.local.Restarting().resolve(({ state, target }) =>
- target.from(updatePlaybackData(state, {}))
- ),
-
- PlaybackEnded: (to) =>
- to.local.Ended().resolve(({ event, state, target }) =>
- target.from(updatePlaybackData(state, { currentTime: event.currentTime }))
- ),
-
- TimeUpdated: (to) =>
- to.local.Buffering().resolve(({ event, state, target }) =>
- target.from(updatePlaybackData(state, { currentTime: event.currentTime }))
- )
- }
- },
-
- Restarting: {
- invoke: (from) =>
- from.effect("restart-audio", () =>
- Effect.gen(function*() {
- const mediaPlayer = yield* MediaPlayer
- yield* mediaPlayer.restart
- })).onDone((to) =>
- to.none.resolve((_, enqueue) => {
- enqueue.raise(MediaPlayerInternalEvents.RestartSucceeded())
- return undefined
- })
- ).onFailure((to) =>
- to.none.resolve(({ error }, enqueue) => {
- enqueue.raise(MediaPlayerInternalEvents.OperationFailed({ message: error.message }))
- return undefined
- })
- ),
- on: {
- RestartSucceeded: (to) =>
- to.local.Playing().resolve(({ target }) => target.from({ currentTime: 0, loudness: null })),
-
- TimeUpdated: (to) =>
- to.local.Restarting().resolve(({ event, state, target }) =>
- target.from(updatePlaybackData(state, { currentTime: event.currentTime }))
- )
- }
- },
-
- Ended: {
- on: {
- PlayRequested: (to) =>
- to.local.Restarting().resolve(({ state, target }) =>
- target.from(updatePlaybackData(state, {}))
- ),
-
- RestartRequested: (to) =>
- to.local.Restarting().resolve(({ state, target }) => target.from(updatePlaybackData(state, {})))
- }
- }
- }
- },
-
- Failed: {
- invoke: (from) =>
- from.effect("report-error", ({ state }) =>
- Effect.gen(function*() {
- const mediaPlayer = yield* MediaPlayer
- yield* mediaPlayer.reportError(state.message)
- })).onDone((to) => to.none)
- }
- }
- }
- }
- },
-
- settings: {
- initialize: ({ builder }) =>
- builder.from({
- volume: 1,
- playbackRate: 1
- }),
- states: {
- Audible: {
- invoke: (from) =>
- from.effect("apply-audio-settings", ({ state }) =>
- Effect.gen(function*() {
- const mediaPlayer = yield* MediaPlayer
- yield* mediaPlayer.applySettings({ ...state, muted: false })
- })).onDone((to) => to.none),
- on: {
- VolumeChanged: (to) =>
- to.local.Audible().resolve(({ event, state, target }) =>
- target.from({
- volume: event.volume,
- playbackRate: state.playbackRate
- }), { reenter: true }),
-
- PlaybackRateChanged: (to) =>
- to.local.Audible().resolve(({ event, state, target }) =>
- target.from({
- volume: state.volume,
- playbackRate: event.playbackRate
- }), { reenter: true }),
-
- MuteRequested: (to) =>
- to.local.Muted().resolve(({ state, target }) =>
- target.from({
- volume: state.volume,
- playbackRate: state.playbackRate
- })
- )
- }
- },
-
- Muted: {
- invoke: (from) =>
- from.effect("apply-audio-settings", ({ state }) =>
- Effect.gen(function*() {
- const mediaPlayer = yield* MediaPlayer
- yield* mediaPlayer.applySettings({ ...state, muted: true })
- })).onDone((to) => to.none),
- on: {
- VolumeChanged: (to) =>
- to.local.Muted().resolve(({ event, state, target }) =>
- target.from({
- volume: event.volume,
- playbackRate: state.playbackRate
- }), { reenter: true }),
-
- PlaybackRateChanged: (to) =>
- to.local.Muted().resolve(({ event, state, target }) =>
- target.from({
- volume: state.volume,
- playbackRate: event.playbackRate
- }), { reenter: true }),
-
- UnmuteRequested: (to) =>
- to.local.Audible().resolve(({ state, target }) =>
- target.from({
- volume: state.volume,
- playbackRate: state.playbackRate
- })
- )
- }
- }
- }
- }
- }
- }
-})
diff --git a/examples/playground/src/examples/media-player/service.ts b/examples/playground/src/examples/media-player/service.ts
deleted file mode 100644
index 2e3c0de..0000000
--- a/examples/playground/src/examples/media-player/service.ts
+++ /dev/null
@@ -1,281 +0,0 @@
-import { Context, Data, Effect, Fiber, Layer, Option, Ref, Schedule, ScopedRef, Stream, SynchronizedRef } from "effect"
-
-export interface AudioSettings {
- readonly volume: number
- readonly muted: boolean
- readonly playbackRate: number
-}
-
-export interface LoudnessSample {
- readonly rms: number
- readonly peak: number
- readonly decibels: number
-}
-
-export class MediaPlayerError extends Data.TaggedError("MediaPlayerError")<{
- readonly operation: "register" | "observe" | "load" | "play" | "pause" | "restart" | "analyze"
- readonly message: string
-}> {}
-
-type MediaElementEvent = Data.TaggedEnum<{
- Waiting: {}
- CanPlay: {}
- Ended: { readonly currentTime: number }
- TimeUpdated: { readonly currentTime: number }
- Failed: { readonly message: string }
-}>
-
-const MediaElementEvent = Data.taggedEnum()
-
-type AudioGraph = Data.TaggedEnum<{
- Registered: {
- readonly audioRef: HTMLAudioElement
- }
- Loaded: {
- readonly audioRef: HTMLAudioElement
- readonly audioContext: AudioContext
- readonly trackSource: MediaElementAudioSourceNode
- readonly analyserNode: AnalyserNode
- }
-}>
-
-const AudioGraph = Data.taggedEnum()
-
-interface AudioSession {
- readonly graph: SynchronizedRef.SynchronizedRef
-}
-
-const fail = (
- operation: MediaPlayerError["operation"],
- message: string
-): Effect.Effect => Effect.fail(new MediaPlayerError({ operation, message }))
-
-export class MediaPlayer extends Context.Service()(
- "effect-machine/playground/MediaPlayer",
- {
- make: Effect.gen(function*() {
- const settingsRef = yield* Ref.make({ volume: 1, muted: false, playbackRate: 1 })
- const sessionRef = yield* ScopedRef.make>(() => Option.none())
-
- const session = (operation: MediaPlayerError["operation"]) =>
- ScopedRef.get(sessionRef).pipe(
- Effect.flatMap(
- Option.match({
- onNone: () => fail(operation, "The audio element is not registered"),
- onSome: Effect.succeed
- })
- )
- )
-
- const applySettings = (audioRef: HTMLAudioElement, settings: AudioSettings) =>
- Effect.sync(() => {
- audioRef.volume = settings.volume
- audioRef.muted = settings.muted
- audioRef.playbackRate = settings.playbackRate
- })
-
- const resume = (audioContext: AudioContext, operation: "play" | "restart") =>
- audioContext.state === "suspended"
- ? Effect.tryPromise({
- try: () => audioContext.resume(),
- catch: () => new MediaPlayerError({ operation, message: "The audio context could not be resumed" })
- })
- : Effect.void
-
- return {
- register: (audioRef: HTMLAudioElement | null) =>
- ScopedRef.set(
- sessionRef,
- audioRef === null
- ? Effect.succeed(Option.none())
- : Effect.acquireRelease(
- Effect.gen(function*() {
- yield* applySettings(audioRef, yield* Ref.get(settingsRef))
- return Option.some({
- graph: yield* SynchronizedRef.make(AudioGraph.Registered({ audioRef }))
- })
- }),
- Option.match({
- onNone: () => Effect.void,
- onSome: ({ graph }) =>
- SynchronizedRef.get(graph).pipe(
- Effect.flatMap((current) =>
- AudioGraph.$is("Loaded")(current)
- ? Effect.all([
- Effect.sync(() => {
- current.trackSource.disconnect()
- current.analyserNode.disconnect()
- }),
- current.audioContext.state === "closed"
- ? Effect.void
- : Effect.promise(() => current.audioContext.close()).pipe(Effect.ignore)
- ], { discard: true })
- : Effect.void
- )
- )
- })
- )
- ),
-
- events: Stream.unwrap(
- Effect.gen(function*() {
- const { graph } = yield* session("observe")
- const audioRef = (yield* SynchronizedRef.get(graph)).audioRef
- return Stream.mergeAll([
- Stream.fromEventListener(audioRef, "waiting").pipe(
- Stream.map((): MediaElementEvent => MediaElementEvent.Waiting())
- ),
- Stream.fromEventListener(audioRef, "canplay").pipe(
- Stream.map((): MediaElementEvent => MediaElementEvent.CanPlay())
- ),
- Stream.fromEventListener(audioRef, "ended").pipe(
- Stream.map((): MediaElementEvent => MediaElementEvent.Ended({ currentTime: audioRef.currentTime }))
- ),
- Stream.fromEventListener(audioRef, "timeupdate").pipe(
- Stream.map((): MediaElementEvent =>
- MediaElementEvent.TimeUpdated({ currentTime: audioRef.currentTime })
- )
- ),
- Stream.fromEventListener(audioRef, "error").pipe(
- Stream.map((): MediaElementEvent =>
- MediaElementEvent.Failed({
- message: audioRef.error?.message ?? "The selected audio file could not be loaded"
- })
- )
- )
- ], { concurrency: "unbounded" })
- })
- ),
-
- load: (url: string) =>
- Effect.gen(function*() {
- const { graph } = yield* session("load")
- yield* SynchronizedRef.updateEffect(graph, (current) =>
- Effect.gen(function*() {
- const audioRef = current.audioRef
- const loaded = Stream.fromEventListener(audioRef, "loadeddata").pipe(
- Stream.take(1),
- Stream.runHead,
- Effect.asVoid
- )
- const failed = Stream.fromEventListener(audioRef, "error").pipe(
- Stream.take(1),
- Stream.runHead,
- Effect.flatMap(() =>
- fail("load", audioRef.error?.message ?? "The selected audio file could not be loaded")
- )
- )
- const waiting = yield* Stream.merge(loaded.pipe(Stream.fromEffect), failed.pipe(Stream.fromEffect))
- .pipe(Stream.runHead, Effect.forkChild({ startImmediately: true }))
-
- yield* Effect.try({
- try: () => {
- audioRef.src = url
- audioRef.load()
- },
- catch: () => new MediaPlayerError({ operation: "load", message: "The audio file could not load" })
- })
- yield* Fiber.join(waiting)
-
- if (AudioGraph.$is("Loaded")(current)) return current
-
- const AudioContextConstructor = window.AudioContext ??
- (window as Window & { readonly webkitAudioContext?: typeof AudioContext }).webkitAudioContext
- if (AudioContextConstructor === undefined) {
- return yield* fail("load", "Web Audio is not supported by this browser")
- }
-
- return yield* Effect.try({
- try: () => {
- const audioContext = new AudioContextConstructor()
- const trackSource = audioContext.createMediaElementSource(audioRef)
- const analyserNode = audioContext.createAnalyser()
- analyserNode.fftSize = 256
- trackSource.connect(analyserNode)
- analyserNode.connect(audioContext.destination)
- return AudioGraph.Loaded({ audioRef, audioContext, trackSource, analyserNode })
- },
- catch: () =>
- new MediaPlayerError({ operation: "load", message: "The audio graph could not be connected" })
- })
- }))
- }),
-
- applySettings: (settings: AudioSettings) =>
- Effect.gen(function*() {
- yield* Ref.set(settingsRef, settings)
- const current = yield* ScopedRef.get(sessionRef)
- if (Option.isSome(current)) {
- yield* SynchronizedRef.get(current.value.graph).pipe(
- Effect.flatMap((graph) => applySettings(graph.audioRef, settings))
- )
- }
- }),
-
- play: Effect.gen(function*() {
- const { graph } = yield* session("play")
- const current = yield* SynchronizedRef.get(graph)
- if (!AudioGraph.$is("Loaded")(current)) return yield* fail("play", "The audio graph is not ready")
- yield* resume(current.audioContext, "play")
- yield* Effect.tryPromise({
- try: () => current.audioRef.play(),
- catch: () => new MediaPlayerError({ operation: "play", message: "Playback could not be started" })
- })
- }),
-
- pause: Effect.gen(function*() {
- const { graph } = yield* session("pause")
- const current = yield* SynchronizedRef.get(graph)
- yield* Effect.try({
- try: () => current.audioRef.pause(),
- catch: () => new MediaPlayerError({ operation: "pause", message: "Playback could not be paused" })
- })
- }),
-
- restart: Effect.gen(function*() {
- const { graph } = yield* session("restart")
- const current = yield* SynchronizedRef.get(graph)
- if (!AudioGraph.$is("Loaded")(current)) return yield* fail("restart", "The audio graph is not ready")
- yield* resume(current.audioContext, "restart")
- yield* Effect.tryPromise({
- try: async () => {
- current.audioRef.currentTime = 0
- await current.audioRef.play()
- },
- catch: () => new MediaPlayerError({ operation: "restart", message: "Playback could not be restarted" })
- })
- }),
-
- loudness: Stream.unwrap(
- Effect.gen(function*() {
- const { graph } = yield* session("analyze")
- const current = yield* SynchronizedRef.get(graph)
- if (!AudioGraph.$is("Loaded")(current)) {
- return yield* fail("analyze", "The audio analyser is not ready")
- }
- const samples = new Uint8Array(current.analyserNode.fftSize)
- return Stream.fromEffectSchedule(
- Effect.sync(() => {
- current.analyserNode.getByteTimeDomainData(samples)
- let sum = 0
- let peak = 0
- for (const value of samples) {
- const normalized = (value - 128) / 128
- sum += normalized * normalized
- peak = Math.max(peak, Math.abs(normalized))
- }
- const rms = Math.sqrt(sum / samples.length)
- return { rms, peak, decibels: 20 * Math.log10(Math.max(rms, 0.0001)) } satisfies LoudnessSample
- }),
- Schedule.spaced("100 millis")
- )
- })
- ),
-
- reportError: (message: string) => Effect.logError(message)
- }
- })
- }
-) {
- static readonly layer = Layer.effect(this)(this.make)
-}
diff --git a/examples/playground/src/examples/microwave/MicrowavePage.tsx b/examples/playground/src/examples/microwave/MicrowavePage.tsx
deleted file mode 100644
index c6417f0..0000000
--- a/examples/playground/src/examples/microwave/MicrowavePage.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { useAtomSet, useAtomValue } from "@effect/atom-react"
-import { Match } from "effect"
-import { ExamplePage } from "../../components/ExamplePage.tsx"
-import { microwaveAtom } from "./atoms.ts"
-import { MicrowaveEvents } from "./machine.ts"
-
-export function MicrowavePage() {
- const stateResult = useAtomValue(microwaveAtom.state)
- const send = useAtomSet(microwaveAtom.send)
-
- return (
-
- {Match.value(stateResult).pipe(
- Match.tagsExhaustive({
- Initial: () => Starting the microwave…
,
- Failure: () => The microwave failed to start.
,
- Success: ({ value: state }) => {
- const oven = state.state
- const open = oven.path === "Oven.Open"
- const cooking = oven.path === "Oven.Closed" && oven.state.path === "Oven.Closed.Cooking"
- const elapsedSeconds = cooking ? oven.state.value.elapsedSeconds : 0
- const engineName = cooking ? "Cooking" : "Idle"
- const doorName = open ? "Open" : "Closed"
-
- return (
-
-
-
-
-
-
- {String(elapsedSeconds).padStart(2, "0")}
-
-
-
-
-
-
Hierarchical safety state
-
{cooking ? `Cooking · ${elapsedSeconds}s` : open ? "Idle · door open" : "Idle · door closed"}
-
- Engine: {engineName} · Door: {doorName}
-
-
- send(MicrowaveEvents.PowerPressed())}
- >
- {cooking ? "Stop" : "Start"}
-
-
- send(
- open ? MicrowaveEvents.DoorClosed() : MicrowaveEvents.DoorOpened()
- )}
- >
- {open ? "Close door" : "Open door"}
-
-
-
- {open
- ? "Power is disabled while the door is open."
- : "Opening the door while cooking stops the engine."}
-
-
-
- )
- }
- })
- )}
-
- )
-}
diff --git a/examples/playground/src/examples/microwave/atoms.ts b/examples/playground/src/examples/microwave/atoms.ts
deleted file mode 100644
index 2e6ff9f..0000000
--- a/examples/playground/src/examples/microwave/atoms.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { AtomMachine } from "@typeonce/effect-machine/reactivity"
-import { MicrowaveMachine } from "./machine.ts"
-
-export const microwaveAtom = AtomMachine.make(MicrowaveMachine)
diff --git a/examples/playground/src/examples/microwave/machine.ts b/examples/playground/src/examples/microwave/machine.ts
deleted file mode 100644
index 95edca5..0000000
--- a/examples/playground/src/examples/microwave/machine.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Schema } from "effect"
-
-export const MicrowaveState = Schema.TaggedUnion({
- Cooking: { elapsedSeconds: Schema.Number }
-})
-
-export const MicrowaveEvents = Machine.events(
- Schema.TaggedUnion({
- PowerPressed: {},
- DoorOpened: {},
- DoorClosed: {}
- })
-)
-
-export const MicrowaveStates = Machine.states({
- Oven: {
- initial: "Closed",
- states: {
- Closed: {
- initial: "Idle",
- states: {
- Idle: {},
- Cooking: MicrowaveState.cases.Cooking
- }
- },
- Open: {}
- }
- }
-})
-
-export const MicrowaveMachine = Machine.make({
- id: "Microwave",
- states: MicrowaveStates.states,
- events: MicrowaveEvents,
- initial: (to) =>
- to.Oven.initial.resolve(({ target }) => target.from((oven) => oven.Closed.from((closed) => closed.Idle.from())))
-}).handle({
- Oven: {
- states: {
- Closed: {
- on: {
- DoorOpened: (to) => to.branch.Oven.Open()
- },
- states: {
- Idle: {
- on: {
- PowerPressed: (to) => to.local.Cooking().resolve(({ target }) => target.from({ elapsedSeconds: 0 }))
- }
- },
- Cooking: {
- invoke: (from) =>
- from.timer("cooking-second", "1 second").onDone((to) =>
- to.local.Cooking().resolve(({ state, target }) =>
- target.from({ elapsedSeconds: state.elapsedSeconds + 1 })
- )
- ),
- on: {
- PowerPressed: (to) => to.local.Idle()
- }
- }
- }
- },
- Open: {
- on: {
- DoorClosed: (to) => to.local.Closed.initial
- }
- }
- }
- }
-})
diff --git a/examples/playground/src/examples/traffic-light/TrafficLightPage.tsx b/examples/playground/src/examples/traffic-light/TrafficLightPage.tsx
deleted file mode 100644
index a70c5d7..0000000
--- a/examples/playground/src/examples/traffic-light/TrafficLightPage.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { useAtomSet, useAtomValue } from "@effect/atom-react"
-import { Match } from "effect"
-import { ExamplePage } from "../../components/ExamplePage.tsx"
-import { trafficLightAtom } from "./atoms.ts"
-import { trafficLightDurations, TrafficLightEvents } from "./machine.ts"
-
-export function TrafficLightPage() {
- const stateResult = useAtomValue(trafficLightAtom.state)
- const send = useAtomSet(trafficLightAtom.send)
-
- return (
-
- {Match.value(stateResult).pipe(
- Match.tagsExhaustive({
- Initial: () => Starting the signal cycle…
,
- Failure: () => The traffic light failed to start.
,
- Success: ({ value: state }) => {
- const signal = state.path
- const red = signal === "Red" || signal === "RedYellow"
- const yellow = signal === "RedYellow" || signal === "Yellow"
- const green = signal === "Green"
- const duration = trafficLightDurations[signal]
-
- return (
-
-
-
-
-
-
-
-
Current state
-
{signal === "RedYellow" ? "Red + yellow" : signal}
-
- The next transition is scheduled in {(duration / 1_000).toFixed(1)} seconds by a state-scoped{` `}
- an inline after invocation.
-
-
-
-
-
- send(TrafficLightEvents.Reset())}>
- Reset cycle
-
-
-
-
- )
- }
- })
- )}
-
- )
-}
diff --git a/examples/playground/src/examples/traffic-light/atoms.ts b/examples/playground/src/examples/traffic-light/atoms.ts
deleted file mode 100644
index b47e61e..0000000
--- a/examples/playground/src/examples/traffic-light/atoms.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { AtomMachine } from "@typeonce/effect-machine/reactivity"
-import { TrafficLightMachine } from "./machine.ts"
-
-export const trafficLightAtom = AtomMachine.make(TrafficLightMachine)
diff --git a/examples/playground/src/examples/traffic-light/machine.ts b/examples/playground/src/examples/traffic-light/machine.ts
deleted file mode 100644
index ed66f8b..0000000
--- a/examples/playground/src/examples/traffic-light/machine.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Schema } from "effect"
-
-export const TrafficLightEvents = Machine.events(
- Schema.TaggedUnion({
- Reset: {}
- })
-)
-
-export const trafficLightDurations = {
- Red: 4_000,
- RedYellow: 1_000,
- Green: 4_000,
- Yellow: 1_500
-} as const
-
-export const TrafficLightStates = Machine.states({
- Red: {},
- RedYellow: {},
- Green: {},
- Yellow: {}
-})
-
-export const TrafficLightMachine = Machine.make({
- id: "TrafficLight",
- states: TrafficLightStates.states,
- events: TrafficLightEvents,
- initial: (to) => to.Red()
-}).handle({
- Red: {
- invoke: (from) => from.timer("red-timer", trafficLightDurations.Red).onDone((to) => to.full.RedYellow()),
- on: {
- Reset: (to) => to.full.Red().resolve(({ target }) => target.from(), { reenter: true })
- }
- },
- RedYellow: {
- invoke: (from) => from.timer("red-yellow-timer", trafficLightDurations.RedYellow).onDone((to) => to.full.Green()),
- on: {
- Reset: (to) => to.full.Red()
- }
- },
- Green: {
- invoke: (from) => from.timer("green-timer", trafficLightDurations.Green).onDone((to) => to.full.Yellow()),
- on: {
- Reset: (to) => to.full.Red()
- }
- },
- Yellow: {
- invoke: (from) => from.timer("yellow-timer", trafficLightDurations.Yellow).onDone((to) => to.full.Red()),
- on: {
- Reset: (to) => to.full.Red()
- }
- }
-})
diff --git a/examples/playground/src/examples/turnstile/TurnstilePage.tsx b/examples/playground/src/examples/turnstile/TurnstilePage.tsx
deleted file mode 100644
index b827527..0000000
--- a/examples/playground/src/examples/turnstile/TurnstilePage.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { useAtomSet, useAtomValue } from "@effect/atom-react"
-import { Match } from "effect"
-import { useState } from "react"
-import { ExamplePage } from "../../components/ExamplePage.tsx"
-import { turnstileAtom } from "./atoms.ts"
-import { TurnstileEvents } from "./machine.ts"
-
-export function TurnstilePage() {
- const stateResult = useAtomValue(turnstileAtom.state)
- const send = useAtomSet(turnstileAtom.send)
- const [lastCommand, setLastCommand] = useState("Insert a coin to unlock the gate.")
-
- return (
-
- {Match.value(stateResult).pipe(
- Match.tagsExhaustive({
- Initial: () => Starting the turnstile…
,
- Failure: () => The turnstile failed to start.
,
- Success: ({ value: state }) => {
- const locked = state.path === "Locked"
- return (
-
-
-
-
Current state
-
{locked ? "Locked" : "Unlocked"}
-
{lastCommand}
-
- {
- setLastCommand(
- locked ? "Coin accepted. Gate unlocked." : "Coin ignored: the gate is already unlocked."
- )
- send(TurnstileEvents.CoinInserted())
- }}
- >
- Insert coin
-
- {
- setLastCommand(
- locked ? "Push ignored: insert a coin first." : "Gate pushed. Turnstile locked again."
- )
- send(TurnstileEvents.GatePushed())
- }}
- >
- Push gate
-
-
-
-
- )
- }
- })
- )}
-
- )
-}
diff --git a/examples/playground/src/examples/turnstile/atoms.ts b/examples/playground/src/examples/turnstile/atoms.ts
deleted file mode 100644
index 847ca18..0000000
--- a/examples/playground/src/examples/turnstile/atoms.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { AtomMachine } from "@typeonce/effect-machine/reactivity"
-import { TurnstileMachine } from "./machine.ts"
-
-export const turnstileAtom = AtomMachine.make(TurnstileMachine)
diff --git a/examples/playground/src/examples/turnstile/machine.ts b/examples/playground/src/examples/turnstile/machine.ts
deleted file mode 100644
index 102979d..0000000
--- a/examples/playground/src/examples/turnstile/machine.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Schema } from "effect"
-
-export const TurnstileEvents = Machine.events(
- Schema.TaggedUnion({
- CoinInserted: {},
- GatePushed: {}
- })
-)
-
-export const TurnstileStates = Machine.states({
- Locked: {},
- Unlocked: {}
-})
-
-export const TurnstileMachine = Machine.make({
- id: "Turnstile",
- states: TurnstileStates.states,
- events: TurnstileEvents,
- initial: (to) => to.Locked()
-}).handle({
- Locked: {
- on: {
- CoinInserted: (to) => to.full.Unlocked()
- }
- },
- Unlocked: {
- on: {
- GatePushed: (to) => to.full.Locked()
- }
- }
-})
diff --git a/examples/playground/src/examples/worker-tabs/WorkerTabsPage.tsx b/examples/playground/src/examples/worker-tabs/WorkerTabsPage.tsx
deleted file mode 100644
index 9980eec..0000000
--- a/examples/playground/src/examples/worker-tabs/WorkerTabsPage.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-import { useEffect, useRef, useState } from "react"
-import { ExamplePage } from "../../components/ExamplePage.tsx"
-import { type SharedEvent, type SharedSnapshot, SharedTransportEvents } from "./machine.ts"
-import type { WorkerResponse } from "./protocol.ts"
-import { createTabChannel } from "./tab-channel.ts"
-import { createMachineWorker, type MachineWorkerClient } from "./worker-client.ts"
-
-export function WorkerTabsPage() {
- const workerRef = useRef(null)
- const channelRef = useRef | null>(null)
- const snapshotRef = useRef(null)
- const tabId = useRef(crypto.randomUUID().slice(0, 8))
- const [workerStatus, setWorkerStatus] = useState("starting")
- const [tabMessage, setTabMessage] = useState("Waiting for another tab…")
- const [snapshot, setSnapshot] = useState(null)
-
- useEffect(() => {
- const worker = createMachineWorker()
- const channel = createTabChannel()
- workerRef.current = worker
- channelRef.current = channel
-
- const unsubscribeWorker = worker.subscribe((response: WorkerResponse) => {
- switch (response._tag) {
- case "Ready":
- setWorkerStatus("ready")
- break
- case "Pong":
- setWorkerStatus(`pong ${response.requestId.slice(0, 8)}`)
- break
- case "MachineSnapshot":
- snapshotRef.current = response.snapshot
- setSnapshot(response.snapshot)
- setWorkerStatus(response.lifecycle)
- break
- case "WorkerError":
- setWorkerStatus(response.message)
- }
- })
-
- const unsubscribeTabs = channel.subscribe((message) => {
- switch (message._tag) {
- case "MachineEvent":
- if (message.senderId !== tabId.current) {
- worker.sendEvent(message.event)
- setTabMessage(`Applied ${message.event._tag} from tab ${message.senderId}.`)
- }
- break
- case "SyncRequest": {
- const current = snapshotRef.current
- if (message.requesterId !== tabId.current && current !== null) {
- channel.publishState(tabId.current, message.requesterId, {
- active: current.path === "Active",
- count: current.value.count
- })
- }
- break
- }
- case "SyncState":
- if (message.recipientId === tabId.current) {
- worker.sendEvent(SharedTransportEvents.Synchronized({ active: message.active, count: message.count }))
- setTabMessage(`Synchronized with tab ${message.senderId}.`)
- }
- }
- })
-
- channel.requestSync(tabId.current)
-
- return () => {
- unsubscribeWorker()
- unsubscribeTabs()
- worker.close()
- channel.close()
- workerRef.current = null
- channelRef.current = null
- snapshotRef.current = null
- }
- }, [])
-
- const send = (event: SharedEvent) => {
- workerRef.current?.sendEvent(event)
- channelRef.current?.publishEvent(tabId.current, event)
- setTabMessage(`Published ${event._tag} from this tab.`)
- }
-
- const active = snapshot?.path === "Active"
- const count = snapshot?.value.count ?? 0
-
- return (
-
-
-
- Worker-hosted machine
- {active ? "Active" : "Idle"}
- {count}
-
-
- send(
- active ? SharedTransportEvents.Incremented() : SharedTransportEvents.Started()
- )}
- >
- {active ? "Increment" : "Start"}
-
- send(SharedTransportEvents.Reset())}>
- Reset
-
- send(SharedTransportEvents.Stopped())}
- >
- Stop
-
-
-
-
-
-
-
- Web Worker
- {workerStatus}
- workerRef.current?.send({ _tag: "Ping", requestId: crypto.randomUUID() })}
- >
- Ping worker
-
-
-
-
- Tab {tabId.current}
- {tabMessage}
- channelRef.current?.requestSync(tabId.current)}>
- Synchronize now
-
-
-
-
-
- )
-}
diff --git a/examples/playground/src/examples/worker-tabs/machine.ts b/examples/playground/src/examples/worker-tabs/machine.ts
deleted file mode 100644
index 304476b..0000000
--- a/examples/playground/src/examples/worker-tabs/machine.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Schema } from "effect"
-import { type SharedEvent, SharedMachineEvents } from "./protocol.ts"
-
-export type { SharedEvent } from "./protocol.ts"
-
-export const SharedMachineState = Schema.TaggedUnion({
- Idle: { count: Schema.Number },
- Active: { count: Schema.Number }
-})
-
-export const SharedMachineStates = Machine.states(SharedMachineState.cases)
-
-// Worker and BroadcastChannel messages need decoded, cloneable data rather
-// than the opaque instructions returned by Machine.events.
-export const SharedTransportEvents = {
- Started: (): SharedEvent => ({ _tag: "Started" }),
- Incremented: (): SharedEvent => ({ _tag: "Incremented" }),
- Reset: (): SharedEvent => ({ _tag: "Reset" }),
- Stopped: (): SharedEvent => ({ _tag: "Stopped" }),
- Synchronized: (fields: { readonly active: boolean; readonly count: number }): SharedEvent => ({
- _tag: "Synchronized",
- ...fields
- })
-}
-
-export const SharedMachine = Machine.make({
- id: "WorkerHostedMachine",
- states: SharedMachineStates.states,
- events: SharedMachineEvents,
- initial: (to) => to.Idle().resolve(({ target }) => target.from({ count: 0 }))
-}).handle({
- Idle: {
- on: {
- Started: (to) => to.full.Active().resolve(({ state, target }) => target.from({ count: state.count })),
- Reset: (to) => to.full.Idle().resolve(({ target }) => target.from({ count: 0 })),
- Synchronized: (to) =>
- to.branches({ active: { target: to.full.Active() }, idle: { target: to.full.Idle() } }).resolve((
- { event, select }
- ) =>
- event.active
- ? select.active.from({ count: event.count })
- : select.idle.from({ count: event.count })
- )
- }
- },
- Active: {
- on: {
- Incremented: (to) => to.full.Active().resolve(({ state, target }) => target.from({ count: state.count + 1 })),
- Reset: (to) => to.full.Active().resolve(({ target }) => target.from({ count: 0 })),
- Stopped: (to) => to.full.Idle().resolve(({ state, target }) => target.from({ count: state.count })),
- Synchronized: (to) =>
- to.branches({ active: { target: to.full.Active() }, idle: { target: to.full.Idle() } }).resolve((
- { event, select }
- ) =>
- event.active
- ? select.active.from({ count: event.count })
- : select.idle.from({ count: event.count })
- )
- }
- }
-})
-
-export type SharedSnapshot = Machine.Snapshot
diff --git a/examples/playground/src/examples/worker-tabs/machine.worker.ts b/examples/playground/src/examples/worker-tabs/machine.worker.ts
deleted file mode 100644
index 80dc748..0000000
--- a/examples/playground/src/examples/worker-tabs/machine.worker.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-///
-
-import { Machine } from "@typeonce/effect-machine"
-import { Effect, Result, Schema, Stream } from "effect"
-import { type SharedEvent, SharedMachine } from "./machine.ts"
-import { WorkerRequestSchema, type WorkerResponse } from "./protocol.ts"
-
-const workerScope: DedicatedWorkerGlobalScope = self as DedicatedWorkerGlobalScope
-const decodeRequest = Schema.decodeUnknownResult(WorkerRequestSchema)
-
-const post = (response: WorkerResponse) => workerScope.postMessage(response)
-
-let deliver: ((event: SharedEvent) => void) | undefined
-const pendingEvents: Array = []
-
-const program = Effect.gen(function*() {
- const ref = yield* Machine.start(SharedMachine)
- const initial = yield* ref.snapshot
-
- if (initial.status === "error") {
- post({ _tag: "WorkerError", message: "The worker-hosted machine failed during startup." })
- return
- }
-
- post({
- _tag: "MachineSnapshot",
- lifecycle: initial.status,
- snapshot: initial.state
- })
-
- deliver = (event) => {
- Effect.runFork(
- ref.send(event).pipe(
- Effect.catchTag(
- "StoppedError",
- () => Effect.sync(() => post({ _tag: "WorkerError", message: "The worker-hosted machine has stopped." }))
- )
- )
- )
- }
-
- for (const event of pendingEvents.splice(0)) deliver(event)
-
- post({ _tag: "Ready" })
-
- yield* Stream.runForEach(ref.changes, (snapshot) =>
- Effect.sync(() => {
- if (snapshot.status === "error") {
- post({ _tag: "WorkerError", message: "The worker-hosted machine failed while processing an event." })
- } else {
- post({
- _tag: "MachineSnapshot",
- lifecycle: snapshot.status,
- snapshot: snapshot.state
- })
- }
- }))
-})
-
-Effect.runFork(program)
-
-workerScope.addEventListener("message", (message: MessageEvent) => {
- const request = decodeRequest(message.data)
- if (Result.isFailure(request)) {
- post({ _tag: "WorkerError", message: "Received an invalid worker request." })
- return
- }
-
- switch (request.success._tag) {
- case "Ping":
- post({ _tag: "Pong", requestId: request.success.requestId })
- return
- case "MachineEvent":
- if (deliver === undefined) {
- pendingEvents.push(request.success.event)
- } else {
- deliver(request.success.event)
- }
- }
-})
-
-export {}
diff --git a/examples/playground/src/examples/worker-tabs/protocol.ts b/examples/playground/src/examples/worker-tabs/protocol.ts
deleted file mode 100644
index c753887..0000000
--- a/examples/playground/src/examples/worker-tabs/protocol.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Schema } from "effect"
-import type { SharedSnapshot } from "./machine.ts"
-
-const SharedMachineEvent = Schema.TaggedUnion({
- Started: {},
- Incremented: {},
- Reset: {},
- Stopped: {},
- Synchronized: { active: Schema.Boolean, count: Schema.Number }
-})
-
-export const SharedMachineEvents = Machine.events(SharedMachineEvent)
-export type SharedEvent = Machine.EventOf
-
-export const WorkerRequestSchema = Schema.TaggedUnion({
- Ping: { requestId: Schema.String },
- MachineEvent: { event: SharedMachineEvent }
-})
-
-export type WorkerRequest = typeof WorkerRequestSchema.Type
-
-export type WorkerResponse =
- | { readonly _tag: "Ready" }
- | { readonly _tag: "Pong"; readonly requestId: string }
- | {
- readonly _tag: "MachineSnapshot"
- readonly lifecycle: "active" | "done" | "stopped"
- readonly snapshot: SharedSnapshot
- }
- | { readonly _tag: "WorkerError"; readonly message: string }
-
-export const TabMessageSchema = Schema.TaggedUnion({
- MachineEvent: {
- senderId: Schema.String,
- sentAt: Schema.Number,
- event: SharedMachineEvent
- },
- SyncRequest: {
- requesterId: Schema.String,
- sentAt: Schema.Number
- },
- SyncState: {
- senderId: Schema.String,
- recipientId: Schema.String,
- sentAt: Schema.Number,
- active: Schema.Boolean,
- count: Schema.Number
- }
-})
-
-export type TabMessage = typeof TabMessageSchema.Type
diff --git a/examples/playground/src/examples/worker-tabs/tab-channel.ts b/examples/playground/src/examples/worker-tabs/tab-channel.ts
deleted file mode 100644
index 1899edd..0000000
--- a/examples/playground/src/examples/worker-tabs/tab-channel.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Result, Schema } from "effect"
-import type { SharedEvent } from "./machine.ts"
-import { type TabMessage, TabMessageSchema } from "./protocol.ts"
-
-const channelName = "effect-machine-example-tabs"
-const decodeMessage = Schema.decodeUnknownResult(TabMessageSchema)
-
-export function createTabChannel() {
- const channel = new BroadcastChannel(channelName)
-
- return {
- publishEvent: (senderId: string, event: SharedEvent) =>
- channel.postMessage(
- {
- _tag: "MachineEvent",
- senderId,
- sentAt: Date.now(),
- event
- } satisfies TabMessage
- ),
- requestSync: (requesterId: string) =>
- channel.postMessage({ _tag: "SyncRequest", requesterId, sentAt: Date.now() } satisfies TabMessage),
- publishState: (
- senderId: string,
- recipientId: string,
- state: { readonly active: boolean; readonly count: number }
- ) =>
- channel.postMessage(
- {
- _tag: "SyncState",
- senderId,
- recipientId,
- sentAt: Date.now(),
- ...state
- } satisfies TabMessage
- ),
- subscribe: (listener: (message: TabMessage) => void) => {
- const onMessage = (event: MessageEvent) => {
- const result = decodeMessage(event.data)
- if (Result.isSuccess(result)) listener(result.success)
- }
- channel.addEventListener("message", onMessage)
- return () => channel.removeEventListener("message", onMessage)
- },
- close: () => channel.close()
- }
-}
diff --git a/examples/playground/src/examples/worker-tabs/worker-client.ts b/examples/playground/src/examples/worker-tabs/worker-client.ts
deleted file mode 100644
index aca4b3a..0000000
--- a/examples/playground/src/examples/worker-tabs/worker-client.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { WorkerRequest, WorkerResponse } from "./protocol.ts"
-
-export function createMachineWorker() {
- const worker = new Worker(new URL("./machine.worker.ts", import.meta.url), {
- type: "module",
- name: "effect-machine-example"
- })
-
- return {
- worker,
- send: (request: WorkerRequest) => worker.postMessage(request),
- sendEvent: (event: Extract["event"]) =>
- worker.postMessage({ _tag: "MachineEvent", event } satisfies WorkerRequest),
- subscribe: (listener: (response: WorkerResponse) => void) => {
- const onMessage = (event: MessageEvent) => listener(event.data)
- worker.addEventListener("message", onMessage)
- return () => worker.removeEventListener("message", onMessage)
- },
- close: () => worker.terminate()
- }
-}
-
-export type MachineWorkerClient = ReturnType
diff --git a/examples/playground/src/main.tsx b/examples/playground/src/main.tsx
deleted file mode 100644
index ab5c64b..0000000
--- a/examples/playground/src/main.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { RegistryProvider } from "@effect/atom-react"
-import { RouterProvider } from "@tanstack/react-router"
-import { StrictMode } from "react"
-import { createRoot } from "react-dom/client"
-import { router } from "./router.tsx"
-import "./styles.css"
-
-const root = document.getElementById("root")
-
-if (root === null) {
- throw new Error("Root element not found")
-}
-
-createRoot(root).render(
-
-
-
-
-
-)
diff --git a/examples/playground/src/router.tsx b/examples/playground/src/router.tsx
deleted file mode 100644
index 7e9ab86..0000000
--- a/examples/playground/src/router.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import { createRootRoute, createRoute, createRouter, Link, Outlet } from "@tanstack/react-router"
-import { MediaPlayerPage } from "./examples/media-player/MediaPlayerPage.tsx"
-import { MicrowavePage } from "./examples/microwave/MicrowavePage.tsx"
-import { TrafficLightPage } from "./examples/traffic-light/TrafficLightPage.tsx"
-import { TurnstilePage } from "./examples/turnstile/TurnstilePage.tsx"
-import { WorkerTabsPage } from "./examples/worker-tabs/WorkerTabsPage.tsx"
-
-const examples = [
- {
- to: "/turnstile" as const,
- title: "Turnstile",
- description: "A compact baseline for states, events, and legal transitions.",
- concepts: ["atomic states", "typed events"]
- },
- {
- to: "/traffic-light" as const,
- title: "Traffic light",
- description: "A timed cycle with state-scoped delays and repeated transitions.",
- concepts: ["timers", "entry and exit"]
- },
- {
- to: "/microwave" as const,
- title: "Microwave",
- description: "A hierarchy that makes cooking with an open door impossible.",
- concepts: ["compound states", "structural invariants"]
- },
- {
- to: "/media-player" as const,
- title: "Media player",
- description: "Keep transport beneath its required audio session while settings remain independent.",
- concepts: ["resource ownership", "parallel states", "Effect services"]
- },
- {
- to: "/worker-tabs" as const,
- title: "Workers and tabs",
- description: "Host a machine off the main thread and synchronize browser tabs.",
- concepts: ["Web Worker", "BroadcastChannel"]
- }
-]
-
-const rootRoute = createRootRoute({
- component: RootLayout
-})
-
-function RootLayout() {
- return (
-
-
-
-
- Effect Machine
-
-
- {examples.map((example) => (
-
- {example.title}
-
- ))}
-
-
-
-
-
-
- )
-}
-
-const homeRoute = createRoute({
- getParentRoute: () => rootRoute,
- path: "/",
- component: HomePage
-})
-
-function HomePage() {
- return (
-
-
-
- {examples.map((example, index) => (
-
-
{String(index + 1).padStart(2, "0")}
-
{example.title}
-
{example.description}
-
- {example.concepts.map((concept) => {concept} )}
-
-
Open example →
-
- ))}
-
-
- )
-}
-
-const turnstileRoute = createRoute({ getParentRoute: () => rootRoute, path: "/turnstile", component: TurnstilePage })
-const trafficLightRoute = createRoute({
- getParentRoute: () => rootRoute,
- path: "/traffic-light",
- component: TrafficLightPage
-})
-const microwaveRoute = createRoute({ getParentRoute: () => rootRoute, path: "/microwave", component: MicrowavePage })
-const mediaPlayerRoute = createRoute({
- getParentRoute: () => rootRoute,
- path: "/media-player",
- component: MediaPlayerPage
-})
-const workerTabsRoute = createRoute({
- getParentRoute: () => rootRoute,
- path: "/worker-tabs",
- component: WorkerTabsPage
-})
-
-const routeTree = rootRoute.addChildren([
- homeRoute,
- turnstileRoute,
- trafficLightRoute,
- microwaveRoute,
- mediaPlayerRoute,
- workerTabsRoute
-])
-
-export const router = createRouter({ routeTree })
-
-declare module "@tanstack/react-router" {
- interface Register {
- router: typeof router
- }
-}
diff --git a/examples/playground/src/styles.css b/examples/playground/src/styles.css
deleted file mode 100644
index 7b10875..0000000
--- a/examples/playground/src/styles.css
+++ /dev/null
@@ -1,966 +0,0 @@
-:root {
- color: #e8ecf4;
- background: #0c0f15;
- font-family:
- Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
- font-synthesis: none;
-}
-
-* {
- box-sizing: border-box;
-}
-
-body {
- margin: 0;
- min-width: 20rem;
- min-height: 100vh;
- background:
- radial-gradient(circle at 8% 0%, rgb(93 117 255 / 18%), transparent 28rem),
- radial-gradient(circle at 95% 18%, rgb(56 214 162 / 10%), transparent 22rem), #0c0f15;
-}
-
-button,
-input {
- font: inherit;
-}
-
-button {
- padding: 0.65rem 0.9rem;
- border: 1px solid #354057;
- border-radius: 0.6rem;
- color: #f4f6fa;
- background: #20283a;
- font-weight: 700;
- cursor: pointer;
-}
-
-button:hover {
- border-color: #7085ff;
- background: #2a3550;
-}
-
-button:disabled {
- border-color: #2b3240;
- color: #626d80;
- background: #181d26;
- cursor: not-allowed;
-}
-
-code {
- font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
-}
-
-.app-shell {
- width: min(76rem, calc(100% - 2rem));
- margin: 0 auto;
-}
-
-.site-header {
- display: flex;
- gap: 2rem;
- align-items: center;
- justify-content: space-between;
- min-height: 4.75rem;
- border-bottom: 1px solid #252b38;
-}
-
-.brand {
- display: inline-flex;
- gap: 0.65rem;
- align-items: center;
- color: #f7f8fb;
- font-weight: 800;
- text-decoration: none;
- white-space: nowrap;
-}
-
-.brand-mark {
- width: 0.85rem;
- height: 0.85rem;
- border: 2px solid #9aaaFF;
- border-radius: 0.2rem;
- box-shadow: 0.28rem 0.28rem 0 #42d6a3;
- transform: rotate(45deg);
-}
-
-nav {
- display: flex;
- gap: 0.3rem;
- overflow-x: auto;
- padding: 0.5rem 0;
-}
-
-nav a {
- padding: 0.5rem 0.7rem;
- border-radius: 0.5rem;
- color: #8f98aa;
- font-size: 0.82rem;
- font-weight: 650;
- text-decoration: none;
- white-space: nowrap;
-}
-
-nav a:hover,
-nav a.is-active {
- color: #eef1ff;
- background: #202638;
-}
-
-main {
- padding: clamp(3rem, 8vw, 6.5rem) 0 6rem;
-}
-
-h1,
-h2,
-p {
- margin-top: 0;
-}
-
-h1 {
- max-width: 13ch;
- margin-bottom: 1.25rem;
- font-size: clamp(3rem, 8vw, 6.6rem);
- line-height: 0.92;
- letter-spacing: -0.07em;
-}
-
-.eyebrow {
- margin-bottom: 1rem;
- color: #7e91ff;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: 0.72rem;
- font-weight: 800;
- letter-spacing: 0.14em;
- text-transform: uppercase;
-}
-
-.lede {
- max-width: 42rem;
- color: #a8b0c0;
- font-size: clamp(1.05rem, 2vw, 1.3rem);
- line-height: 1.65;
-}
-
-.hero {
- margin-bottom: 4rem;
-}
-
-.example-grid {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 1rem;
-}
-
-.example-card {
- position: relative;
- min-height: 19rem;
- padding: 1.5rem;
- overflow: hidden;
- border: 1px solid #293040;
- border-radius: 1rem;
- color: inherit;
- background: linear-gradient(145deg, rgb(29 35 48 / 92%), rgb(17 21 29 / 92%));
- text-decoration: none;
- transition:
- transform 180ms ease,
- border-color 180ms ease;
-}
-
-.example-card:hover {
- border-color: #5264ad;
- transform: translateY(-3px);
-}
-
-.example-card h2 {
- margin: 3.2rem 0 0.7rem;
- font-size: 1.7rem;
- letter-spacing: -0.035em;
-}
-
-.example-card p {
- max-width: 30rem;
- color: #9ca5b6;
- line-height: 1.55;
-}
-
-.example-card ul {
- display: flex;
- gap: 0.4rem;
- margin: 1.25rem 0 3rem;
- padding: 0;
- list-style: none;
-}
-
-.example-card li,
-.file-pill {
- padding: 0.35rem 0.55rem;
- border: 1px solid #343d50;
- border-radius: 999px;
- color: #aeb7c9;
- background: #151a24;
- font-size: 0.72rem;
-}
-
-.card-index {
- color: #6e7d99;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: 0.75rem;
-}
-
-.card-action {
- position: absolute;
- right: 1.5rem;
- bottom: 1.5rem;
- color: #94a4ff;
- font-size: 0.82rem;
- font-weight: 750;
-}
-
-.example-header {
- margin-bottom: 3rem;
-}
-
-.example-header h1 {
- font-size: clamp(2.8rem, 7vw, 5.5rem);
-}
-
-.file-pill {
- display: inline-block;
- margin-top: 0.5rem;
- border-radius: 0.4rem;
- color: #79dcb9;
-}
-
-.workbench {
- min-height: 25rem;
- border: 1px solid #2a3242;
- border-radius: 1.25rem;
- background:
- linear-gradient(rgb(255 255 255 / 2%) 1px, transparent 1px),
- linear-gradient(90deg, rgb(255 255 255 / 2%) 1px, transparent 1px), #111620;
- background-size: 1.5rem 1.5rem;
- box-shadow: 0 2rem 5rem rgb(0 0 0 / 25%);
-}
-
-.example-message {
- display: grid;
- min-height: 25rem;
- place-items: center;
- color: #aeb8ca;
-}
-
-.example-message.is-error {
- color: #ff9ea6;
-}
-
-.machine-demo {
- display: grid;
- grid-template-columns: minmax(12rem, 0.75fr) minmax(16rem, 1.25fr);
- gap: clamp(2rem, 7vw, 6rem);
- align-items: center;
- min-height: 25rem;
- padding: clamp(2rem, 7vw, 5rem);
-}
-
-.machine-demo-copy {
- max-width: 32rem;
-}
-
-.machine-demo-copy h2,
-.worker-counter h2 {
- margin-bottom: 0.8rem;
- font-size: clamp(2rem, 5vw, 3.8rem);
- letter-spacing: -0.055em;
-}
-
-.machine-demo-copy > p:not(.machine-state-label, .safety-note) {
- color: #9da8bb;
- line-height: 1.65;
-}
-
-.machine-state-label {
- margin-bottom: 0.55rem;
- color: #7e91ff;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: 0.7rem;
- font-weight: 800;
- letter-spacing: 0.12em;
- text-transform: uppercase;
-}
-
-.machine-controls {
- display: flex;
- gap: 0.6rem;
- flex-wrap: wrap;
- margin-top: 1.4rem;
-}
-
-.turnstile {
- position: relative;
- width: 8rem;
- height: 8rem;
- justify-self: center;
-}
-
-.turnstile-post {
- position: absolute;
- bottom: 0;
- left: 3rem;
- width: 2rem;
- height: 6.5rem;
- border: 1px solid #8390a9;
- border-radius: 0.5rem 0.5rem 0.2rem 0.2rem;
- background: #343e51;
-}
-
-.turnstile-arm {
- position: absolute;
- z-index: 1;
- top: 2.25rem;
- left: 0.25rem;
- width: 7.5rem;
- height: 0.7rem;
- border-radius: 999px;
- background: #8092ff;
- transform: rotate(-15deg);
- transform-origin: center;
- transition: transform 280ms ease;
-}
-
-.turnstile.is-unlocked .turnstile-arm {
- background: #42d6a3;
- transform: rotate(25deg);
-}
-
-.traffic-light {
- display: grid;
- gap: 0.65rem;
- padding: 0.8rem;
- border: 1px solid #475064;
- border-radius: 1rem;
- background: #202733;
-}
-
-.traffic-light span {
- width: 3.5rem;
- height: 3.5rem;
- border-radius: 50%;
- background: #303746;
- box-shadow: inset 0 0.2rem 0.5rem rgb(0 0 0 / 45%);
-}
-
-.traffic-light .red {
- --signal: #d8555f;
-}
-
-.traffic-light .yellow {
- --signal: #f0bc5e;
-}
-
-.traffic-light .green {
- --signal: #42d6a3;
-}
-
-.traffic-light span.is-active {
- background: var(--signal);
- box-shadow: 0 0 2rem color-mix(in srgb, var(--signal) 45%, transparent);
-}
-
-.traffic-light-demo .traffic-light {
- justify-self: center;
-}
-
-.timer-track {
- height: 0.4rem;
- margin-top: 1.2rem;
- overflow: hidden;
- border-radius: 999px;
- background: #283142;
-}
-
-.timer-track span {
- display: block;
- width: 100%;
- height: 100%;
- border-radius: inherit;
- background: #8092ff;
- transform-origin: left;
- animation: timer-countdown linear forwards;
-}
-
-@keyframes timer-countdown {
- to {
- transform: scaleX(0);
- }
-}
-
-.microwave {
- display: grid;
- grid-template-columns: 10rem 3rem;
- gap: 0.75rem;
- padding: 0.8rem;
- border: 1px solid #4a5366;
- border-radius: 0.8rem;
- background: #343c49;
-}
-
-.microwave-window {
- position: relative;
- display: grid;
- height: 7rem;
- border: 0.4rem solid #202630;
- border-radius: 0.35rem;
- background: radial-gradient(circle, #34485d, #151d27);
- place-items: center;
- transition: transform 220ms ease;
- transform-origin: left;
-}
-
-.microwave-window span {
- width: 3.3rem;
- height: 3.3rem;
- border-radius: 50%;
- background: rgb(232 239 255 / 12%);
-}
-
-.microwave.is-cooking .microwave-window span {
- background: #f1b55a;
- box-shadow: 0 0 2.5rem rgb(241 181 90 / 50%);
- animation: microwave-turn 2s linear infinite;
-}
-
-.microwave.is-open .microwave-window {
- transform: perspective(20rem) rotateY(-22deg);
-}
-
-@keyframes microwave-turn {
- to {
- transform: rotate(360deg);
- }
-}
-
-.microwave-controls {
- display: flex;
- flex-direction: column;
- gap: 0.6rem;
- align-items: center;
- padding-top: 0.4rem;
-}
-
-.microwave-controls span {
- width: 1.5rem;
- height: 1.5rem;
- border-radius: 50%;
- background: #8793a8;
-}
-
-.microwave-controls strong {
- display: grid;
- width: 100%;
- min-height: 1.5rem;
- place-items: center;
- color: #73e3bd;
- background: #131a1d;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: 0.75rem;
-}
-
-.microwave-demo .microwave {
- justify-self: center;
-}
-
-.safety-note {
- margin-top: 1rem;
- color: #f1c679;
- font-size: 0.8rem;
-}
-
-.file-picker {
- display: inline-flex;
- gap: 0.8rem;
- align-items: center;
- margin-bottom: 1.25rem;
- padding: 0.65rem 0.85rem;
- border: 1px dashed #4d5c7b;
- border-radius: 0.65rem;
- color: #b8c1d2;
- cursor: pointer;
-}
-
-.file-picker input {
- max-width: 13rem;
- color: #7f899b;
- font-size: 0.75rem;
-}
-
-.audio-player {
- width: min(100%, 32rem);
- margin-bottom: 0.5rem;
-}
-
-.media-player-message {
- display: grid;
- min-height: 25rem;
- place-items: center;
- color: #aeb8ca;
-}
-
-.media-player-message.is-error,
-.media-error-message {
- color: #ff9ea6;
-}
-
-.media-player-console {
- min-height: 25rem;
- padding: clamp(1rem, 3vw, 2rem);
-}
-
-.media-player-toolbar,
-.media-player-toolbar-actions,
-.media-panel-heading,
-.media-time-row,
-.media-controls {
- display: flex;
- align-items: center;
-}
-
-.media-player-toolbar {
- gap: 1.5rem;
- justify-content: space-between;
- padding-bottom: 1.5rem;
- border-bottom: 1px solid #2c3547;
-}
-
-.media-player-toolbar h2 {
- max-width: 32rem;
- margin: 0;
- overflow: hidden;
- font-size: clamp(1.15rem, 2vw, 1.5rem);
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.media-player-kicker {
- margin-bottom: 0.35rem;
- color: #7785a1;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: 0.7rem;
- font-weight: 750;
- letter-spacing: 0.1em;
- text-transform: uppercase;
-}
-
-.media-player-toolbar-actions {
- gap: 0.75rem;
- flex: none;
-}
-
-.media-state-chip {
- padding: 0.42rem 0.65rem;
- border: 1px solid #485573;
- border-radius: 999px;
- color: #aeb9ce;
- background: #171d28;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: 0.68rem;
- font-weight: 800;
- letter-spacing: 0.08em;
- text-transform: uppercase;
-}
-
-.media-state-chip.is-playing {
- border-color: rgb(66 214 163 / 55%);
- color: #79e4bf;
- box-shadow: 0 0 1.2rem rgb(66 214 163 / 14%);
-}
-
-.media-state-chip.is-loading,
-.media-state-chip.is-buffering,
-.media-state-chip.is-restarting {
- border-color: rgb(244 190 91 / 55%);
- color: #f4c978;
-}
-
-.media-state-chip.is-ended {
- border-color: rgb(161 132 255 / 55%);
- color: #c0aaff;
-}
-
-.media-state-chip.is-failed {
- border-color: rgb(255 113 126 / 55%);
- color: #ff9ea6;
-}
-
-.media-file-button {
- position: relative;
- padding: 0.62rem 0.8rem;
- overflow: hidden;
- border: 1px solid #5065b0;
- border-radius: 0.55rem;
- color: #dbe1ff;
- background: #29355b;
- font-size: 0.76rem;
- font-weight: 750;
- cursor: pointer;
-}
-
-.media-file-button:hover {
- background: #344476;
-}
-
-.media-file-button input,
-.media-audio-element {
- position: absolute;
- width: 1px;
- height: 1px;
- overflow: hidden;
- clip: rect(0 0 0 0);
- clip-path: inset(50%);
- white-space: nowrap;
-}
-
-.media-player-layout {
- display: grid;
- grid-template-columns: minmax(0, 1.2fr) minmax(18rem, 0.8fr);
- gap: 1rem;
- padding-top: 1rem;
-}
-
-.media-now-playing,
-.media-settings,
-.media-state-preview {
- border: 1px solid #2e384b;
- border-radius: 0.9rem;
- background: rgb(17 23 33 / 88%);
-}
-
-.media-now-playing {
- display: grid;
- grid-template-columns: minmax(10rem, 15rem) minmax(12rem, 1fr);
- gap: 1.25rem;
- align-content: center;
- min-height: 29rem;
- padding: clamp(1rem, 3vw, 2rem);
-}
-
-.media-artwork {
- position: relative;
- display: grid;
- grid-row: span 3;
- aspect-ratio: 1;
- place-items: center;
- overflow: hidden;
- border: 1px solid #39465f;
- border-radius: 1rem;
- background:
- radial-gradient(circle at 28% 20%, rgb(128 146 255 / 38%), transparent 38%),
- radial-gradient(circle at 80% 88%, rgb(66 214 163 / 24%), transparent 35%), #161c27;
-}
-
-.media-record {
- position: relative;
- display: grid;
- width: 72%;
- aspect-ratio: 1;
- place-items: center;
- border-radius: 50%;
- background:
- repeating-radial-gradient(circle, transparent 0 5px, rgb(255 255 255 / 8%) 6px 7px),
- radial-gradient(circle, #7e91ff 0 13%, #111722 14% 100%);
- box-shadow: 0 1.4rem 2.5rem rgb(0 0 0 / 42%);
-}
-
-.media-record span {
- width: 5%;
- aspect-ratio: 1;
- border-radius: 50%;
- background: #dce1ee;
-}
-
-.media-artwork.is-playing .media-record {
- animation: media-spin 5s linear infinite;
-}
-
-.media-tonearm {
- position: absolute;
- top: 13%;
- right: 12%;
- width: 0.45rem;
- height: 48%;
- border-radius: 999px;
- background: #a7b0bf;
- box-shadow: 0 0 0 0.25rem #313b4d;
- transform: rotate(18deg);
- transform-origin: top;
-}
-
-@keyframes media-spin {
- to {
- transform: rotate(360deg);
- }
-}
-
-.media-time-row,
-.media-panel-heading {
- justify-content: space-between;
-}
-
-.media-time-row {
- align-self: end;
- color: #8f9aaf;
- font-size: 0.8rem;
-}
-
-.media-time-row strong {
- color: #f2f5fa;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: 2rem;
- letter-spacing: -0.04em;
-}
-
-.media-controls {
- gap: 0.6rem;
- flex-wrap: wrap;
-}
-
-.media-controls button {
- flex: 1 1 5.5rem;
-}
-
-.media-controls button:disabled {
- border-color: #2b3240;
- color: #626d80;
- background: #181d26;
- cursor: not-allowed;
-}
-
-.media-meter {
- align-self: start;
- padding-top: 1rem;
- border-top: 1px solid #2c3545;
-}
-
-.media-panel-heading {
- gap: 1rem;
- color: #8f9aaf;
- font-size: 0.77rem;
-}
-
-.media-panel-heading strong {
- color: #dfe5f0;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: 0.72rem;
-}
-
-.media-meter-track {
- position: relative;
- height: 0.72rem;
- margin-top: 0.8rem;
- border-radius: 999px;
- background: #242c3a;
-}
-
-.media-meter-fill {
- display: block;
- height: 100%;
- border-radius: inherit;
- background: linear-gradient(90deg, #42d6a3, #d4dc69, #ff7a84);
- transition: width 100ms linear;
-}
-
-.media-meter-peak {
- position: absolute;
- top: -0.18rem;
- width: 2px;
- height: 1.08rem;
- background: #fff;
- transition: left 100ms linear;
-}
-
-.media-meter p {
- margin: 0.55rem 0 0;
- color: #737f93;
- font-size: 0.7rem;
-}
-
-.media-player-inspector {
- display: grid;
- gap: 1rem;
- align-content: start;
-}
-
-.media-settings,
-.media-state-preview {
- display: grid;
- gap: 1rem;
- padding: 1.1rem;
-}
-
-.media-settings label {
- display: grid;
- grid-template-columns: 4.5rem minmax(0, 1fr);
- gap: 0.75rem;
- align-items: center;
- color: #a7b0c1;
- font-size: 0.78rem;
-}
-
-.media-settings input[type="range"] {
- width: 100%;
- accent-color: #7e91ff;
-}
-
-.media-settings input[type="checkbox"] {
- justify-self: start;
- accent-color: #7e91ff;
-}
-
-.media-settings select {
- width: 100%;
- padding: 0.45rem 0.55rem;
- border: 1px solid #3a465d;
- border-radius: 0.4rem;
- color: #e5e9f2;
- background: #1d2532;
- font: inherit;
-}
-
-.media-state-preview pre {
- max-height: 16rem;
- margin: 0;
- padding: 0.9rem;
- overflow: auto;
- border: 1px solid #283143;
- border-radius: 0.5rem;
- color: #95e0c4;
- background: #0c1119;
- font-size: 0.67rem;
- line-height: 1.55;
-}
-
-.media-error-message {
- margin: 0;
- padding: 0.8rem 1rem;
- border: 1px solid rgb(255 113 126 / 28%);
- border-radius: 0.65rem;
- background: rgb(255 113 126 / 8%);
- font-size: 0.78rem;
- line-height: 1.45;
-}
-
-.transport-grid {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 1rem;
- width: 100%;
- margin-bottom: 2rem;
- text-align: left;
-}
-
-.worker-demo {
- display: grid;
- grid-template-columns: minmax(14rem, 0.65fr) minmax(24rem, 1.35fr);
- gap: 1rem;
- min-height: 25rem;
- padding: clamp(1rem, 4vw, 2rem);
-}
-
-.worker-counter {
- padding: clamp(1.2rem, 4vw, 2rem);
- border: 1px solid #303a4d;
- border-radius: 0.8rem;
- background: rgb(20 26 37 / 90%);
-}
-
-.counter-value {
- display: block;
- color: #79e4bf;
- font-family: "SFMono-Regular", Consolas, monospace;
- font-size: clamp(4rem, 9vw, 7rem);
- line-height: 1;
- letter-spacing: -0.08em;
-}
-
-.worker-demo .transport-grid {
- margin-bottom: 0;
-}
-
-.transport-grid article {
- min-height: 12rem;
- padding: 1.25rem;
- border: 1px solid #303a4d;
- border-radius: 0.8rem;
- background: rgb(20 26 37 / 90%);
-}
-
-.transport-grid h2 {
- margin-top: 1.25rem;
-}
-
-.transport-grid p {
- min-height: 2.8rem;
- color: #98a2b5;
- font-size: 0.85rem;
-}
-
-.status-dot {
- display: block;
- width: 0.7rem;
- height: 0.7rem;
- border-radius: 50%;
- background: #5fddae;
- box-shadow: 0 0 1rem rgb(95 221 174 / 60%);
-}
-
-.status-dot.channel {
- background: #8092ff;
- box-shadow: 0 0 1rem rgb(128 146 255 / 60%);
-}
-
-.implementation-note {
- padding-top: 1.5rem;
- border-top: 1px solid #2d3546;
-}
-
-@media (max-width: 48rem) {
- .site-header {
- display: block;
- padding-top: 1.25rem;
- }
-
- nav {
- margin-top: 0.75rem;
- }
-
- .example-grid,
- .machine-demo,
- .transport-grid,
- .worker-demo,
- .media-player-layout {
- grid-template-columns: 1fr;
- }
-
- .media-player-toolbar {
- align-items: flex-start;
- flex-direction: column;
- }
-
- .media-player-toolbar-actions {
- width: 100%;
- justify-content: space-between;
- }
-
- .media-now-playing {
- grid-template-columns: 1fr;
- }
-
- .media-artwork {
- grid-row: auto;
- width: min(100%, 17rem);
- justify-self: center;
- }
-
- .example-card {
- min-height: 17rem;
- }
-}
diff --git a/examples/playground/tsconfig.json b/examples/playground/tsconfig.json
deleted file mode 100644
index bb95bc0..0000000
--- a/examples/playground/tsconfig.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/tsconfig",
- "compilerOptions": {
- "target": "ES2022",
- "useDefineForClassFields": true,
- "lib": ["ES2022", "DOM", "DOM.Iterable", "WebWorker"],
- "module": "ESNext",
- "moduleResolution": "Bundler",
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": true,
- "strict": true,
- "skipLibCheck": true,
- "noEmit": true,
- "jsx": "react-jsx",
- "types": ["vite/client"]
- },
- "include": ["src", "vite.config.ts"]
-}
diff --git a/examples/playground/vite.config.ts b/examples/playground/vite.config.ts
deleted file mode 100644
index c989213..0000000
--- a/examples/playground/vite.config.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import react from "@vitejs/plugin-react"
-import { defineConfig } from "vite"
-
-export default defineConfig({
- plugins: [react()]
-})
diff --git a/examples/pokemon/.gitignore b/examples/pokemon/.gitignore
deleted file mode 100644
index 3bdd52e..0000000
--- a/examples/pokemon/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-node_modules/
-dist/
-.DS_Store
diff --git a/examples/pokemon/README.md b/examples/pokemon/README.md
deleted file mode 100644
index 30d7075..0000000
--- a/examples/pokemon/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Pokémon statechart example
-
-A standalone React and Vite application demonstrating hierarchical parent and
-child workflows and invoked Effect machines with a live PokéAPI integration.
-
-This project installs `@typeonce/effect-machine` from the repository root
-through a local `file:` dependency. It keeps an independent lockfile and
-dependency graph while validating the current library build.
-
-```sh
-pnpm install --frozen-lockfile
-pnpm dev
-```
-
-Open the displayed Vite URL and select **Machine**. The application loads a
-random team of six Pokémon. You can select a team member, search by Pokémon
-name, replace from a search result, or replace a team member with a random
-Pokémon.
-
-Run the complete standalone validation with:
-
-```sh
-pnpm check
-```
diff --git a/examples/pokemon/index.html b/examples/pokemon/index.html
deleted file mode 100644
index 9fbbc05..0000000
--- a/examples/pokemon/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- Effect Machine Playground
-
-
-
-
-
-
diff --git a/examples/pokemon/package.json b/examples/pokemon/package.json
deleted file mode 100644
index c4bf9c9..0000000
--- a/examples/pokemon/package.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "name": "@typeonce/effect-machine-example-pokemon",
- "version": "0.0.0",
- "private": true,
- "description": "Pokémon statechart example using the local @typeonce/effect-machine package",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "typecheck": "tsc --noEmit",
- "build": "pnpm typecheck && vite build",
- "preview": "vite preview",
- "check": "pnpm build"
- },
- "dependencies": {
- "@effect/atom-react": "4.0.0-rc.111",
- "@tanstack/react-router": "1.170.18",
- "@typeonce/effect-machine": "file:../..",
- "effect": "4.0.0-rc.111",
- "react": "19.2.7",
- "react-dom": "19.2.7",
- "scheduler": "0.27.0"
- },
- "devDependencies": {
- "@types/react": "19.2.17",
- "@types/react-dom": "19.2.2",
- "@vitejs/plugin-react": "6.0.4",
- "typescript": "6.0.3",
- "vite": "8.1.5"
- },
- "packageManager": "pnpm@10.17.1",
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
-}
diff --git a/examples/pokemon/pnpm-lock.yaml b/examples/pokemon/pnpm-lock.yaml
deleted file mode 100644
index 6b43d87..0000000
--- a/examples/pokemon/pnpm-lock.yaml
+++ /dev/null
@@ -1,1096 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-overrides:
- effect: 4.0.0-rc.111
- '@effect/atom-react': 4.0.0-rc.111
-
-importers:
-
- .:
- dependencies:
- '@effect/atom-react':
- specifier: 4.0.0-rc.111
- version: 4.0.0-rc.111(effect@4.0.0-rc.111)(react@19.2.7)(scheduler@0.27.0)
- '@tanstack/react-router':
- specifier: 1.170.18
- version: 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
- '@typeonce/effect-machine':
- specifier: file:../..
- version: file:../..(effect@4.0.0-rc.111)
- effect:
- specifier: 4.0.0-rc.111
- version: 4.0.0-rc.111
- react:
- specifier: 19.2.7
- version: 19.2.7
- react-dom:
- specifier: 19.2.7
- version: 19.2.7(react@19.2.7)
- scheduler:
- specifier: 0.27.0
- version: 0.27.0
- devDependencies:
- '@types/react':
- specifier: 19.2.17
- version: 19.2.17
- '@types/react-dom':
- specifier: 19.2.2
- version: 19.2.2(@types/react@19.2.17)
- '@vitejs/plugin-react':
- specifier: 6.0.4
- version: 6.0.4(vite@8.1.5(esbuild@0.28.1)(yaml@2.9.0))
- typescript:
- specifier: 6.0.3
- version: 6.0.3
- vite:
- specifier: 8.1.5
- version: 8.1.5(esbuild@0.28.1)(yaml@2.9.0)
-
-packages:
-
- '@effect/atom-react@4.0.0-rc.111':
- resolution: {integrity: sha512-MQgX2+ayms/J686g19vV2AkLSocMaubZmFf2VFrWN3fwPg60XXsaPtlQKdcsIy/McxVGZYgYUPXBVVtcmUjjSA==}
- peerDependencies:
- effect: 4.0.0-rc.111
- react: '>=19.2.7 <20.0.0'
- scheduler: '>=0.27.0 <0.28.0'
-
- '@emnapi/core@1.11.1':
- resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
-
- '@emnapi/runtime@1.11.1':
- resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
-
- '@emnapi/wasi-threads@1.2.2':
- resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
-
- '@esbuild/aix-ppc64@0.28.1':
- resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [aix]
-
- '@esbuild/android-arm64@0.28.1':
- resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm@0.28.1':
- resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-x64@0.28.1':
- resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/darwin-arm64@0.28.1':
- resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-x64@0.28.1':
- resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [darwin]
-
- '@esbuild/freebsd-arm64@0.28.1':
- resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [freebsd]
-
- '@esbuild/freebsd-x64@0.28.1':
- resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/linux-arm64@0.28.1':
- resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm@0.28.1':
- resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-ia32@0.28.1':
- resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-loong64@0.28.1':
- resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
- engines: {node: '>=18'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-mips64el@0.28.1':
- resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
- engines: {node: '>=18'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.28.1':
- resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.28.1':
- resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
- engines: {node: '>=18'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-s390x@0.28.1':
- resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
- engines: {node: '>=18'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-x64@0.28.1':
- resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/netbsd-arm64@0.28.1':
- resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [netbsd]
-
- '@esbuild/netbsd-x64@0.28.1':
- resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/openbsd-arm64@0.28.1':
- resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openbsd]
-
- '@esbuild/openbsd-x64@0.28.1':
- resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/openharmony-arm64@0.28.1':
- resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openharmony]
-
- '@esbuild/sunos-x64@0.28.1':
- resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/win32-arm64@0.28.1':
- resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-ia32@0.28.1':
- resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-x64@0.28.1':
- resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [win32]
-
- '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4':
- resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==}
- cpu: [arm64]
- os: [darwin]
-
- '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4':
- resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==}
- cpu: [x64]
- os: [darwin]
-
- '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4':
- resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==}
- cpu: [arm64]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4':
- resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==}
- cpu: [arm]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4':
- resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==}
- cpu: [x64]
- os: [linux]
-
- '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4':
- resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==}
- cpu: [x64]
- os: [win32]
-
- '@napi-rs/wasm-runtime@1.1.6':
- resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
- peerDependencies:
- '@emnapi/core': ^1.7.1
- '@emnapi/runtime': ^1.7.1
-
- '@oxc-project/types@0.139.0':
- resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==}
-
- '@rolldown/binding-android-arm64@1.1.5':
- resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [android]
-
- '@rolldown/binding-darwin-arm64@1.1.5':
- resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [darwin]
-
- '@rolldown/binding-darwin-x64@1.1.5':
- resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [darwin]
-
- '@rolldown/binding-freebsd-x64@1.1.5':
- resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [freebsd]
-
- '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
- resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm]
- os: [linux]
-
- '@rolldown/binding-linux-arm64-gnu@1.1.5':
- resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-arm64-musl@1.1.5':
- resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
- '@rolldown/binding-linux-ppc64-gnu@1.1.5':
- resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [ppc64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-s390x-gnu@1.1.5':
- resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [s390x]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-x64-gnu@1.1.5':
- resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [linux]
- libc: [glibc]
-
- '@rolldown/binding-linux-x64-musl@1.1.5':
- resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [linux]
- libc: [musl]
-
- '@rolldown/binding-openharmony-arm64@1.1.5':
- resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [openharmony]
-
- '@rolldown/binding-wasm32-wasi@1.1.5':
- resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [wasm32]
-
- '@rolldown/binding-win32-arm64-msvc@1.1.5':
- resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [arm64]
- os: [win32]
-
- '@rolldown/binding-win32-x64-msvc@1.1.5':
- resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- cpu: [x64]
- os: [win32]
-
- '@rolldown/pluginutils@1.0.1':
- resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
-
- '@standard-schema/spec@1.1.0':
- resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
-
- '@tanstack/history@1.162.0':
- resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==}
- engines: {node: '>=20.19'}
-
- '@tanstack/react-router@1.170.18':
- resolution: {integrity: sha512-wpbGYZEp/fmz1q4bn7BD8VZ+/VZ7GBqSJv5V969pU+chP8y7dquWDmKTFMohvUegb9lg12m1uPVvD6kB2wORvQ==}
- engines: {node: '>=20.19'}
- peerDependencies:
- react: '>=18.0.0 || >=19.0.0'
- react-dom: '>=18.0.0 || >=19.0.0'
-
- '@tanstack/react-store@0.9.3':
- resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==}
- peerDependencies:
- react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
-
- '@tanstack/router-core@1.171.15':
- resolution: {integrity: sha512-IILCDcLaItMZQ2jEmCABHY1Nhjjn5XUvwpQp3e4Nmu+vfg0BgYFuu/QASz2SwE2ZNbVMrvt8X/wxa+Gg5aErxA==}
- engines: {node: '>=20.19'}
-
- '@tanstack/store@0.9.3':
- resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==}
-
- '@tybys/wasm-util@0.10.3':
- resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
-
- '@typeonce/effect-machine@file:../..':
- resolution: {directory: ../.., type: directory}
- engines: {node: '>=20'}
- peerDependencies:
- effect: 4.0.0-rc.111
-
- '@types/react-dom@19.2.2':
- resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==}
- peerDependencies:
- '@types/react': ^19.2.0
-
- '@types/react@19.2.17':
- resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}
-
- '@vitejs/plugin-react@6.0.4':
- resolution: {integrity: sha512-XcCQz0TBpBgljhj0gMuuDj49i6Ytqh5q1osT/Gp5uAVJUCTWxyskk/l1jwYYiu2xcNHHipdMz40EGfM1VdamVg==}
- engines: {node: ^20.19.0 || >=22.12.0}
- peerDependencies:
- '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0
- babel-plugin-react-compiler: ^1.0.0
- vite: ^8.0.0
- peerDependenciesMeta:
- '@rolldown/plugin-babel':
- optional: true
- babel-plugin-react-compiler:
- optional: true
-
- cookie-es@3.1.1:
- resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==}
-
- csstype@3.2.3:
- resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
-
- detect-libc@2.1.2:
- resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
- engines: {node: '>=8'}
-
- effect@4.0.0-rc.111:
- resolution: {integrity: sha512-ASd5L58EIR0CUNueZNKKjSsyOCd+2alxOAIaTcHaqkJkPsaYSsw5Cg/cfANk5K4Jr2YsX756xvX11shzqsreWA==}
-
- esbuild@0.28.1:
- resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
- engines: {node: '>=18'}
- hasBin: true
-
- fast-check@4.9.0:
- resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==}
- engines: {node: '>=12.17.0'}
-
- fdir@6.5.0:
- resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
- engines: {node: '>=12.0.0'}
- peerDependencies:
- picomatch: ^3 || ^4
- peerDependenciesMeta:
- picomatch:
- optional: true
-
- fsevents@2.3.3:
- resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
- isbot@5.2.1:
- resolution: {integrity: sha512-dJ+LpKyClQZ7NG+j3OensC/mAZkGpukE9YUrgPYvAZj2doVL0edfDgywTUh5CXa0o+nW9a1V9e5+CJTX8+SxRw==}
- engines: {node: '>=18'}
-
- lightningcss-android-arm64@1.33.0:
- resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [android]
-
- lightningcss-darwin-arm64@1.33.0:
- resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [darwin]
-
- lightningcss-darwin-x64@1.33.0:
- resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [darwin]
-
- lightningcss-freebsd-x64@1.33.0:
- resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [freebsd]
-
- lightningcss-linux-arm-gnueabihf@1.33.0:
- resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm]
- os: [linux]
-
- lightningcss-linux-arm64-gnu@1.33.0:
- resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [linux]
- libc: [glibc]
-
- lightningcss-linux-arm64-musl@1.33.0:
- resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [linux]
- libc: [musl]
-
- lightningcss-linux-x64-gnu@1.33.0:
- resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [linux]
- libc: [glibc]
-
- lightningcss-linux-x64-musl@1.33.0:
- resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [linux]
- libc: [musl]
-
- lightningcss-win32-arm64-msvc@1.33.0:
- resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
- engines: {node: '>= 12.0.0'}
- cpu: [arm64]
- os: [win32]
-
- lightningcss-win32-x64-msvc@1.33.0:
- resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
- engines: {node: '>= 12.0.0'}
- cpu: [x64]
- os: [win32]
-
- lightningcss@1.33.0:
- resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
- engines: {node: '>= 12.0.0'}
-
- msgpackr-extract@3.0.4:
- resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==}
- hasBin: true
-
- msgpackr@2.0.5:
- resolution: {integrity: sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==}
-
- nanoid@3.3.16:
- resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- node-gyp-build-optional-packages@5.2.2:
- resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
- hasBin: true
-
- picocolors@1.1.1:
- resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
-
- picomatch@4.0.5:
- resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==}
- engines: {node: '>=12'}
-
- postcss@8.5.23:
- resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==}
- engines: {node: ^10 || ^12 || >=14}
-
- pure-rand@8.4.2:
- resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==}
-
- react-dom@19.2.7:
- resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==}
- peerDependencies:
- react: ^19.2.7
-
- react@19.2.7:
- resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}
- engines: {node: '>=0.10.0'}
-
- rolldown@1.1.5:
- resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==}
- engines: {node: ^20.19.0 || >=22.12.0}
- hasBin: true
-
- scheduler@0.27.0:
- resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
-
- seroval-plugins@1.5.6:
- resolution: {integrity: sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ==}
- engines: {node: '>=10'}
- peerDependencies:
- seroval: ^1.0
-
- seroval@1.5.6:
- resolution: {integrity: sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA==}
- engines: {node: '>=10'}
-
- source-map-js@1.2.1:
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
- engines: {node: '>=0.10.0'}
-
- tinyglobby@0.2.17:
- resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
- engines: {node: '>=12.0.0'}
-
- tslib@2.8.1:
- resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
-
- typescript@6.0.3:
- resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
- engines: {node: '>=14.17'}
- hasBin: true
-
- use-sync-external-store@1.6.0:
- resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
- peerDependencies:
- react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
-
- vite@8.1.5:
- resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==}
- engines: {node: ^20.19.0 || >=22.12.0}
- hasBin: true
- peerDependencies:
- '@types/node': ^20.19.0 || >=22.12.0
- '@vitejs/devtools': ^0.3.0
- esbuild: ^0.27.0 || ^0.28.0
- jiti: '>=1.21.0'
- less: ^4.0.0
- sass: ^1.70.0
- sass-embedded: ^1.70.0
- stylus: '>=0.54.8'
- sugarss: ^5.0.0
- terser: ^5.16.0
- tsx: ^4.8.1
- yaml: ^2.4.2
- peerDependenciesMeta:
- '@types/node':
- optional: true
- '@vitejs/devtools':
- optional: true
- esbuild:
- optional: true
- jiti:
- optional: true
- less:
- optional: true
- sass:
- optional: true
- sass-embedded:
- optional: true
- stylus:
- optional: true
- sugarss:
- optional: true
- terser:
- optional: true
- tsx:
- optional: true
- yaml:
- optional: true
-
- yaml@2.9.0:
- resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
- engines: {node: '>= 14.6'}
- hasBin: true
-
-snapshots:
-
- '@effect/atom-react@4.0.0-rc.111(effect@4.0.0-rc.111)(react@19.2.7)(scheduler@0.27.0)':
- dependencies:
- effect: 4.0.0-rc.111
- react: 19.2.7
- scheduler: 0.27.0
-
- '@emnapi/core@1.11.1':
- dependencies:
- '@emnapi/wasi-threads': 1.2.2
- tslib: 2.8.1
- optional: true
-
- '@emnapi/runtime@1.11.1':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@emnapi/wasi-threads@1.2.2':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@esbuild/aix-ppc64@0.28.1':
- optional: true
-
- '@esbuild/android-arm64@0.28.1':
- optional: true
-
- '@esbuild/android-arm@0.28.1':
- optional: true
-
- '@esbuild/android-x64@0.28.1':
- optional: true
-
- '@esbuild/darwin-arm64@0.28.1':
- optional: true
-
- '@esbuild/darwin-x64@0.28.1':
- optional: true
-
- '@esbuild/freebsd-arm64@0.28.1':
- optional: true
-
- '@esbuild/freebsd-x64@0.28.1':
- optional: true
-
- '@esbuild/linux-arm64@0.28.1':
- optional: true
-
- '@esbuild/linux-arm@0.28.1':
- optional: true
-
- '@esbuild/linux-ia32@0.28.1':
- optional: true
-
- '@esbuild/linux-loong64@0.28.1':
- optional: true
-
- '@esbuild/linux-mips64el@0.28.1':
- optional: true
-
- '@esbuild/linux-ppc64@0.28.1':
- optional: true
-
- '@esbuild/linux-riscv64@0.28.1':
- optional: true
-
- '@esbuild/linux-s390x@0.28.1':
- optional: true
-
- '@esbuild/linux-x64@0.28.1':
- optional: true
-
- '@esbuild/netbsd-arm64@0.28.1':
- optional: true
-
- '@esbuild/netbsd-x64@0.28.1':
- optional: true
-
- '@esbuild/openbsd-arm64@0.28.1':
- optional: true
-
- '@esbuild/openbsd-x64@0.28.1':
- optional: true
-
- '@esbuild/openharmony-arm64@0.28.1':
- optional: true
-
- '@esbuild/sunos-x64@0.28.1':
- optional: true
-
- '@esbuild/win32-arm64@0.28.1':
- optional: true
-
- '@esbuild/win32-ia32@0.28.1':
- optional: true
-
- '@esbuild/win32-x64@0.28.1':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4':
- optional: true
-
- '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4':
- optional: true
-
- '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
- dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
- '@tybys/wasm-util': 0.10.3
- optional: true
-
- '@oxc-project/types@0.139.0': {}
-
- '@rolldown/binding-android-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-darwin-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-darwin-x64@1.1.5':
- optional: true
-
- '@rolldown/binding-freebsd-x64@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-arm64-musl@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-ppc64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-s390x-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-x64-gnu@1.1.5':
- optional: true
-
- '@rolldown/binding-linux-x64-musl@1.1.5':
- optional: true
-
- '@rolldown/binding-openharmony-arm64@1.1.5':
- optional: true
-
- '@rolldown/binding-wasm32-wasi@1.1.5':
- dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
- '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
- optional: true
-
- '@rolldown/binding-win32-arm64-msvc@1.1.5':
- optional: true
-
- '@rolldown/binding-win32-x64-msvc@1.1.5':
- optional: true
-
- '@rolldown/pluginutils@1.0.1': {}
-
- '@standard-schema/spec@1.1.0': {}
-
- '@tanstack/history@1.162.0': {}
-
- '@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
- dependencies:
- '@tanstack/history': 1.162.0
- '@tanstack/react-store': 0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
- '@tanstack/router-core': 1.171.15
- isbot: 5.2.1
- react: 19.2.7
- react-dom: 19.2.7(react@19.2.7)
-
- '@tanstack/react-store@0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
- dependencies:
- '@tanstack/store': 0.9.3
- react: 19.2.7
- react-dom: 19.2.7(react@19.2.7)
- use-sync-external-store: 1.6.0(react@19.2.7)
-
- '@tanstack/router-core@1.171.15':
- dependencies:
- '@tanstack/history': 1.162.0
- cookie-es: 3.1.1
- seroval: 1.5.6
- seroval-plugins: 1.5.6(seroval@1.5.6)
-
- '@tanstack/store@0.9.3': {}
-
- '@tybys/wasm-util@0.10.3':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@typeonce/effect-machine@file:../..(effect@4.0.0-rc.111)':
- dependencies:
- effect: 4.0.0-rc.111
-
- '@types/react-dom@19.2.2(@types/react@19.2.17)':
- dependencies:
- '@types/react': 19.2.17
-
- '@types/react@19.2.17':
- dependencies:
- csstype: 3.2.3
-
- '@vitejs/plugin-react@6.0.4(vite@8.1.5(esbuild@0.28.1)(yaml@2.9.0))':
- dependencies:
- '@rolldown/pluginutils': 1.0.1
- vite: 8.1.5(esbuild@0.28.1)(yaml@2.9.0)
-
- cookie-es@3.1.1: {}
-
- csstype@3.2.3: {}
-
- detect-libc@2.1.2: {}
-
- effect@4.0.0-rc.111:
- dependencies:
- '@standard-schema/spec': 1.1.0
- fast-check: 4.9.0
- msgpackr: 2.0.5
-
- esbuild@0.28.1:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.28.1
- '@esbuild/android-arm': 0.28.1
- '@esbuild/android-arm64': 0.28.1
- '@esbuild/android-x64': 0.28.1
- '@esbuild/darwin-arm64': 0.28.1
- '@esbuild/darwin-x64': 0.28.1
- '@esbuild/freebsd-arm64': 0.28.1
- '@esbuild/freebsd-x64': 0.28.1
- '@esbuild/linux-arm': 0.28.1
- '@esbuild/linux-arm64': 0.28.1
- '@esbuild/linux-ia32': 0.28.1
- '@esbuild/linux-loong64': 0.28.1
- '@esbuild/linux-mips64el': 0.28.1
- '@esbuild/linux-ppc64': 0.28.1
- '@esbuild/linux-riscv64': 0.28.1
- '@esbuild/linux-s390x': 0.28.1
- '@esbuild/linux-x64': 0.28.1
- '@esbuild/netbsd-arm64': 0.28.1
- '@esbuild/netbsd-x64': 0.28.1
- '@esbuild/openbsd-arm64': 0.28.1
- '@esbuild/openbsd-x64': 0.28.1
- '@esbuild/openharmony-arm64': 0.28.1
- '@esbuild/sunos-x64': 0.28.1
- '@esbuild/win32-arm64': 0.28.1
- '@esbuild/win32-ia32': 0.28.1
- '@esbuild/win32-x64': 0.28.1
- optional: true
-
- fast-check@4.9.0:
- dependencies:
- pure-rand: 8.4.2
-
- fdir@6.5.0(picomatch@4.0.5):
- optionalDependencies:
- picomatch: 4.0.5
-
- fsevents@2.3.3:
- optional: true
-
- isbot@5.2.1: {}
-
- lightningcss-android-arm64@1.33.0:
- optional: true
-
- lightningcss-darwin-arm64@1.33.0:
- optional: true
-
- lightningcss-darwin-x64@1.33.0:
- optional: true
-
- lightningcss-freebsd-x64@1.33.0:
- optional: true
-
- lightningcss-linux-arm-gnueabihf@1.33.0:
- optional: true
-
- lightningcss-linux-arm64-gnu@1.33.0:
- optional: true
-
- lightningcss-linux-arm64-musl@1.33.0:
- optional: true
-
- lightningcss-linux-x64-gnu@1.33.0:
- optional: true
-
- lightningcss-linux-x64-musl@1.33.0:
- optional: true
-
- lightningcss-win32-arm64-msvc@1.33.0:
- optional: true
-
- lightningcss-win32-x64-msvc@1.33.0:
- optional: true
-
- lightningcss@1.33.0:
- dependencies:
- detect-libc: 2.1.2
- optionalDependencies:
- lightningcss-android-arm64: 1.33.0
- lightningcss-darwin-arm64: 1.33.0
- lightningcss-darwin-x64: 1.33.0
- lightningcss-freebsd-x64: 1.33.0
- lightningcss-linux-arm-gnueabihf: 1.33.0
- lightningcss-linux-arm64-gnu: 1.33.0
- lightningcss-linux-arm64-musl: 1.33.0
- lightningcss-linux-x64-gnu: 1.33.0
- lightningcss-linux-x64-musl: 1.33.0
- lightningcss-win32-arm64-msvc: 1.33.0
- lightningcss-win32-x64-msvc: 1.33.0
-
- msgpackr-extract@3.0.4:
- dependencies:
- node-gyp-build-optional-packages: 5.2.2
- optionalDependencies:
- '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4
- '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4
- optional: true
-
- msgpackr@2.0.5:
- optionalDependencies:
- msgpackr-extract: 3.0.4
-
- nanoid@3.3.16: {}
-
- node-gyp-build-optional-packages@5.2.2:
- dependencies:
- detect-libc: 2.1.2
- optional: true
-
- picocolors@1.1.1: {}
-
- picomatch@4.0.5: {}
-
- postcss@8.5.23:
- dependencies:
- nanoid: 3.3.16
- picocolors: 1.1.1
- source-map-js: 1.2.1
-
- pure-rand@8.4.2: {}
-
- react-dom@19.2.7(react@19.2.7):
- dependencies:
- react: 19.2.7
- scheduler: 0.27.0
-
- react@19.2.7: {}
-
- rolldown@1.1.5:
- dependencies:
- '@oxc-project/types': 0.139.0
- '@rolldown/pluginutils': 1.0.1
- optionalDependencies:
- '@rolldown/binding-android-arm64': 1.1.5
- '@rolldown/binding-darwin-arm64': 1.1.5
- '@rolldown/binding-darwin-x64': 1.1.5
- '@rolldown/binding-freebsd-x64': 1.1.5
- '@rolldown/binding-linux-arm-gnueabihf': 1.1.5
- '@rolldown/binding-linux-arm64-gnu': 1.1.5
- '@rolldown/binding-linux-arm64-musl': 1.1.5
- '@rolldown/binding-linux-ppc64-gnu': 1.1.5
- '@rolldown/binding-linux-s390x-gnu': 1.1.5
- '@rolldown/binding-linux-x64-gnu': 1.1.5
- '@rolldown/binding-linux-x64-musl': 1.1.5
- '@rolldown/binding-openharmony-arm64': 1.1.5
- '@rolldown/binding-wasm32-wasi': 1.1.5
- '@rolldown/binding-win32-arm64-msvc': 1.1.5
- '@rolldown/binding-win32-x64-msvc': 1.1.5
-
- scheduler@0.27.0: {}
-
- seroval-plugins@1.5.6(seroval@1.5.6):
- dependencies:
- seroval: 1.5.6
-
- seroval@1.5.6: {}
-
- source-map-js@1.2.1: {}
-
- tinyglobby@0.2.17:
- dependencies:
- fdir: 6.5.0(picomatch@4.0.5)
- picomatch: 4.0.5
-
- tslib@2.8.1:
- optional: true
-
- typescript@6.0.3: {}
-
- use-sync-external-store@1.6.0(react@19.2.7):
- dependencies:
- react: 19.2.7
-
- vite@8.1.5(esbuild@0.28.1)(yaml@2.9.0):
- dependencies:
- lightningcss: 1.33.0
- picomatch: 4.0.5
- postcss: 8.5.23
- rolldown: 1.1.5
- tinyglobby: 0.2.17
- optionalDependencies:
- esbuild: 0.28.1
- fsevents: 2.3.3
- yaml: 2.9.0
-
- yaml@2.9.0:
- optional: true
diff --git a/examples/pokemon/pnpm-workspace.yaml b/examples/pokemon/pnpm-workspace.yaml
deleted file mode 100644
index 9634759..0000000
--- a/examples/pokemon/pnpm-workspace.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-packages:
- - "."
-
-overrides:
- effect: 4.0.0-rc.111
- "@effect/atom-react": 4.0.0-rc.111
-
-minimumReleaseAge: 1440
-minimumReleaseAgeExclude:
- - effect
- - "@effect/atom-react"
- - vite@8.1.5
- - "@vitejs/plugin-react@6.0.4"
diff --git a/examples/pokemon/src/atoms.ts b/examples/pokemon/src/atoms.ts
deleted file mode 100644
index 75cbdaf..0000000
--- a/examples/pokemon/src/atoms.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { AtomMachine } from "@typeonce/effect-machine/reactivity"
-import { Atom } from "effect/unstable/reactivity"
-import { machine, ReplaceChild, SelectionChild } from "./machine.ts"
-import { PokemonService } from "./pokemon.ts"
-
-const atomRuntime = Atom.runtime(PokemonService.layer)
-
-export const machineAtom = AtomMachine.bind(atomRuntime).make(machine)
-export const selectionMachineAtom = machineAtom.child(SelectionChild)
-export const replaceMachineAtom = machineAtom.child(ReplaceChild)
diff --git a/examples/pokemon/src/machine.ts b/examples/pokemon/src/machine.ts
deleted file mode 100644
index 9d001de..0000000
--- a/examples/pokemon/src/machine.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Effect, Schema } from "effect"
-import { ReplaceMachine } from "./machines/replace.ts"
-import { SelectionMachine } from "./machines/selection.ts"
-import { Pokemon, PokemonService, TeamEvents } from "./pokemon.ts"
-
-class ActiveTeam extends Schema.TaggedClass("ActiveTeam")("ActiveTeam", {
- team: Schema.Array(Pokemon)
-}) {}
-
-export const States = Machine.states({
- Loading: {},
- ActiveTeam,
- Failed: {}
-})
-
-export const SelectionChild = Machine.child("selection", SelectionMachine)
-export const ReplaceChild = Machine.child("replace", ReplaceMachine)
-
-export const machine = Machine.make({
- states: States.states,
- events: TeamEvents,
- initial: (to) => to.Loading()
-}).handle({
- Loading: {
- invoke: (from) =>
- from.effect("load-team", () =>
- Effect.gen(function*() {
- const service = yield* PokemonService
- return yield* service.getRandomTeam()
- })).onDone((to) => to.full.ActiveTeam().resolve(({ output, target }) => target.from({ team: output })))
- .onFailure((to) => to.full.Failed())
- },
- ActiveTeam: {
- invoke: (
- from
- ) => [
- from.child(SelectionChild).onFailure((to) => to.full.Failed()),
- from.child(ReplaceChild).onFailure((to) => to.full.Failed())
- ],
- on: {
- ReplaceInTeam: (to) =>
- to.full.ActiveTeam().resolve(({ event, target, state }) =>
- target.from({
- team: state.team.map((pokemon) => (pokemon.id === event.id ? event.pokemon : pokemon))
- })
- )
- }
- },
- Failed: {}
-})
diff --git a/examples/pokemon/src/machines/replace.ts b/examples/pokemon/src/machines/replace.ts
deleted file mode 100644
index 4ed2c93..0000000
--- a/examples/pokemon/src/machines/replace.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Effect, Schema } from "effect"
-import { Pokemon, PokemonService, TeamEvents } from "../pokemon.ts"
-
-class Replacing extends Schema.TaggedClass("Replacing")("Replacing", {
- id: Pokemon.fields.id
-}) {}
-
-export const ReplaceStates = Machine.states({
- Idle: {},
- Replacing
-})
-
-export const ReplaceEvents = Machine.events(
- Schema.TaggedUnion({ ReplacePokemon: { id: Pokemon.fields.id } })
-)
-const ReplaceInternalEvents = Machine.internalEvents(
- Schema.TaggedUnion({ Replaced: { pokemon: Pokemon } })
-)
-export const ReplaceMachine = Machine.make({
- states: ReplaceStates.states,
- events: ReplaceEvents,
- internalEvents: ReplaceInternalEvents,
- parent: Machine.parent(TeamEvents),
- initial: (to) => to.Idle()
-}).handle({
- Idle: {
- on: {
- ReplacePokemon: (to) => to.full.Replacing().resolve(({ event, target }) => target.from({ id: event.id }))
- }
- },
- Replacing: {
- invoke: (from) =>
- from.effect("replaceWithRandom", () =>
- Effect.sleep("500 millis").pipe(
- Effect.andThen(
- Effect.gen(function*() {
- const service = yield* PokemonService
- const pokemon = yield* service.getRandomPokemon()
- return ReplaceInternalEvents.Replaced({ pokemon })
- })
- ),
- Effect.onInterrupt(() => Effect.log("Replace with random interrupted"))
- )).onDone((to) =>
- to.none.resolve(({ output }, enqueue) => {
- enqueue.raise(output)
- })
- ).onFailure((to) => to.full.Idle()),
- on: {
- Replaced: (to) =>
- to.full.Idle().resolve(({ event, parent, state, target }, enqueue) => {
- enqueue.sendTo(parent, TeamEvents.ReplaceInTeam({ id: state.id, pokemon: event.pokemon }))
- return target.from()
- })
- }
- }
-})
diff --git a/examples/pokemon/src/machines/selection.ts b/examples/pokemon/src/machines/selection.ts
deleted file mode 100644
index 067b860..0000000
--- a/examples/pokemon/src/machines/selection.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Effect, Option, Schema } from "effect"
-import { Pokemon, PokemonService, TeamEvents } from "../pokemon.ts"
-
-const State = Schema.TaggedUnion({
- Selected: {
- id: Pokemon.fields.id,
- searchText: Schema.String
- },
- WithPokemon: { pokemon: Pokemon }
-})
-
-export const SelectionStates = Machine.states({
- form: {
- initial: "Unselected",
- states: {
- Unselected: {},
- Selected: {
- schema: State.cases.Selected,
- initial: "NoPokemon",
- states: {
- NoPokemon: {},
- WithPokemon: State.cases.WithPokemon,
- Searching: {}
- }
- }
- }
- }
-})
-
-export const SelectionEvents = Machine.events(
- Schema.TaggedUnion({
- SelectPokemon: { id: Pokemon.fields.id },
- UpdateSearchText: { value: Schema.String },
- ReplacePokemon: {}
- })
-)
-
-const SelectionInternalEvents = Machine.internalEvents(
- Schema.TaggedUnion({ SearchResult: { result: Schema.Option(Pokemon) } })
-)
-
-export const SelectionMachine = Machine.make({
- states: SelectionStates.states,
- events: SelectionEvents,
- internalEvents: SelectionInternalEvents,
- parent: Machine.parent(TeamEvents),
- initial: (to) => to.form.initial.resolve(({ target }) => target.from((form) => form.Unselected.from()))
-}).handle({
- form: {
- states: {
- Unselected: {
- on: {
- SelectPokemon: (to) =>
- to.local.Selected.initial.resolve(({ event, target }) => target.from({ id: event.id, searchText: "" }))
- }
- },
- Selected: {
- on: {
- SelectPokemon: (to) =>
- to.branches({
- unselected: { title: "Unselect", target: to.branch.form.Unselected() },
- selected: { title: "Select another Pokémon", target: to.branch.form.Selected.initial }
- }).resolve(({ event, select, state }) =>
- state.id === event.id
- ? select.unselected.from()
- : select.selected.from({ id: event.id, searchText: "" })
- ),
- UpdateSearchText: (to) =>
- to.local.with.resolve(
- ({ event, state, target }) =>
- target.from(
- { id: state.id, searchText: event.value },
- (selected) => selected.Searching.from()
- ),
- { reenter: true }
- )
- },
- states: {
- WithPokemon: {
- on: {
- ReplacePokemon: (to) =>
- to.branch.form.Unselected().resolve(({ ancestors, parent, state, target }, enqueue) => {
- enqueue.sendTo(
- parent,
- TeamEvents.ReplaceInTeam({
- id: ancestors["form.Selected"].id,
- pokemon: state.pokemon
- })
- )
- return target.from()
- })
- }
- },
- Searching: {
- invoke: (from) =>
- from.effect("search", ({ ancestors }) =>
- Effect.sleep("500 millis").pipe(
- Effect.andThen(
- Effect.gen(function*() {
- const service = yield* PokemonService
- const pokemon = yield* service.getByName(ancestors["form.Selected"].searchText)
- return SelectionInternalEvents.SearchResult({ result: pokemon })
- })
- ),
- Effect.onInterrupt(() => Effect.log("Search interrupted"))
- )).onDone((to) =>
- to.none.resolve(({ output }, enqueue) => {
- enqueue.raise(output)
- })
- ).onFailure((to) => to.local.NoPokemon()),
- on: {
- SearchResult: (to) =>
- to.branches({
- found: { target: to.local.WithPokemon() },
- notFound: { title: "Not found", target: to.local.NoPokemon() }
- }).resolve(({ event, select }) =>
- Option.match(event.result, {
- onNone: () => select.notFound.from(),
- onSome: (pokemon) => select.found.from({ pokemon })
- })
- )
- }
- }
- }
- }
- }
- }
-})
diff --git a/examples/pokemon/src/main.tsx b/examples/pokemon/src/main.tsx
deleted file mode 100644
index 82ba67d..0000000
--- a/examples/pokemon/src/main.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { RegistryProvider } from "@effect/atom-react"
-import { RouterProvider } from "@tanstack/react-router"
-import { StrictMode } from "react"
-import { createRoot } from "react-dom/client"
-import { router } from "./router.js"
-import "./styles.css"
-
-const root = document.getElementById("root")
-
-if (root === null) {
- throw new Error("Root element not found")
-}
-
-createRoot(root).render(
-
-
-
-
-
-)
diff --git a/examples/pokemon/src/pokemon.ts b/examples/pokemon/src/pokemon.ts
deleted file mode 100644
index ca25c46..0000000
--- a/examples/pokemon/src/pokemon.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { Machine } from "@typeonce/effect-machine"
-import { Array, Context, Effect, flow, Layer, Option, Random, Schema } from "effect"
-import {
- FetchHttpClient,
- HttpClient,
- HttpClientError,
- HttpClientRequest,
- HttpClientResponse
-} from "effect/unstable/http"
-
-export const Pokemon = Schema.Struct({
- id: Schema.Number,
- name: Schema.NonEmptyString,
- order: Schema.Number,
- sprites: Schema.Struct({
- front_default: Schema.NonEmptyString,
- back_default: Schema.NonEmptyString
- })
-})
-
-export const TeamEvents = Machine.events(
- Schema.TaggedUnion({
- ReplaceInTeam: {
- id: Pokemon.fields.id,
- pokemon: Pokemon
- }
- })
-)
-
-export class PokemonService extends Context.Service()("app/PokemonService", {
- make: Effect.gen(function*() {
- const baseClient = yield* HttpClient.HttpClient
- const client = baseClient.pipe(
- HttpClient.mapRequest(
- flow(HttpClientRequest.prependUrl("https://pokeapi.co/api/v2"), HttpClientRequest.acceptJson)
- )
- )
-
- return {
- getRandomTeam: Effect.fn("PokemonService.getTeam")(function*() {
- const teamIndexes = yield* Random.shuffle(Array.range(1, 1025)).pipe(Effect.map(Array.take(6)))
-
- return yield* Effect.all(
- teamIndexes.map((index) =>
- client.get(`/pokemon/${index}`).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(Pokemon)))
- ),
- { concurrency: 3 }
- )
- }),
-
- getRandomPokemon: Effect.fn("PokemonService.getRandomPokemon")(function*() {
- const index = yield* Random.nextIntBetween(1, 1025)
- const response = yield* client.get(`/pokemon/${index}`)
- return yield* HttpClientResponse.schemaBodyJson(Pokemon)(response)
- }),
-
- getByName: Effect.fn("PokemonService.getByName")(function*(name: string) {
- const response = yield* client.get(`/pokemon/${encodeURIComponent(name.trim().toLowerCase())}`)
- return yield* HttpClientResponse.matchStatus(response, {
- 404: () => Effect.succeed(Option.none()),
- "2xx": (response) => HttpClientResponse.schemaBodyJson(Pokemon)(response).pipe(Effect.map(Option.some)),
- orElse: (response) =>
- Effect.fail(
- new HttpClientError.HttpClientError({
- reason: new HttpClientError.StatusCodeError({
- response,
- request: response.request,
- description: "non 2xx status code"
- })
- })
- )
- })
- })
- }
- })
-}) {
- static readonly layer = Layer.effect(this, this.make).pipe(Layer.provide(FetchHttpClient.layer))
-}
diff --git a/examples/pokemon/src/router.tsx b/examples/pokemon/src/router.tsx
deleted file mode 100644
index f07111c..0000000
--- a/examples/pokemon/src/router.tsx
+++ /dev/null
@@ -1,236 +0,0 @@
-import { useAtomSet, useAtomValue } from "@effect/atom-react"
-import { createRootRoute, createRoute, createRouter, Link, Outlet } from "@tanstack/react-router"
-import { Match, Option } from "effect"
-import { AsyncResult } from "effect/unstable/reactivity"
-import { machineAtom, replaceMachineAtom, selectionMachineAtom } from "./atoms.js"
-import { States } from "./machine.js"
-import { ReplaceEvents, ReplaceStates } from "./machines/replace.ts"
-import { SelectionEvents, SelectionStates } from "./machines/selection.ts"
-import type { Pokemon } from "./pokemon.ts"
-
-const rootRoute = createRootRoute({
- component: () => (
-
-
- Home
- Machine
-
-
-
- )
-})
-
-const homeRoute = createRoute({
- getParentRoute: () => rootRoute,
- path: "/",
- component: () => (
-
- Effect Machine Playground
-
- Edit src/machine.ts to build the machine.
-
-
- )
-})
-
-const machineRoute = createRoute({
- getParentRoute: () => rootRoute,
- path: "/machine",
- component: MachinePage
-})
-
-function Selection() {
- const stateResult = useAtomValue(selectionMachineAtom.state)
- const send = useAtomSet(selectionMachineAtom.send)
-
- if (AsyncResult.isInitial(stateResult)) {
- return Starting selection machine…
- }
-
- if (AsyncResult.isFailure(stateResult)) {
- return Selection machine failed.
- }
-
- const state = stateResult.value
-
- if (Option.isNone(state)) {
- return null
- }
-
- return (
-
- Selection state: {state.value.path}
-
- {SelectionStates.get(state.value, "form.Selected").pipe(
- Option.match({
- onNone: () => No Pokémon selected.
,
- onSome: ({ id, searchText }) => (
- <>
- Selected Pokémon id: {id}
-
- send(SelectionEvents.UpdateSearchText({ value: event.target.value }))}
- />
-
- {SelectionStates.matches(state.value, "form.Selected.Searching") && Searching…
}
-
- {SelectionStates.get(state.value, "form.Selected.WithPokemon").pipe(
- Option.match({
- onNone: () => null,
- onSome: ({ pokemon }) => (
-
- {pokemon.name}
-
- send(SelectionEvents.ReplacePokemon())}>
- Replace
-
-
- )
- })
- )}
- >
- )
- })
- )}
-
- )
-}
-
-function Replace() {
- const stateResult = useAtomValue(replaceMachineAtom.state)
-
- if (AsyncResult.isInitial(stateResult)) {
- return Starting replacement machine…
- }
-
- if (AsyncResult.isFailure(stateResult)) {
- return Replacement machine failed.
- }
-
- const state = stateResult.value
-
- if (Option.isNone(state)) {
- return null
- }
-
- return ReplaceStates.get(state.value, "Replacing").pipe(
- Option.match({
- onNone: () => null,
- onSome: ({ id }) => Replacing Pokémon {id}…
- })
- )
-}
-
-function PokemonGrid({ team }: { team: readonly (typeof Pokemon.Type)[] }) {
- const selected = useAtomValue(selectionMachineAtom.state)
- const replacing = useAtomValue(replaceMachineAtom.state)
-
- const sendSelection = useAtomSet(selectionMachineAtom.send)
- const sendReplace = useAtomSet(replaceMachineAtom.send)
-
- const selectedId = Match.value(selected).pipe(
- Match.tagsExhaustive({
- Initial: () => null,
- Failure: () => null,
- Success: (current) =>
- current.value.pipe(
- Option.flatMap((current) => SelectionStates.get(current, "form.Selected")),
- Option.map((current) => current.id),
- Option.getOrNull
- )
- })
- )
-
- const replacingId = Match.value(replacing).pipe(
- Match.tagsExhaustive({
- Initial: () => null,
- Failure: () => null,
- Success: (current) =>
- current.value.pipe(
- Option.flatMap((current) => ReplaceStates.get(current, "Replacing")),
- Option.map((current) => current.id),
- Option.getOrNull
- )
- })
- )
-
- return (
-
- {team.map((pokemon) => {
- const isSelected = pokemon.id === selectedId
- const isReplacing = pokemon.id === replacingId
-
- return (
-
-
{pokemon.name}
-
-
- sendSelection(
- SelectionEvents.SelectPokemon({
- id: pokemon.id
- })
- )}
- >
- Select
-
-
sendReplace(ReplaceEvents.ReplacePokemon({ id: pokemon.id }))}
- >
- Replace with random
-
-
- )
- })}
-
- )
-}
-
-function MachinePage() {
- const snapshot = useAtomValue(machineAtom.snapshot)
-
- if (AsyncResult.isInitial(snapshot)) {
- return Starting machine…
- }
-
- if (AsyncResult.isFailure(snapshot)) {
- return Machine failed to start.
- }
-
- const state = snapshot.value.state
-
- return (
-
- Machine
- Status: {snapshot.value.status}
- State: {state.path}
-
-
-
-
- {States.get(state, "ActiveTeam").pipe(
- Option.match({
- onNone: () => No team selected
,
- onSome: ({ team }) =>
- })
- )}
-
- )
-}
-
-const routeTree = rootRoute.addChildren([homeRoute, machineRoute])
-
-export const router = createRouter({ routeTree })
-
-declare module "@tanstack/react-router" {
- interface Register {
- router: typeof router
- }
-}
diff --git a/examples/pokemon/src/styles.css b/examples/pokemon/src/styles.css
deleted file mode 100644
index 8e98297..0000000
--- a/examples/pokemon/src/styles.css
+++ /dev/null
@@ -1,253 +0,0 @@
-:root {
- color: #20202a;
- background: #f5f4fa;
- font-family:
- Inter,
- ui-sans-serif,
- system-ui,
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- sans-serif;
- font-synthesis: none;
-}
-
-body {
- margin: 0;
- min-width: 20rem;
- min-height: 100vh;
- background: radial-gradient(circle at top left, rgb(124 92 255 / 10%), transparent 28rem), #f5f4fa;
-}
-
-main {
- width: min(68rem, calc(100% - 2rem));
- margin: 0 auto;
- padding: 1.5rem 0 4rem;
-}
-
-nav {
- display: flex;
- gap: 0.35rem;
- width: fit-content;
- margin-bottom: 2.5rem;
- padding: 0.3rem;
- border: 1px solid #e4e1ef;
- border-radius: 0.75rem;
- background: rgb(255 255 255 / 75%);
- box-shadow: 0 0.5rem 1.5rem rgb(42 32 74 / 6%);
-}
-
-nav a {
- padding: 0.55rem 0.85rem;
- border-radius: 0.5rem;
- color: #5f5b6b;
- font-size: 0.9rem;
- font-weight: 650;
- text-decoration: none;
- transition:
- color 150ms ease,
- background 150ms ease;
-}
-
-nav a:hover {
- color: #4d35c5;
- background: #f0edff;
-}
-
-h1,
-h2,
-h3,
-p {
- margin-top: 0;
-}
-
-h1 {
- margin-bottom: 1.5rem;
- font-size: clamp(2rem, 6vw, 3.4rem);
- line-height: 1;
- letter-spacing: -0.055em;
-}
-
-.machine-page > h3 {
- display: inline-flex;
- margin: 0 0.5rem 1.5rem 0;
- padding: 0.4rem 0.7rem;
- border: 1px solid #dfdbea;
- border-radius: 999px;
- color: #625d70;
- background: #fff;
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
- font-size: 0.75rem;
- font-weight: 600;
-}
-
-.machine-panel {
- margin-bottom: 1rem;
- padding: 1rem 1.15rem;
- border: 1px solid #e3dfec;
- border-radius: 1rem;
- background: rgb(255 255 255 / 82%);
- box-shadow: 0 0.75rem 2rem rgb(42 32 74 / 6%);
-}
-
-.machine-panel p:last-child {
- margin-bottom: 0;
-}
-
-input {
- box-sizing: border-box;
- width: min(100%, 24rem);
- margin-bottom: 1rem;
- padding: 0.7rem 0.85rem;
- border: 1px solid #d9d4e5;
- border-radius: 0.65rem;
- outline: none;
- color: inherit;
- background: #fff;
- font: inherit;
- transition:
- border-color 150ms ease,
- box-shadow 150ms ease;
-}
-
-input:focus {
- border-color: #7961e8;
- box-shadow: 0 0 0 0.2rem rgb(121 97 232 / 14%);
-}
-
-button {
- padding: 0.55rem 0.75rem;
- border: 1px solid #d7d2e3;
- border-radius: 0.55rem;
- color: #373242;
- background: #fff;
- font: inherit;
- font-size: 0.82rem;
- font-weight: 650;
- cursor: pointer;
- transition:
- border-color 150ms ease,
- color 150ms ease,
- background 150ms ease,
- transform 150ms ease;
-}
-
-button:hover:not(:disabled) {
- border-color: #7961e8;
- color: #4d35c5;
- background: #f5f2ff;
- transform: translateY(-1px);
-}
-
-button:disabled {
- cursor: wait;
- opacity: 0.55;
-}
-
-.pokemon-grid {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 1rem;
- margin-top: 1.5rem;
-}
-
-.pokemon-card {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 0.6rem;
- align-items: center;
- padding: 1rem;
- border: 1px solid #e2deea;
- border-radius: 1rem;
- background: rgb(255 255 255 / 90%);
- box-shadow: 0 0.75rem 1.75rem rgb(42 32 74 / 6%);
- transition:
- border-color 180ms ease,
- box-shadow 180ms ease,
- opacity 180ms ease,
- transform 180ms ease;
-}
-
-.pokemon-card:hover {
- transform: translateY(-2px);
- box-shadow: 0 1rem 2rem rgb(42 32 74 / 10%);
-}
-
-.pokemon-card.is-selected {
- border-color: #7961e8;
- box-shadow:
- 0 0 0 0.2rem rgb(121 97 232 / 12%),
- 0 1rem 2rem rgb(42 32 74 / 10%);
-}
-
-.pokemon-card.is-replacing {
- opacity: 0.55;
-}
-
-.pokemon-card h3 {
- grid-column: 1 / -1;
- margin-bottom: 0;
- overflow: hidden;
- font-size: 1rem;
- text-overflow: ellipsis;
- text-transform: capitalize;
-}
-
-.pokemon-card img {
- grid-column: 1 / -1;
- width: min(100%, 8rem);
- margin: 0 auto;
- image-rendering: pixelated;
-}
-
-.machine-panel article {
- display: flex;
- gap: 1rem;
- align-items: center;
- width: fit-content;
- margin-top: 0.5rem;
- padding: 0.75rem 1rem;
- border: 1px solid #dfd9f0;
- border-radius: 0.85rem;
- background: #f8f6ff;
-}
-
-.machine-panel article h3 {
- margin: 0;
- text-transform: capitalize;
-}
-
-.machine-panel article img {
- width: 5rem;
-}
-
-pre {
- overflow: auto;
- padding: 1rem;
- border: 1px solid #ddd;
- border-radius: 0.5rem;
- background: white;
-}
-
-code {
- padding: 0.12rem 0.35rem;
- border-radius: 0.3rem;
- color: #5338c6;
- background: #ebe7ff;
-}
-
-@media (max-width: 48rem) {
- .pokemon-grid {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
-}
-
-@media (max-width: 32rem) {
- main {
- width: min(100% - 1rem, 68rem);
- }
-
- .pokemon-grid {
- grid-template-columns: 1fr;
- }
-}
diff --git a/examples/pokemon/tsconfig.json b/examples/pokemon/tsconfig.json
deleted file mode 100644
index d6fe209..0000000
--- a/examples/pokemon/tsconfig.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/tsconfig",
- "compilerOptions": {
- "target": "ES2022",
- "useDefineForClassFields": true,
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "moduleResolution": "Bundler",
- "allowImportingTsExtensions": true,
- "verbatimModuleSyntax": true,
- "strict": true,
- "skipLibCheck": true,
- "noEmit": true,
- "jsx": "react-jsx",
- "types": ["vite/client"]
- },
- "include": ["src", "vite.config.ts"]
-}
diff --git a/examples/pokemon/vite.config.ts b/examples/pokemon/vite.config.ts
deleted file mode 100644
index c989213..0000000
--- a/examples/pokemon/vite.config.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import react from "@vitejs/plugin-react"
-import { defineConfig } from "vite"
-
-export default defineConfig({
- plugins: [react()]
-})
diff --git a/scripts/ci-changes.mjs b/scripts/ci-changes.mjs
index fa1ef69..33d0b01 100644
--- a/scripts/ci-changes.mjs
+++ b/scripts/ci-changes.mjs
@@ -1,5 +1,5 @@
import { execFileSync } from "node:child_process"
-import { appendFileSync, existsSync, readdirSync, readFileSync } from "node:fs"
+import { appendFileSync } from "node:fs"
import { resolve } from "node:path"
import { fileURLToPath } from "node:url"
@@ -18,18 +18,8 @@ const relevantDependenciesChanged = (beforePackageJson, afterPackageJson) => {
return [...performanceDependencies].some((dependency) => before[dependency] !== after[dependency])
}
-const changedExampleNames = (changedFiles, availableExamples) => {
- const available = new Set(availableExamples)
- return [...new Set(
- changedFiles
- .map((path) => /^examples\/([^/]+)\//.exec(path)?.[1])
- .filter((name) => name !== undefined && available.has(name))
- )].sort()
-}
-
export const classifyChanges = ({
afterPackageJson,
- availableExamples,
beforePackageJson,
changedFiles
}) => {
@@ -58,21 +48,7 @@ export const classifyChanges = ({
path === "tsconfig.build.json" ||
path === ".github/workflows/runtime-performance.yml"
)
- const allExamples = sourceChanged ||
- classifierChanged ||
- changedFiles.some((path) =>
- path === "package.json" ||
- path === "pnpm-workspace.yaml" ||
- path === "tsconfig.build.json" ||
- path === "scripts/list-examples.mjs" ||
- path === ".github/workflows/ci.yml"
- )
- const examples = allExamples
- ? [...availableExamples].sort()
- : changedExampleNames(changedFiles, availableExamples)
-
return {
- examples,
runtimePerformance,
typePerformance
}
@@ -82,20 +58,6 @@ const git = (...args) => execFileSync("git", args, { encoding: "utf8" })
const readPackageJsonAt = (revision) => JSON.parse(git("show", `${revision}:package.json`))
-const readAvailableExamples = () => {
- const examplesRoot = resolve(import.meta.dirname, "..", "examples")
- return readdirSync(examplesRoot, { withFileTypes: true })
- .filter((entry) => entry.isDirectory())
- .filter((entry) => existsSync(resolve(examplesRoot, entry.name, "package.json")))
- .map((entry) => {
- const packageJson = JSON.parse(readFileSync(resolve(examplesRoot, entry.name, "package.json"), "utf8"))
- if (typeof packageJson.scripts?.check !== "string") {
- throw new Error(`examples/${entry.name}/package.json must define a check script`)
- }
- return entry.name
- })
-}
-
const main = () => {
const options = {
base: undefined,
@@ -127,18 +89,12 @@ const main = () => {
).trim().split("\n").filter(Boolean)
const result = classifyChanges({
afterPackageJson: readPackageJsonAt(options.head),
- availableExamples: readAvailableExamples(),
beforePackageJson: readPackageJsonAt(options.base),
changedFiles
})
const output = [
`type_performance=${result.typePerformance}`,
- `runtime_performance=${result.runtimePerformance}`,
- `examples_required=${result.examples.length > 0}`,
- `examples=${JSON.stringify(result.examples.map((example) => ({
- example,
- directory: `examples/${example}`
- })))}`
+ `runtime_performance=${result.runtimePerformance}`
].join("\n")
if (options.githubOutput === undefined) {
diff --git a/scripts/ci-changes.test.mjs b/scripts/ci-changes.test.mjs
index 62bac0e..9c3b8e3 100644
--- a/scripts/ci-changes.test.mjs
+++ b/scripts/ci-changes.test.mjs
@@ -14,30 +14,19 @@ const packageJson = {
const classify = (changedFiles, afterPackageJson = packageJson) =>
classifyChanges({
afterPackageJson,
- availableExamples: ["platformer", "playground", "pokemon"],
beforePackageJson: packageJson,
changedFiles
})
-test("skips performance and examples for documentation changes", () => {
+test("skips performance for documentation changes", () => {
assert.deepEqual(classify(["README.md", "docs/agent-guide.md"]), {
- examples: [],
runtimePerformance: false,
typePerformance: false
})
})
-test("selects only the changed example", () => {
- assert.deepEqual(classify(["examples/pokemon/src/machine.ts"]), {
- examples: ["pokemon"],
- runtimePerformance: false,
- typePerformance: false
- })
-})
-
-test("runs type performance and every example for public source changes", () => {
+test("runs type performance for public source changes", () => {
assert.deepEqual(classify(["src/index.ts"]), {
- examples: ["platformer", "playground", "pokemon"],
runtimePerformance: false,
typePerformance: true
})
@@ -45,7 +34,6 @@ test("runs type performance and every example for public source changes", () =>
test("adds runtime performance for machine implementation changes", () => {
assert.deepEqual(classify(["src/internal/machine/runtime.ts"]), {
- examples: ["platformer", "playground", "pokemon"],
runtimePerformance: true,
typePerformance: true
})
@@ -56,7 +44,6 @@ test("distinguishes performance dependencies from unrelated tooling", () => {
...packageJson,
devDependencies: { ...packageJson.devDependencies, dprint: "2.0.0" }
}), {
- examples: ["platformer", "playground", "pokemon"],
runtimePerformance: false,
typePerformance: false
})
@@ -64,7 +51,6 @@ test("distinguishes performance dependencies from unrelated tooling", () => {
...packageJson,
devDependencies: { ...packageJson.devDependencies, effect: "4.1.0" }
}), {
- examples: ["platformer", "playground", "pokemon"],
runtimePerformance: true,
typePerformance: true
})
@@ -72,12 +58,10 @@ test("distinguishes performance dependencies from unrelated tooling", () => {
test("classifies performance harness and classifier changes", () => {
assert.deepEqual(classify(["scripts/type-performance.mjs"]), {
- examples: [],
runtimePerformance: false,
typePerformance: true
})
assert.deepEqual(classify(["scripts/ci-changes.mjs"]), {
- examples: ["platformer", "playground", "pokemon"],
runtimePerformance: true,
typePerformance: true
})
diff --git a/scripts/list-examples.mjs b/scripts/list-examples.mjs
deleted file mode 100644
index 129725e..0000000
--- a/scripts/list-examples.mjs
+++ /dev/null
@@ -1,38 +0,0 @@
-import { readdirSync, readFileSync } from "node:fs"
-import { resolve } from "node:path"
-
-const root = resolve(import.meta.dirname, "..")
-const examplesRoot = resolve(root, "examples")
-const examples = []
-
-for (const entry of readdirSync(examplesRoot, { withFileTypes: true })) {
- if (!entry.isDirectory()) {
- continue
- }
-
- const directory = `examples/${entry.name}`
- const packageJsonPath = resolve(root, directory, "package.json")
- let packageJson
-
- try {
- packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"))
- } catch (error) {
- throw new Error(`${directory} must contain a valid package.json`, { cause: error })
- }
-
- if (typeof packageJson.scripts?.check !== "string") {
- throw new Error(`${directory}/package.json must define a check script`)
- }
-
- examples.push({
- example: entry.name,
- directory
- })
-}
-
-if (examples.length === 0) {
- throw new Error("At least one package is required in examples/")
-}
-
-examples.sort((left, right) => left.example.localeCompare(right.example))
-console.log(JSON.stringify(examples))