Skip to content

Commit ee4251e

Browse files
Merge pull request #13 from typeonce-dev/codex/examples-playground
Add examples playground and explicit example typechecks
2 parents 13af066 + 4451beb commit ee4251e

29 files changed

Lines changed: 2246 additions & 15 deletions

examples/platformer/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"type": "module",
77
"scripts": {
88
"dev": "vite",
9-
"build": "tsc --noEmit && vite build",
9+
"typecheck": "tsc --noEmit",
10+
"build": "pnpm typecheck && vite build",
1011
"preview": "vite preview",
1112
"check": "pnpm build"
1213
},

examples/platformer/src/machine.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ const initialCharacter = () =>
137137
export const CharacterMachine = Machine.make({
138138
id: "PlatformerCharacter",
139139
states: CharacterStates.states,
140-
events: Object.values(Event.cases),
141-
internalEvents: Object.values(InternalEvent.cases),
140+
events: [Event],
141+
internalEvents: [InternalEvent],
142142
initial: initialCharacter
143143
}).handle({
144144
Character: {
@@ -151,18 +151,29 @@ export const CharacterMachine = Machine.make({
151151
Grounded: {
152152
on: {
153153
JumpPressed: ({ event, target }) =>
154-
target.branch.Character.locomotion.Airborne(
155-
State.cases.Airborne.make({ originY: event.y }),
156-
(airborne) =>
157-
airborne
158-
.motion(State.cases.Motion.make({}), (motion) =>
159-
motion.Jumping(
160-
State.cases.Jumping.make({ startedAt: event.at, push: 0, kind: "Ground" })
161-
)
162-
)
163-
.airJump(State.cases.AirJump.make({}), (airJump) =>
164-
airJump.AirJumpGroundLock(State.cases.AirJumpGroundLock.make({}))
154+
target.full.Character(State.cases.Character.make({}), (character) =>
155+
character
156+
.locomotion(State.cases.Locomotion.make({}), (locomotion) =>
157+
locomotion.Airborne(State.cases.Airborne.make({ originY: event.y }), (airborne) =>
158+
airborne
159+
.motion(State.cases.Motion.make({}), (motion) =>
160+
motion.Jumping(
161+
State.cases.Jumping.make({ startedAt: event.at, push: 0, kind: "Ground" })
162+
)
163+
)
164+
.airJump(State.cases.AirJump.make({}), (airJump) =>
165+
airJump.AirJumpGroundLock(State.cases.AirJumpGroundLock.make({}))
166+
)
165167
)
168+
)
169+
.facing(State.cases.Facing.make({}), (facing) => facing.Right(State.cases.Right.make({})))
170+
.contact(State.cases.WallContact.make({}), (contact) =>
171+
event.wall === -1
172+
? contact.LeftWall(State.cases.LeftWall.make({}))
173+
: event.wall === 1
174+
? contact.RightWall(State.cases.RightWall.make({}))
175+
: contact.NoWall(State.cases.NoWall.make({}))
176+
)
166177
)
167178
},
168179
states: {

examples/playground/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist
2+
node_modules
3+

examples/playground/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Effect Machine examples playground
2+
3+
A React and Vite workspace with TanStack Router routes for implementing small
4+
`@typeonce/effect-machine` examples.
5+
6+
## Run it
7+
8+
From this directory:
9+
10+
```sh
11+
pnpm install
12+
pnpm dev
13+
```
14+
15+
Build and type-check with:
16+
17+
```sh
18+
pnpm check
19+
```
20+
21+
## Starters
22+
23+
Each starter keeps its machine definition next to its route component:
24+
25+
- `src/examples/turnstile`
26+
- `src/examples/traffic-light`
27+
- `src/examples/microwave`
28+
- `src/examples/media-player`
29+
- `src/examples/worker-tabs`
30+
31+
The first four route components are intentionally light: their domain schemas,
32+
events, and state topology are ready, while the React-to-machine adapter is left
33+
for the exercise.
34+
35+
The workers route additionally contains:
36+
37+
- `machine.worker.ts`: Vite's module-worker entry point and the place to start
38+
the machine runtime.
39+
- `worker-client.ts`: typed worker construction, send, subscribe, and cleanup.
40+
- `tab-channel.ts`: a typed `BroadcastChannel` wrapper for cross-tab messages.
41+
- `protocol.ts`: worker and tab message boundaries.
42+
43+
Vite discovers the worker because `worker-client.ts` constructs it with
44+
`new Worker(new URL("./machine.worker.ts", import.meta.url), { type: "module" })`.
45+
No extra Vite worker plugin is required.

examples/playground/index.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta name="theme-color" content="#10131a" />
7+
<title>Effect Machine examples</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>
14+

examples/playground/package.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "@typeonce/effect-machine-example-playground",
3+
"version": "0.0.0",
4+
"private": true,
5+
"description": "React playground for building @typeonce/effect-machine examples",
6+
"type": "module",
7+
"scripts": {
8+
"dev": "vite",
9+
"typecheck": "tsc --noEmit",
10+
"build": "pnpm typecheck && vite build",
11+
"preview": "vite preview",
12+
"check": "pnpm build"
13+
},
14+
"dependencies": {
15+
"@effect/atom-react": "4.0.0-beta.102",
16+
"@tanstack/react-router": "1.170.18",
17+
"@typeonce/effect-machine": "file:../..",
18+
"effect": "4.0.0-beta.102",
19+
"react": "19.2.7",
20+
"react-dom": "19.2.7",
21+
"scheduler": "0.27.0"
22+
},
23+
"devDependencies": {
24+
"@types/react": "19.2.17",
25+
"@types/react-dom": "19.2.2",
26+
"@vitejs/plugin-react": "6.0.4",
27+
"typescript": "6.0.3",
28+
"vite": "8.1.5"
29+
},
30+
"packageManager": "pnpm@10.17.1",
31+
"engines": {
32+
"node": "^20.19.0 || >=22.12.0"
33+
}
34+
}

0 commit comments

Comments
 (0)