automatick 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{chunk-YNLOTPDY.js → chunk-A375T3UD.js} +39 -3
- package/dist/chunk-IKR53C2U.js +12 -0
- package/dist/chunk-LMHH7YPE.js +89 -0
- package/dist/chunk-VPS3ZXWI.js +132 -0
- package/dist/engine.cjs +42 -3
- package/dist/engine.d.cts +42 -18
- package/dist/engine.d.ts +42 -18
- package/dist/engine.js +2 -1
- package/dist/react/EngineContext.d.cts +4 -3
- package/dist/react/EngineContext.d.ts +4 -3
- package/dist/react/Simulation.cjs +341 -102
- package/dist/react/Simulation.d.cts +36 -16
- package/dist/react/Simulation.d.ts +36 -16
- package/dist/react/Simulation.js +93 -100
- package/dist/react/SimulationContext.d.cts +1 -2
- package/dist/react/SimulationContext.d.ts +1 -2
- package/dist/react/hooks.d.cts +1 -1
- package/dist/react/hooks.d.ts +1 -1
- package/dist/react/useSimulationCanvas.cjs +2 -10
- package/dist/react/useSimulationCanvas.d.cts +4 -7
- package/dist/react/useSimulationCanvas.d.ts +4 -7
- package/dist/react/useSimulationCanvas.js +2 -10
- package/dist/sim.cjs +7 -2
- package/dist/sim.d.cts +34 -14
- package/dist/sim.d.ts +34 -14
- package/dist/sim.js +6 -5
- package/dist/standalone/engine.js +242 -0
- package/dist/standalone/sim.js +11 -0
- package/dist/state.cjs +18 -0
- package/dist/state.d.cts +16 -0
- package/dist/state.d.ts +16 -0
- package/dist/state.js +0 -0
- package/dist/worker/createSimWorker.cjs +12 -3
- package/dist/worker/createSimWorker.d.cts +1 -2
- package/dist/worker/createSimWorker.d.ts +1 -2
- package/dist/worker/createSimWorker.js +3 -119
- package/dist/worker/protocol.d.cts +5 -5
- package/dist/worker/protocol.d.ts +5 -5
- package/dist/worker/workerRunner.cjs +21 -6
- package/dist/worker/workerRunner.d.cts +11 -3
- package/dist/worker/workerRunner.d.ts +11 -3
- package/dist/worker/workerRunner.js +3 -70
- package/package.json +2 -2
package/dist/react/Simulation.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useStableCallback
|
|
3
3
|
} from "../chunk-SK5SHIWY.js";
|
|
4
|
+
import {
|
|
5
|
+
createSimWorker
|
|
6
|
+
} from "../chunk-VPS3ZXWI.js";
|
|
7
|
+
import {
|
|
8
|
+
createWorkerRunner
|
|
9
|
+
} from "../chunk-LMHH7YPE.js";
|
|
4
10
|
import {
|
|
5
11
|
createEngine
|
|
6
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-A375T3UD.js";
|
|
13
|
+
import "../chunk-IKR53C2U.js";
|
|
7
14
|
import {
|
|
8
15
|
EngineContext
|
|
9
16
|
} from "../chunk-OA6FGXTP.js";
|
|
@@ -15,18 +22,35 @@ import {
|
|
|
15
22
|
import React from "react";
|
|
16
23
|
import { jsx } from "react/jsx-runtime";
|
|
17
24
|
function LocalSimulation(props) {
|
|
18
|
-
const {
|
|
25
|
+
const {
|
|
26
|
+
init,
|
|
27
|
+
step,
|
|
28
|
+
shouldStop,
|
|
29
|
+
defaultParams,
|
|
30
|
+
params: paramsProp,
|
|
31
|
+
children,
|
|
32
|
+
autoplay
|
|
33
|
+
} = props;
|
|
19
34
|
const engineRef = React.useRef(null);
|
|
20
35
|
if (!engineRef.current) {
|
|
21
|
-
|
|
36
|
+
let initialParams;
|
|
37
|
+
if (defaultParams && paramsProp) {
|
|
38
|
+
initialParams = { ...defaultParams, ...paramsProp };
|
|
39
|
+
} else if (defaultParams) {
|
|
40
|
+
initialParams = defaultParams;
|
|
41
|
+
} else if (paramsProp) {
|
|
42
|
+
initialParams = paramsProp;
|
|
43
|
+
}
|
|
22
44
|
engineRef.current = createEngine({
|
|
23
|
-
init
|
|
24
|
-
step
|
|
25
|
-
shouldStop
|
|
45
|
+
init,
|
|
46
|
+
step,
|
|
47
|
+
shouldStop,
|
|
26
48
|
initialParams,
|
|
27
49
|
maxTime: props.maxTime,
|
|
28
50
|
delayMs: props.delayMs,
|
|
29
|
-
ticksPerFrame: props.ticksPerFrame
|
|
51
|
+
ticksPerFrame: props.ticksPerFrame,
|
|
52
|
+
// The React adapter drives its own rAF loop tied to component lifecycle.
|
|
53
|
+
autoFrame: false
|
|
30
54
|
});
|
|
31
55
|
}
|
|
32
56
|
const engine = engineRef.current;
|
|
@@ -62,116 +86,70 @@ function LocalSimulation(props) {
|
|
|
62
86
|
}, [engine]);
|
|
63
87
|
return /* @__PURE__ */ jsx(SimulationProvider, { snapshot, backend: engine, children });
|
|
64
88
|
}
|
|
89
|
+
function engineUrl() {
|
|
90
|
+
return new URL("../standalone/engine.js", import.meta.url).href;
|
|
91
|
+
}
|
|
65
92
|
function WorkerSimulation(props) {
|
|
66
93
|
const { children, autoplay } = props;
|
|
67
|
-
const [
|
|
94
|
+
const [runner, setRunner] = React.useState(
|
|
95
|
+
null
|
|
96
|
+
);
|
|
97
|
+
const [snapshot, setSnapshot] = React.useState(
|
|
68
98
|
null
|
|
69
99
|
);
|
|
70
|
-
const [snapshot, setSnapshot] = React.useState(null);
|
|
71
|
-
const propsRef = React.useRef(props);
|
|
72
|
-
propsRef.current = props;
|
|
73
100
|
React.useEffect(() => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
shouldStop: simModule.shouldStop,
|
|
86
|
-
initialParams,
|
|
87
|
-
maxTime: p.maxTime
|
|
88
|
-
});
|
|
89
|
-
if (cancelled) {
|
|
90
|
-
engine.destroy();
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const delayMs = p.delayMs ?? 0;
|
|
94
|
-
const ticksPerFrame = p.ticksPerFrame ?? 1;
|
|
95
|
-
let loopTimer = null;
|
|
96
|
-
function stopLoop() {
|
|
97
|
-
if (loopTimer !== null) {
|
|
98
|
-
clearTimeout(loopTimer);
|
|
99
|
-
loopTimer = null;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function tickLoop() {
|
|
103
|
-
if (engine.getStatus() !== "playing") return;
|
|
104
|
-
for (let i = 0; i < ticksPerFrame; i++) {
|
|
105
|
-
engine.advance(1);
|
|
106
|
-
const s = engine.getStatus();
|
|
107
|
-
if (s === "stopped") return;
|
|
108
|
-
if (s !== "paused") break;
|
|
109
|
-
}
|
|
110
|
-
if (engine.getStatus() === "paused") {
|
|
111
|
-
engine.play();
|
|
112
|
-
}
|
|
113
|
-
loopTimer = setTimeout(tickLoop, delayMs);
|
|
101
|
+
const moduleUrl = props.worker.toString();
|
|
102
|
+
const initialParams = props.params ?? {};
|
|
103
|
+
const worker = createSimWorker({
|
|
104
|
+
moduleUrl,
|
|
105
|
+
engineUrl: engineUrl(),
|
|
106
|
+
initialParams,
|
|
107
|
+
config: {
|
|
108
|
+
maxTime: props.maxTime,
|
|
109
|
+
delayMs: props.delayMs,
|
|
110
|
+
ticksPerFrame: props.ticksPerFrame,
|
|
111
|
+
snapshotIntervalMs: props.snapshotIntervalMs
|
|
114
112
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
pause: () => {
|
|
124
|
-
stopLoop();
|
|
125
|
-
engine.pause();
|
|
126
|
-
},
|
|
127
|
-
stop: () => {
|
|
128
|
-
stopLoop();
|
|
129
|
-
engine.stop();
|
|
130
|
-
},
|
|
131
|
-
seek: (tick) => {
|
|
132
|
-
stopLoop();
|
|
133
|
-
engine.seek(tick);
|
|
134
|
-
},
|
|
135
|
-
advance: (count) => engine.advance(count),
|
|
136
|
-
setParams: (patch) => engine.setParams(patch),
|
|
137
|
-
resetWith: (patch) => {
|
|
138
|
-
stopLoop();
|
|
139
|
-
engine.resetWith(patch);
|
|
140
|
-
},
|
|
141
|
-
destroy: () => {
|
|
142
|
-
stopLoop();
|
|
143
|
-
engine.destroy();
|
|
144
|
-
},
|
|
145
|
-
recordDrawTime: (tick, ms) => engine.recordDrawTime(tick, ms),
|
|
146
|
-
getPerformance: () => engine.getPerformance()
|
|
147
|
-
};
|
|
148
|
-
if (!cancelled) {
|
|
149
|
-
setBackend(runner);
|
|
150
|
-
setSnapshot(engine.getSnapshot());
|
|
113
|
+
});
|
|
114
|
+
const r = createWorkerRunner(worker, {
|
|
115
|
+
initialParams,
|
|
116
|
+
config: {
|
|
117
|
+
maxTime: props.maxTime,
|
|
118
|
+
delayMs: props.delayMs,
|
|
119
|
+
ticksPerFrame: props.ticksPerFrame,
|
|
120
|
+
snapshotIntervalMs: props.snapshotIntervalMs
|
|
151
121
|
}
|
|
152
|
-
})
|
|
122
|
+
});
|
|
123
|
+
const unsub = r.subscribe((next) => setSnapshot(next));
|
|
124
|
+
setRunner(r);
|
|
153
125
|
return () => {
|
|
154
|
-
|
|
155
|
-
|
|
126
|
+
unsub();
|
|
127
|
+
r.destroy();
|
|
156
128
|
};
|
|
157
129
|
}, []);
|
|
158
130
|
React.useEffect(() => {
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
}, [backend]);
|
|
162
|
-
React.useEffect(() => {
|
|
163
|
-
if (autoplay && backend) backend.play();
|
|
164
|
-
}, [backend, autoplay]);
|
|
131
|
+
if (autoplay && runner) runner.play();
|
|
132
|
+
}, [runner, autoplay]);
|
|
165
133
|
const isFirstRender = React.useRef(true);
|
|
166
134
|
React.useEffect(() => {
|
|
167
135
|
if (isFirstRender.current) {
|
|
168
136
|
isFirstRender.current = false;
|
|
169
137
|
return;
|
|
170
138
|
}
|
|
171
|
-
if (props.params &&
|
|
172
|
-
}, [
|
|
173
|
-
|
|
174
|
-
|
|
139
|
+
if (props.params && runner) runner.setParams(props.params);
|
|
140
|
+
}, [runner, props.params]);
|
|
141
|
+
React.useEffect(() => {
|
|
142
|
+
if (!runner) return;
|
|
143
|
+
if (props.delayMs === void 0 && props.ticksPerFrame === void 0 && props.snapshotIntervalMs === void 0)
|
|
144
|
+
return;
|
|
145
|
+
runner.setConfig({
|
|
146
|
+
delayMs: props.delayMs,
|
|
147
|
+
ticksPerFrame: props.ticksPerFrame,
|
|
148
|
+
snapshotIntervalMs: props.snapshotIntervalMs
|
|
149
|
+
});
|
|
150
|
+
}, [runner, props.delayMs, props.ticksPerFrame, props.snapshotIntervalMs]);
|
|
151
|
+
if (!snapshot || !runner || snapshot.data === void 0) return null;
|
|
152
|
+
return /* @__PURE__ */ jsx(SimulationProvider, { snapshot, backend: runner, children });
|
|
175
153
|
}
|
|
176
154
|
function SimulationProvider({
|
|
177
155
|
snapshot,
|
|
@@ -207,6 +185,8 @@ function SimulationProvider({
|
|
|
207
185
|
);
|
|
208
186
|
const engineValue = React.useMemo(
|
|
209
187
|
() => ({
|
|
188
|
+
// TODO(#14): casts here bridge EngineContext's non-generic shape to
|
|
189
|
+
// the typed Backend<Data, Params>. Make EngineContext generic to drop them.
|
|
210
190
|
subscribe: (listener) => backend.subscribe(
|
|
211
191
|
listener
|
|
212
192
|
),
|
|
@@ -228,6 +208,19 @@ function Simulation(props) {
|
|
|
228
208
|
if ("worker" in props && props.worker != null) {
|
|
229
209
|
return /* @__PURE__ */ jsx(WorkerSimulation, { ...props });
|
|
230
210
|
}
|
|
211
|
+
if ("sim" in props && props.sim != null) {
|
|
212
|
+
const { sim, ...rest } = props;
|
|
213
|
+
return /* @__PURE__ */ jsx(
|
|
214
|
+
LocalSimulation,
|
|
215
|
+
{
|
|
216
|
+
...rest,
|
|
217
|
+
init: sim.init,
|
|
218
|
+
step: sim.step,
|
|
219
|
+
shouldStop: sim.shouldStop,
|
|
220
|
+
defaultParams: sim.defaultParams
|
|
221
|
+
}
|
|
222
|
+
);
|
|
223
|
+
}
|
|
231
224
|
return /* @__PURE__ */ jsx(LocalSimulation, { ...props });
|
|
232
225
|
}
|
|
233
226
|
export {
|
package/dist/react/hooks.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SimulationContextValue } from './SimulationContext.cjs';
|
|
2
2
|
import { SimModule } from '../sim.cjs';
|
|
3
3
|
import 'react';
|
|
4
|
-
import '../
|
|
4
|
+
import '../state.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Helper type: extract Data and Params from a SimModule type, or use them directly.
|
package/dist/react/hooks.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SimulationContextValue } from './SimulationContext.js';
|
|
2
2
|
import { SimModule } from '../sim.js';
|
|
3
3
|
import 'react';
|
|
4
|
-
import '../
|
|
4
|
+
import '../state.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Helper type: extract Data and Params from a SimModule type, or use them directly.
|
|
@@ -59,11 +59,7 @@ function useSimulationCanvas(draw) {
|
|
|
59
59
|
if (ctx) {
|
|
60
60
|
const snap = engineCtx.getSnapshot();
|
|
61
61
|
const t0 = performance.now();
|
|
62
|
-
drawRef.current(ctx,
|
|
63
|
-
data: snap.data,
|
|
64
|
-
params: snap.params,
|
|
65
|
-
tick: snap.tick
|
|
66
|
-
});
|
|
62
|
+
drawRef.current(ctx, snap);
|
|
67
63
|
engineCtx.recordDrawTime(snap.tick, performance.now() - t0);
|
|
68
64
|
}
|
|
69
65
|
}
|
|
@@ -74,11 +70,7 @@ function useSimulationCanvas(draw) {
|
|
|
74
70
|
const ctx = canvas2.getContext("2d");
|
|
75
71
|
if (!ctx) return;
|
|
76
72
|
const t0 = performance.now();
|
|
77
|
-
drawRef.current(ctx,
|
|
78
|
-
data: snap.data,
|
|
79
|
-
params: snap.params,
|
|
80
|
-
tick: snap.tick
|
|
81
|
-
});
|
|
73
|
+
drawRef.current(ctx, snap);
|
|
82
74
|
engineCtx.recordDrawTime(snap.tick, performance.now() - t0);
|
|
83
75
|
}
|
|
84
76
|
);
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { SimModule } from '../sim.cjs';
|
|
3
|
+
import { State } from '../state.cjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Draw callback signature for useSimulationCanvas.
|
|
6
|
-
* Called imperatively on every engine
|
|
7
|
+
* Called imperatively on every engine state emit — no React re-render involved.
|
|
7
8
|
*/
|
|
8
|
-
type CanvasDrawFn<Data, Params> = (ctx: CanvasRenderingContext2D, snapshot:
|
|
9
|
-
data: Data;
|
|
10
|
-
params: Params;
|
|
11
|
-
tick: number;
|
|
12
|
-
}) => void;
|
|
9
|
+
type CanvasDrawFn<Data, Params> = (ctx: CanvasRenderingContext2D, snapshot: State<Data, Params>) => void;
|
|
13
10
|
type InferDraw<T, FallbackParams> = T extends SimModule<infer D, infer P> ? CanvasDrawFn<D, P> : CanvasDrawFn<T, FallbackParams>;
|
|
14
11
|
/**
|
|
15
|
-
* Subscribe to simulation
|
|
12
|
+
* Subscribe to simulation state and draw to a canvas without React re-renders.
|
|
16
13
|
*
|
|
17
14
|
* Returns a ref to attach to a `<canvas>` element. The `draw` callback is called
|
|
18
15
|
* directly from the engine's subscription — it bypasses React's state/reconciliation
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { SimModule } from '../sim.js';
|
|
3
|
+
import { State } from '../state.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Draw callback signature for useSimulationCanvas.
|
|
6
|
-
* Called imperatively on every engine
|
|
7
|
+
* Called imperatively on every engine state emit — no React re-render involved.
|
|
7
8
|
*/
|
|
8
|
-
type CanvasDrawFn<Data, Params> = (ctx: CanvasRenderingContext2D, snapshot:
|
|
9
|
-
data: Data;
|
|
10
|
-
params: Params;
|
|
11
|
-
tick: number;
|
|
12
|
-
}) => void;
|
|
9
|
+
type CanvasDrawFn<Data, Params> = (ctx: CanvasRenderingContext2D, snapshot: State<Data, Params>) => void;
|
|
13
10
|
type InferDraw<T, FallbackParams> = T extends SimModule<infer D, infer P> ? CanvasDrawFn<D, P> : CanvasDrawFn<T, FallbackParams>;
|
|
14
11
|
/**
|
|
15
|
-
* Subscribe to simulation
|
|
12
|
+
* Subscribe to simulation state and draw to a canvas without React re-renders.
|
|
16
13
|
*
|
|
17
14
|
* Returns a ref to attach to a `<canvas>` element. The `draw` callback is called
|
|
18
15
|
* directly from the engine's subscription — it bypasses React's state/reconciliation
|
|
@@ -21,11 +21,7 @@ function useSimulationCanvas(draw) {
|
|
|
21
21
|
if (ctx) {
|
|
22
22
|
const snap = engineCtx.getSnapshot();
|
|
23
23
|
const t0 = performance.now();
|
|
24
|
-
drawRef.current(ctx,
|
|
25
|
-
data: snap.data,
|
|
26
|
-
params: snap.params,
|
|
27
|
-
tick: snap.tick
|
|
28
|
-
});
|
|
24
|
+
drawRef.current(ctx, snap);
|
|
29
25
|
engineCtx.recordDrawTime(snap.tick, performance.now() - t0);
|
|
30
26
|
}
|
|
31
27
|
}
|
|
@@ -36,11 +32,7 @@ function useSimulationCanvas(draw) {
|
|
|
36
32
|
const ctx = canvas2.getContext("2d");
|
|
37
33
|
if (!ctx) return;
|
|
38
34
|
const t0 = performance.now();
|
|
39
|
-
drawRef.current(ctx,
|
|
40
|
-
data: snap.data,
|
|
41
|
-
params: snap.params,
|
|
42
|
-
tick: snap.tick
|
|
43
|
-
});
|
|
35
|
+
drawRef.current(ctx, snap);
|
|
44
36
|
engineCtx.recordDrawTime(snap.tick, performance.now() - t0);
|
|
45
37
|
}
|
|
46
38
|
);
|
package/dist/sim.cjs
CHANGED
|
@@ -20,13 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/sim.ts
|
|
21
21
|
var sim_exports = {};
|
|
22
22
|
__export(sim_exports, {
|
|
23
|
-
defineSim: () => defineSim
|
|
23
|
+
defineSim: () => defineSim,
|
|
24
|
+
isInitFn: () => isInitFn
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(sim_exports);
|
|
27
|
+
function isInitFn(init) {
|
|
28
|
+
return typeof init === "function";
|
|
29
|
+
}
|
|
26
30
|
function defineSim(sim) {
|
|
27
31
|
return sim;
|
|
28
32
|
}
|
|
29
33
|
// Annotate the CommonJS export names for ESM import in node:
|
|
30
34
|
0 && (module.exports = {
|
|
31
|
-
defineSim
|
|
35
|
+
defineSim,
|
|
36
|
+
isInitFn
|
|
32
37
|
});
|
package/dist/sim.d.cts
CHANGED
|
@@ -1,19 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { State } from './state.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Initial state for a simulation. Either a value of type `Data`, or a function
|
|
5
|
+
* `(params) => Data`. The function form is required when the initial state
|
|
6
|
+
* depends on params; otherwise pass the value directly.
|
|
7
|
+
*
|
|
8
|
+
* `Data` itself must not be a function — it has to be structured-cloneable so
|
|
9
|
+
* it can cross the worker boundary and be safely re-emitted on resetWith.
|
|
10
|
+
* That's what makes this union unambiguous: a `function` value is always the
|
|
11
|
+
* `(params) => Data` branch.
|
|
12
|
+
*/
|
|
13
|
+
type SimInit<Data, Params> = ((params: Params) => Data) | Data;
|
|
14
|
+
/**
|
|
15
|
+
* Type guard that narrows a `SimInit` to its function branch. Defined as a
|
|
16
|
+
* user-typed predicate so we can do the narrowing without an `as` cast.
|
|
17
|
+
*/
|
|
18
|
+
declare function isInitFn<Data, Params>(init: SimInit<Data, Params>): init is (params: Params) => Data;
|
|
7
19
|
/** A simulation module: the pure business logic that automatick drives. */
|
|
8
|
-
type SimModule<Data, Params
|
|
9
|
-
/**
|
|
10
|
-
|
|
20
|
+
type SimModule<Data, Params = Record<string, never>> = {
|
|
21
|
+
/**
|
|
22
|
+
* Initial simulation state — value or `(params) => Data`. When a value is
|
|
23
|
+
* passed, the engine takes a fresh `structuredClone` on every (re)init so
|
|
24
|
+
* mutations inside `step` never leak across resets.
|
|
25
|
+
*/
|
|
26
|
+
init: SimInit<Data, Params>;
|
|
11
27
|
/** Advance the simulation by one tick. Must be pure and synchronous. */
|
|
12
|
-
step: (
|
|
28
|
+
step: (state: State<Data, Params>) => Data;
|
|
13
29
|
/** Optional termination predicate. Checked after each step. If it returns true, the simulation stops. */
|
|
14
30
|
shouldStop?: (data: Data, params: Params) => boolean;
|
|
15
|
-
/**
|
|
16
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Default parameter values. Optional — sims without tweakable params can
|
|
33
|
+
* omit this. When omitted, `Params` defaults to `Record<string, never>` and
|
|
34
|
+
* the engine seeds an empty params object.
|
|
35
|
+
*/
|
|
36
|
+
defaultParams?: Params;
|
|
17
37
|
};
|
|
18
38
|
/**
|
|
19
39
|
* Define a simulation module with full type inference.
|
|
@@ -31,10 +51,10 @@ type SimModule<Data, Params> = {
|
|
|
31
51
|
* });
|
|
32
52
|
* ```
|
|
33
53
|
*/
|
|
34
|
-
declare function defineSim<Data, Params
|
|
54
|
+
declare function defineSim<Data, Params = Record<string, never>>(sim: SimModule<Data, Params>): SimModule<Data, Params>;
|
|
35
55
|
/** Extract the Data type from a SimModule. */
|
|
36
56
|
type SimData<M> = M extends SimModule<infer D, infer _P> ? D : never;
|
|
37
57
|
/** Extract the Params type from a SimModule. */
|
|
38
58
|
type SimParams<M> = M extends SimModule<infer _D, infer P> ? P : never;
|
|
39
59
|
|
|
40
|
-
export { type SimData, type
|
|
60
|
+
export { type SimData, type SimInit, type SimModule, type SimParams, defineSim, isInitFn };
|
package/dist/sim.d.ts
CHANGED
|
@@ -1,19 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { State } from './state.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Initial state for a simulation. Either a value of type `Data`, or a function
|
|
5
|
+
* `(params) => Data`. The function form is required when the initial state
|
|
6
|
+
* depends on params; otherwise pass the value directly.
|
|
7
|
+
*
|
|
8
|
+
* `Data` itself must not be a function — it has to be structured-cloneable so
|
|
9
|
+
* it can cross the worker boundary and be safely re-emitted on resetWith.
|
|
10
|
+
* That's what makes this union unambiguous: a `function` value is always the
|
|
11
|
+
* `(params) => Data` branch.
|
|
12
|
+
*/
|
|
13
|
+
type SimInit<Data, Params> = ((params: Params) => Data) | Data;
|
|
14
|
+
/**
|
|
15
|
+
* Type guard that narrows a `SimInit` to its function branch. Defined as a
|
|
16
|
+
* user-typed predicate so we can do the narrowing without an `as` cast.
|
|
17
|
+
*/
|
|
18
|
+
declare function isInitFn<Data, Params>(init: SimInit<Data, Params>): init is (params: Params) => Data;
|
|
7
19
|
/** A simulation module: the pure business logic that automatick drives. */
|
|
8
|
-
type SimModule<Data, Params
|
|
9
|
-
/**
|
|
10
|
-
|
|
20
|
+
type SimModule<Data, Params = Record<string, never>> = {
|
|
21
|
+
/**
|
|
22
|
+
* Initial simulation state — value or `(params) => Data`. When a value is
|
|
23
|
+
* passed, the engine takes a fresh `structuredClone` on every (re)init so
|
|
24
|
+
* mutations inside `step` never leak across resets.
|
|
25
|
+
*/
|
|
26
|
+
init: SimInit<Data, Params>;
|
|
11
27
|
/** Advance the simulation by one tick. Must be pure and synchronous. */
|
|
12
|
-
step: (
|
|
28
|
+
step: (state: State<Data, Params>) => Data;
|
|
13
29
|
/** Optional termination predicate. Checked after each step. If it returns true, the simulation stops. */
|
|
14
30
|
shouldStop?: (data: Data, params: Params) => boolean;
|
|
15
|
-
/**
|
|
16
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Default parameter values. Optional — sims without tweakable params can
|
|
33
|
+
* omit this. When omitted, `Params` defaults to `Record<string, never>` and
|
|
34
|
+
* the engine seeds an empty params object.
|
|
35
|
+
*/
|
|
36
|
+
defaultParams?: Params;
|
|
17
37
|
};
|
|
18
38
|
/**
|
|
19
39
|
* Define a simulation module with full type inference.
|
|
@@ -31,10 +51,10 @@ type SimModule<Data, Params> = {
|
|
|
31
51
|
* });
|
|
32
52
|
* ```
|
|
33
53
|
*/
|
|
34
|
-
declare function defineSim<Data, Params
|
|
54
|
+
declare function defineSim<Data, Params = Record<string, never>>(sim: SimModule<Data, Params>): SimModule<Data, Params>;
|
|
35
55
|
/** Extract the Data type from a SimModule. */
|
|
36
56
|
type SimData<M> = M extends SimModule<infer D, infer _P> ? D : never;
|
|
37
57
|
/** Extract the Params type from a SimModule. */
|
|
38
58
|
type SimParams<M> = M extends SimModule<infer _D, infer P> ? P : never;
|
|
39
59
|
|
|
40
|
-
export { type SimData, type
|
|
60
|
+
export { type SimData, type SimInit, type SimModule, type SimParams, defineSim, isInitFn };
|
package/dist/sim.js
CHANGED