@mantaq/core 0.3.0 → 0.5.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @mantaq/core
2
2
 
3
- State machine library. Small API surface — cook the primitives, not the framework.
3
+ State machine library. Small API surface. Cook the primitives, not the framework.
4
4
 
5
5
  ## Install
6
6
 
@@ -81,7 +81,7 @@ See [anderscan.github.io/mantaq](https://anderscan.github.io/mantaq) for full do
81
81
 
82
82
  ### Typed Actor Context
83
83
 
84
- Cast the context type once at the constructor level. Handlers receive the typed context automatically — no casting needed per-handler.
84
+ Cast the context type once at the constructor level. Handlers receive the typed context automatically. No casting needed per-handler.
85
85
 
86
86
  ```ts
87
87
  interface AuthContext {
@@ -106,7 +106,7 @@ const actor = new Actor({
106
106
  });
107
107
  ```
108
108
 
109
- Context is user land — mutate it freely. `set()` is the write signal: any `set()` triggers `change`, even with the same reference. Call it after you mutate:
109
+ Context is user land. Mutate it freely. `set()` is the write signal: any `set()` triggers `change`, even with the same reference. Call it after you mutate:
110
110
 
111
111
  ```ts
112
112
  const s = opts.context.get();
@@ -121,7 +121,7 @@ opts.context.set(s);
121
121
  opts!.context.phoneNumber = event.payload.phoneNumber;
122
122
  ```
123
123
 
124
- **Anti-pattern — casting in every handler:**
124
+ **Anti-pattern. Casting in every handler:**
125
125
 
126
126
  ```ts
127
127
  setup: (m) => {
@@ -160,7 +160,7 @@ setup: (m) => {
160
160
  },
161
161
  ```
162
162
 
163
- **Anti-pattern — skipping the generic parameter:**
163
+ **Anti-pattern. Skipping the generic parameter:**
164
164
 
165
165
  ```ts
166
166
  // ❌ payload is `unknown`, no type safety
@@ -170,7 +170,7 @@ const badEvent = event("SIGN_IN")();
170
170
  const goodEvent = event("SIGN_IN")<{ phoneNumber: string }>();
171
171
  ```
172
172
 
173
- **Anti-pattern — sending raw objects instead of using `.create()`:**
173
+ **Anti-pattern. Sending raw objects instead of using `.create()`:**
174
174
 
175
175
  ```ts
176
176
  // ✅ typed
@@ -182,7 +182,7 @@ actor.send({ type: "SIGN_IN", payload: { phoneNumber: "+1234567890" } });
182
182
 
183
183
  ### Effects and Event Typing
184
184
 
185
- Effects run when entering a state. The `event` parameter in effects is typed `InternalEvent` — `{ type, payload? }` with `payload: unknown` — not the specific event that triggered the transition. This is by design: effects live on states, not transitions. Multiple transitions can lead to the same state, so the effect cannot know which event caused entry.
185
+ Effects run when entering a state. The `event` parameter in effects is typed `InternalEvent`, `{ type, payload? }` with `payload: unknown`, not the specific event that triggered the transition. This is by design: effects live on states, not transitions. Multiple transitions can lead to the same state, so the effect cannot know which event caused entry.
186
186
 
187
187
  **Pass data to effects via state payload.** Set the payload in the transition return, read it from `state.payload` in the effect.
188
188
 
@@ -198,12 +198,15 @@ const actor = new Actor({
198
198
  states: [idleState, loadingState, loadedState],
199
199
  initial: idleState,
200
200
  setup: (m) => {
201
- m.effect(loadingState, ({ state, emit, clock }) => {
202
- // state.payload is typed to the state's declared payload: { url: string }
203
- const url = state.payload.url;
204
- clock.setTimeout(1000, () => {
205
- emit(doneEvent.create());
206
- });
201
+ m.effect(loadingState, {
202
+ name: "fetchUrl",
203
+ fn: ({ state, emit, clock }) => {
204
+ // state.payload is typed to the state's declared payload: { url: string }
205
+ const url = state.payload.url;
206
+ clock.setTimeout(1000, () => {
207
+ emit(doneEvent.create());
208
+ });
209
+ },
207
210
  });
208
211
  m.on(idleState, fetchEvent, (event) => {
209
212
  return { state: loadingState.create({ url: event.payload.url }) };
@@ -213,7 +216,7 @@ const actor = new Actor({
213
216
  });
214
217
  ```
215
218
 
216
- **Context is also possible but less precise.** Context is shared across all states — writing to context in one state leaks into others. Prefer state payload for data that belongs to a specific state entry.
219
+ **Context is also possible but less precise.** Context is shared across all states. Writing to context in one state leaks into others. Prefer state payload for data that belongs to a specific state entry.
217
220
 
218
221
  ```ts
219
222
  // ✅ state payload — scoped to this state entry
@@ -226,12 +229,15 @@ opts!.context.set(s);
226
229
  return { state: loadingState };
227
230
  ```
228
231
 
229
- **Anti-pattern — depending on `event` in effects:**
232
+ **Anti-pattern. Depending on `event` in effects:**
230
233
 
231
234
  ```ts
232
- m.effect(loadingState, ({ event, emit }) => {
233
- // ❌ event is InternalEvent — payload is unknown, not the triggering event's payload
234
- emit(doneEvent.create()); // fine — but event.payload.url would be a type error
235
+ m.effect(loadingState, {
236
+ name: "fetchUrl",
237
+ fn: ({ event, emit }) => {
238
+ // ❌ event is InternalEvent — payload is unknown, not the triggering event's payload
239
+ emit(doneEvent.create()); // fine — but event.payload.url would be a type error
240
+ },
235
241
  });
236
242
  ```
237
243
 
@@ -241,9 +247,9 @@ The `event` parameter exists for convenience (e.g., logging), not for business l
241
247
 
242
248
  Mantaq uses two event queues: **external** (user-sent) and **internal** (effect-emitted). Understanding the difference prevents subtle bugs.
243
249
 
244
- **External events** are sent via `actor.send()` — they trigger transitions on the current state.
250
+ **External events** are sent via `actor.send()`. They trigger transitions on the current state.
245
251
 
246
- **Internal events** are emitted from effects via `emit()` — they are queued and processed after the current transition completes.
252
+ **Internal events** are emitted from effects via `emit()`. They are queued and processed after the current transition completes.
247
253
 
248
254
  ```ts
249
255
  const tickEvent = event("TICK")();
@@ -255,10 +261,13 @@ const actor = new Actor({
255
261
  states: [idleState, connectedState],
256
262
  initial: idleState,
257
263
  setup: (m) => {
258
- m.effect(idleState, ({ emit, clock }) => {
259
- clock.setTimeout(1000, () => {
260
- emit(tickEvent.create()); // goes to internal queue
261
- });
264
+ m.effect(idleState, {
265
+ name: "scheduleTick",
266
+ fn: ({ emit, clock }) => {
267
+ clock.setTimeout(1000, () => {
268
+ emit(tickEvent.create()); // goes to internal queue
269
+ });
270
+ },
262
271
  });
263
272
  m.on(idleState, connectEvent, () => ({ state: connectedState })); // external handler
264
273
  m.on(idleState, tickEvent, () => ({})); // internal handler
@@ -266,9 +275,9 @@ const actor = new Actor({
266
275
  });
267
276
  ```
268
277
 
269
- **Ordering:** External events process one at a time. Internal events emitted during a transition process depth-first after that transition. Internal events from effects process after the effect completes — this is a common source of bugs. If an effect emits an internal event and the actor is in a different state by the time that event processes, the handler may not exist or may behave unexpectedly.
278
+ **Ordering:** External events process one at a time. Internal events emitted during a transition process depth-first after that transition. Internal events from effects process after the effect completes. This is a common source of bugs. If an effect emits an internal event and the actor is in a different state by the time that event processes, the handler may not exist or may behave unexpectedly.
270
279
 
271
- **Anti-pattern — mixing internal/external incorrectly:**
280
+ **Anti-pattern. Mixing internal/external incorrectly:**
272
281
 
273
282
  ```ts
274
283
  // ❌ declaring a user-sent event as internal
@@ -280,7 +289,7 @@ inputs: [connectEvent],
280
289
 
281
290
  ### Effect Pattern
282
291
 
283
- Effects run on state entry. They receive typed context, an AbortSignal, and an emit function.
292
+ Effects run on state entry. They receive typed context, an AbortSignal, and an emit function. Each effect carries a required camelCase `name` describing what it does — tests can assert on it (`harness.assertEffectRan(stateName, effectName)`) and executed effects appear in history as `{ stateName, effectName }`.
284
293
 
285
294
  ```ts
286
295
  type MyContext = { retryCount: number; maxRetries: number };
@@ -297,17 +306,20 @@ function createActor() {
297
306
  initial: idleState,
298
307
  context: { retryCount: 0, maxRetries: 3 } as MyContext,
299
308
  setup: (m) => {
300
- m.effect(workingState, ({ signal, context, emit, clock }) => {
301
- // context is MyContext — typed from the constructor generic
302
- if (context.retryCount >= context.maxRetries) {
303
- emit(failedEvent.create({ error: "Max retries exceeded" }));
304
- return;
305
- }
306
-
307
- clock.setTimeout(2000, () => {
308
- if (signal.aborted) return; // ✅ check signal before emitting
309
- emit(doneEvent.create());
310
- });
309
+ m.effect(workingState, {
310
+ name: "attemptWork",
311
+ fn: ({ signal, context, emit, clock }) => {
312
+ // context is MyContext — typed from the constructor generic
313
+ if (context.retryCount >= context.maxRetries) {
314
+ emit(failedEvent.create({ error: "Max retries exceeded" }));
315
+ return;
316
+ }
317
+
318
+ clock.setTimeout(2000, () => {
319
+ if (signal.aborted) return; // ✅ check signal before emitting
320
+ emit(doneEvent.create());
321
+ });
322
+ },
311
323
  });
312
324
  m.on(idleState, startEvent, () => ({ state: workingState }));
313
325
  m.on(workingState, doneEvent, () => ({ state: doneState }));
@@ -322,30 +334,36 @@ function createActor() {
322
334
  }
323
335
  ```
324
336
 
325
- **Anti-pattern — not checking `signal.aborted`:**
337
+ **Anti-pattern. Not checking `signal.aborted`:**
326
338
 
327
339
  ```ts
328
- m.effect(workingState, ({ signal, emit, clock }) => {
329
- // ❌ if state changes before timeout, this still fires
330
- clock.setTimeout(2000, () => {
331
- emit(doneEvent.create());
332
- });
340
+ m.effect(workingState, {
341
+ name: "attemptWork",
342
+ fn: ({ signal, emit, clock }) => {
343
+ // ❌ if state changes before timeout, this still fires
344
+ clock.setTimeout(2000, () => {
345
+ emit(doneEvent.create());
346
+ });
347
+ },
333
348
  });
334
349
  ```
335
350
 
336
351
  ```ts
337
- m.effect(workingState, ({ signal, emit, clock }) => {
338
- // ✅ guard with abort check
339
- clock.setTimeout(2000, () => {
340
- if (signal.aborted) return;
341
- emit(doneEvent.create());
342
- });
352
+ m.effect(workingState, {
353
+ name: "attemptWork",
354
+ fn: ({ signal, emit, clock }) => {
355
+ // ✅ guard with abort check
356
+ clock.setTimeout(2000, () => {
357
+ if (signal.aborted) return;
358
+ emit(doneEvent.create());
359
+ });
360
+ },
343
361
  });
344
362
  ```
345
363
 
346
364
  ### Snapshot & Restore
347
365
 
348
- `actor.snapshot()` returns a `Snapshot` — `{ path, context, payload?, regions, done?, error? }`. Context is included; `payload` is the payload the current state was entered with (present only when the transition carried one). Use it to save/restore actor state across sessions.
366
+ `actor.snapshot()` returns a `Snapshot`: `{ path, context, payload?, regions, done?, error? }`. Context is included; `payload` is the payload the current state was entered with (present only when the transition carried one). Use it to save/restore actor state across sessions.
349
367
 
350
368
  ```ts
351
369
  // Snapshot carries path, context, and regions — serialize it directly
@@ -359,7 +377,7 @@ if (raw) {
359
377
  }
360
378
  ```
361
379
 
362
- **Anti-pattern — re-serializing context by hand:**
380
+ **Anti-pattern. Re-serializing context by hand:**
363
381
 
364
382
  ```ts
365
383
  // ❌ snapshot() already includes context — no manual step needed
@@ -410,7 +428,7 @@ const manager = new Actor({
410
428
  matches(manager, "connected.health.healthy"); // ✅
411
429
  ```
412
430
 
413
- **Anti-pattern — child outputs not declared as parent internal events:**
431
+ **Anti-pattern. Child outputs not declared as parent internal events:**
414
432
 
415
433
  ```ts
416
434
  const child = new Actor({
@@ -433,7 +451,7 @@ const parent = new Actor({
433
451
 
434
452
  ### Error Handling
435
453
 
436
- Never throw from effects or transitions. Emit an error as an internal event and let a transition handle it. If user code throws anyway, the machine does **not** misbehave silently: it dies into a built-in terminal `__error` state, records `snapshot().error` (the thrown value, the state/context at the point of failure, the bad event), drops remaining events, and ignores later `send`s. Errors never escape `send()` and the machine stays deterministic — same inputs, same trace.
454
+ Never throw from effects or transitions. Emit an error as an internal event and let a transition handle it. If user code throws anyway, the machine does **not** misbehave silently: it dies into a built-in terminal `__error` state, records `snapshot().error` (the thrown value, the state/context at the point of failure, the bad event), drops remaining events, and ignores later `send`s. Errors never escape `send()` and the machine stays deterministic. Same inputs, same trace.
437
455
 
438
456
  ```ts
439
457
  const snap = actor.snapshot();
@@ -446,10 +464,10 @@ if (snap.error) {
446
464
 
447
465
  Two deliberate exceptions to "every failure is loud":
448
466
 
449
- - **Subscribers only watch.** They read snapshots and never change the machine, so a throwing `on("change")`/`on("done")`/`on("transition")`/`on("error")` callback is swallowed — the machine and its callers are unaffected.
467
+ - **Subscribers only watch.** They read snapshots and never change the machine, so a throwing `on("change")`/`on("done")`/`on("transition")`/`on("error")` callback is swallowed. The machine and its callers are unaffected.
450
468
  - **Unhandled external events are ignored.** An event with no handler in the current state is dropped by design (broadcast fan-out, cross-state sends). An **internal** event with no handler, however, is a machine-authoring bug and routes to the error state (`reason: "unhandled"`).
451
469
 
452
- A dead machine can be manually resumed with `actor.recover({ state, context })` — an explicit, **inherently dangerous** escape hatch (the caller injects state and context, so determinism no longer holds). Effects are not re-run and timers are not re-armed; processing resumes on the next event. Prefer fixing the root cause and recreating the actor.
470
+ A dead machine can be manually resumed with `actor.recover({ state, context })`. An explicit, **inherently dangerous** escape hatch (the caller injects state and context, so determinism no longer holds). Effects are not re-run and timers are not re-armed; processing resumes on the next event. Prefer fixing the root cause and recreating the actor.
453
471
 
454
472
  Catch errors inside effects and emit recovery events:
455
473
 
@@ -458,17 +476,20 @@ const doneEvent = event("WORK_DONE")();
458
476
  const failedEvent = event("WORK_FAILED")<{ error: string }>();
459
477
 
460
478
  setup: (m) => {
461
- m.effect(workingState, ({ signal, emit, clock }) => {
462
- clock.setTimeout(100, () => {
463
- if (signal.aborted) return;
464
- try {
465
- const result = riskyOperation();
466
- emit(doneEvent.create());
467
- } catch (err) {
468
- // ✅ emit error as internal event — lets transition handle it
469
- emit(failedEvent.create({ error: String(err) }));
470
- }
471
- });
479
+ m.effect(workingState, {
480
+ name: "runRiskyOperation",
481
+ fn: ({ signal, emit, clock }) => {
482
+ clock.setTimeout(100, () => {
483
+ if (signal.aborted) return;
484
+ try {
485
+ const result = riskyOperation();
486
+ emit(doneEvent.create());
487
+ } catch (err) {
488
+ // ✅ emit error as internal event — lets transition handle it
489
+ emit(failedEvent.create({ error: String(err) }));
490
+ }
491
+ });
492
+ },
472
493
  });
473
494
  m.on(workingState, doneEvent, () => ({ state: doneState }));
474
495
  m.on(workingState, failedEvent, (event) => {
@@ -478,7 +499,7 @@ setup: (m) => {
478
499
  },
479
500
  ```
480
501
 
481
- **Anti-pattern — re-throwing in effects:**
502
+ **Anti-pattern. Re-throwing in effects:**
482
503
 
483
504
  ```ts
484
505
  try {
@@ -489,7 +510,7 @@ try {
489
510
  }
490
511
  ```
491
512
 
492
- **Anti-pattern — throwing in transition handlers:**
513
+ **Anti-pattern. Throwing in transition handlers:**
493
514
 
494
515
  ```ts
495
516
  m.on(idleState, submitEvent, (event) => {
@@ -514,7 +535,7 @@ m.on(idleState, submitEvent, (event, opts) => {
514
535
 
515
536
  ### Any Handler
516
537
 
517
- Use `onAny` to intercept events across all states — useful for universal error handling, cleanup, or logging.
538
+ Use `onAny` to intercept events across all states. Useful for universal error handling, cleanup, or logging.
518
539
 
519
540
  ```ts
520
541
  setup: (m) => {
@@ -530,7 +551,7 @@ setup: (m) => {
530
551
  },
531
552
  ```
532
553
 
533
- **Anti-pattern — using `onAny` for everything:**
554
+ **Anti-pattern. Using `onAny` for everything:**
534
555
 
535
556
  ```ts
536
557
  setup: (m) => {
@@ -574,8 +595,11 @@ const actor = new Actor({
574
595
  initial: idle,
575
596
  clock,
576
597
  setup: (m) => {
577
- m.effect(idle, ({ emit, clock }) => {
578
- clock.setTimeout(5000, () => emit(timer.create()));
598
+ m.effect(idle, {
599
+ name: "armIdleTimeout",
600
+ fn: ({ emit, clock }) => {
601
+ clock.setTimeout(5000, () => emit(timer.create()));
602
+ },
579
603
  });
580
604
  m.on(idle, timer, () => ({ state: timedOut }));
581
605
  },
@@ -588,7 +612,7 @@ expect(actor.state.name).toBe("timedOut");
588
612
  expect(clock.hasPending()).toBe(false);
589
613
  ```
590
614
 
591
- **Anti-pattern — using `new Date()` or `setTimeout` directly:**
615
+ **Anti-pattern. Using `new Date()` or `setTimeout` directly:**
592
616
 
593
617
  ```ts
594
618
  // ❌ real timers — slow, flaky, non-deterministic
@@ -618,11 +642,14 @@ const actor = new Actor({
618
642
  initial: loading,
619
643
  clock,
620
644
  setup: (m) => {
621
- m.effect(loading, ({ signal, clock }) => {
622
- const id = clock.setTimeout(5000, () => {
623
- /* ... */
624
- });
625
- signal.addEventListener("abort", () => clock.clearTimeout(id));
645
+ m.effect(loading, {
646
+ name: "scheduleWork",
647
+ fn: ({ signal, clock }) => {
648
+ const id = clock.setTimeout(5000, () => {
649
+ /* ... */
650
+ });
651
+ signal.addEventListener("abort", () => clock.clearTimeout(id));
652
+ },
626
653
  });
627
654
  m.on(loading, cancel, () => ({ state: success }));
628
655
  },
@@ -651,8 +678,11 @@ const actor = new Actor({
651
678
  initial: active,
652
679
  clock,
653
680
  setup: (m) => {
654
- m.effect(active, ({ emit, clock }) => {
655
- clock.setInterval(1000, () => emit(tick.create()));
681
+ m.effect(active, {
682
+ name: "startTicker",
683
+ fn: ({ emit, clock }) => {
684
+ clock.setInterval(1000, () => emit(tick.create()));
685
+ },
656
686
  });
657
687
  m.on(active, tick, () => {
658
688
  count++;
@@ -665,7 +695,7 @@ clock.advance(3500);
665
695
  expect(count).toBe(3); // fired at 1000, 2000, 3000
666
696
  ```
667
697
 
668
- **Anti-pattern — expecting single `advance` to fire interval once:**
698
+ **Anti-pattern. Expecting single `advance` to fire interval once:**
669
699
 
670
700
  ```ts
671
701
  // ❌ advance(5000) fires interval at 1000, 2000, 3000, 4000, 5000
@@ -697,12 +727,12 @@ vp run mutation:core # mutation score must stay ≥ 90
697
727
 
698
728
  **Test contract.** Behavior is pinned by four kinds of tests, split by filename:
699
729
 
700
- - `*.test.ts` — features and happy paths, hand-maintained.
701
- - `*.error.test.ts` — failure paths: warnings, budgets, abort, unregistered, the `__error` state.
702
- - `*.property.test.ts` — property-based invariants against a reference model (fast-check, replayable via `MANTAQ_SEED`).
703
- - `*.mutation.test.ts` — directed tests that kill specific mutants; regenerable and thrown away between stryker runs.
730
+ - `*.test.ts`. Features and happy paths, hand-maintained.
731
+ - `*.error.test.ts`. Failure paths: warnings, budgets, abort, unregistered, the `__error` state.
732
+ - `*.property.test.ts`. Property-based invariants against a reference model (fast-check, replayable via `MANTAQ_SEED`).
733
+ - `*.mutation.test.ts`. Directed tests that kill specific mutants. Regenerable and thrown away between stryker runs.
704
734
 
705
- Coverage is analyzed per test (stryker `perTest` coverage analysis) so every mutant is attributed to the test that pins it. The mutation break threshold is 90 — a change that drops the score below 90 fails the build.
735
+ Coverage is analyzed per test (stryker `perTest` coverage analysis) so every mutant is attributed to the test that pins it. The mutation break threshold is 90. A change that drops the score below 90 fails the build.
706
736
 
707
737
  ## License
708
738
 
package/dist/index.d.mts CHANGED
@@ -1,16 +1,41 @@
1
- //#region src/state.d.ts
2
- declare function state<const T extends string>(id: T): <Payload = unknown>() => StateRef<T, Payload, false>;
1
+ interface Clock {
2
+ setTimeout(ms: number, options: ClockTimerOptions): number;
3
+ clearTimeout(id: number): void;
4
+ setInterval(ms: number, options: ClockIntervalOptions): number;
5
+ clearInterval(id: number): void;
6
+ now(): number;
7
+ /** Optional hook for draining queues after clock advances (VirtualClock). */
8
+ setDrain?(fn: () => void): void;
9
+ }
10
+ interface ClockTimerOptions {
11
+ cb: () => void;
12
+ signal?: AbortSignal;
13
+ eventName?: string;
14
+ }
15
+ interface ClockIntervalOptions {
16
+ cb: () => void;
17
+ signal?: AbortSignal;
18
+ }
19
+ export interface Context<T> {
20
+ get(): T;
21
+ set(value: T): void;
22
+ }
23
+ /** Two-way handle over an owner's context: reads see live values, writes flag dirty state upstream. */
24
+ export declare function Context<T>(options: {
25
+ get: () => T;
26
+ set: (value: T) => void;
27
+ }): Context<T>;
28
+ export declare function state<const T extends string>(name: T): <Payload = unknown>() => StateRef<T, Payload, false>;
3
29
  type AnyStateRef = StateRef<string, unknown, boolean>;
4
30
  interface RegionOptions {
5
31
  initial: string;
6
32
  states: Record<string, AnyStateRef>;
7
33
  }
8
34
  type RegionsOptions = Record<string, RegionOptions>;
9
- declare class StateRef<T extends string, Payload = unknown, IsFinal extends boolean = false> {
35
+ export interface StateRef<T extends string = string, Payload = unknown, IsFinal extends boolean = false> {
10
36
  readonly name: T;
11
37
  readonly isFinal: IsFinal;
12
- /** @internal */ _regions: RegionsOptions | undefined;
13
- constructor(name: T, isFinal: IsFinal);
38
+ /** @internal */ _regions?: RegionsOptions;
14
39
  regions(options: RegionsOptions): this;
15
40
  final(): StateRef<T, Payload, true>;
16
41
  create(payload: Payload): {
@@ -18,104 +43,15 @@ declare class StateRef<T extends string, Payload = unknown, IsFinal extends bool
18
43
  payload: Payload;
19
44
  };
20
45
  }
21
- //#endregion
22
- //#region src/event.d.ts
23
- declare function event<const T extends string>(type: T): <Payload extends object | void = void>() => EventRef<T, Payload>;
24
- type AnyEventRef = EventRef<string, object | void>;
25
- type InternalEvent = {
26
- type: string;
27
- payload?: unknown;
28
- };
29
- type CreatedOfEvent<T extends string, P> = P extends void ? {
30
- type: T;
31
- } : {
32
- type: T;
33
- payload: P;
34
- };
35
- declare class EventRef<const T extends string, Payload extends object | void = void> {
36
- readonly type: T;
37
- constructor(type: T);
38
- is(anyEvent: unknown): anyEvent is CreatedOfEvent<T, Payload>;
39
- create(): Payload extends void ? {
40
- type: T;
41
- } : void;
42
- create(payload: Payload): Payload extends void ? {
43
- type: T;
44
- } : {
45
- type: T;
46
- payload: Payload;
47
- };
48
- }
49
- //#endregion
50
- //#region src/clock.d.ts
51
- interface Clock {
52
- setTimeout(ms: number, cb: () => void, options?: {
53
- signal?: AbortSignal;
54
- eventName?: string;
55
- }): number;
56
- clearTimeout(id: number): void;
57
- setInterval(ms: number, cb: () => void, options?: {
58
- signal?: AbortSignal;
59
- }): number;
60
- clearInterval(id: number): void;
61
- now(): number;
62
- /** Optional hook for draining queues after clock advances (VirtualClock). */
63
- setDrain?(fn: () => void): void;
64
- }
65
- //#endregion
66
- //#region src/real-clock.d.ts
67
- declare class RealClock implements Clock {
68
- #private;
69
- now(): number;
70
- setTimeout(ms: number, cb: () => void, options?: {
71
- signal?: AbortSignal;
72
- eventName?: string;
73
- }): number;
74
- clearTimeout(id: number): void;
75
- setInterval(ms: number, cb: () => void, options?: {
76
- signal?: AbortSignal;
77
- }): number;
78
- clearInterval(id: number): void;
79
- }
80
- //#endregion
81
- //#region src/virtual-clock.d.ts
82
- declare class VirtualClock implements Clock {
83
- #private;
84
- now(): number;
85
- setTimeout(ms: number, cb: () => void, options?: {
86
- signal?: AbortSignal;
87
- eventName?: string;
88
- }): number;
89
- clearTimeout(id: number): void;
90
- setInterval(ms: number, cb: () => void, options?: {
91
- signal?: AbortSignal;
92
- }): number;
93
- clearInterval(id: number): void;
94
- advance(ms: number): void;
95
- hasPending(): boolean;
96
- pendingTimers(): Array<{
97
- id: number;
98
- deadline: number;
99
- ms: number;
100
- eventName?: string;
101
- }>;
102
- setDrain(fn: () => void): void;
103
- }
104
- //#endregion
105
- //#region src/context.d.ts
106
- declare class Context<T> {
107
- #private;
108
- constructor(get: () => T, set: (value: T) => void);
109
- get(): T;
110
- set(value: T): void;
111
- }
112
- //#endregion
113
- //#region src/actor-types.d.ts
114
- type CreatedOf<E extends AnyEventRef> = E extends EventRef<infer Type, infer P> ? CreatedOfEvent<Type, P> : never;
46
+ export declare function isStateRef(value: unknown): value is StateRef;
47
+ export declare function StateRef<T extends string, Payload = unknown, IsFinal extends boolean = false>(name: T, options: {
48
+ isFinal: IsFinal;
49
+ }): StateRef<T, Payload, IsFinal>;
50
+ type CreatedOf<E extends EventRef<string, object | void>> = E extends EventRef<infer Type, infer P> ? CreatedOfEvent<Type, P> : never;
115
51
  type NonFinalStateRef<States extends AnyStateRef[]> = Extract<States[number], {
116
52
  isFinal: false;
117
53
  }>;
118
- interface EffectInput<ActorContext, Payload = unknown> {
54
+ interface EffectInput<ActorContext, Payload = unknown, Emit = InternalEvent> {
119
55
  signal: AbortSignal;
120
56
  state: {
121
57
  name: string;
@@ -123,10 +59,12 @@ interface EffectInput<ActorContext, Payload = unknown> {
123
59
  };
124
60
  event: InternalEvent;
125
61
  context: Context<ActorContext>;
126
- emit: (event: InternalEvent) => void;
62
+ emit: (event: Emit) => void;
127
63
  clock: Clock;
128
64
  }
129
- type EffectFn<ActorContext, Payload = unknown> = (input: EffectInput<ActorContext, Payload>) => void | Promise<void>;
65
+ type EffectFn<ActorContext, Payload = unknown, Emit = InternalEvent> = (input: EffectInput<ActorContext, Payload, Emit>) => void | Promise<void> | {
66
+ then(onFulfilled: unknown, onRejected: unknown): unknown;
67
+ };
130
68
  type ErrorReason = "transition" | "effect" | "budget" | "output" | "internal" | "async" | "unhandled";
131
69
  interface ErrorInfo {
132
70
  error: unknown;
@@ -140,6 +78,8 @@ interface TransitionInfo {
140
78
  from: string;
141
79
  to: string;
142
80
  transitioned: boolean;
81
+ /** Names of effects invoked on entry into `to`, in registration order. */
82
+ effects: string[];
143
83
  }
144
84
  type ErrorState = StateRef<"__error", unknown, true>;
145
85
  type TransitionResult<AllowedState extends AnyStateRef = AnyStateRef, AllowedEmit extends string = string> = {
@@ -153,8 +93,6 @@ type TransitionResult<AllowedState extends AnyStateRef = AnyStateRef, AllowedEmi
153
93
  payload?: unknown;
154
94
  }>;
155
95
  };
156
- //#endregion
157
- //#region src/actor-internal.d.ts
158
96
  interface Snapshot<C = unknown> {
159
97
  path: string[];
160
98
  context: C;
@@ -163,17 +101,17 @@ interface Snapshot<C = unknown> {
163
101
  done?: boolean;
164
102
  error?: ErrorInfo;
165
103
  }
104
+ type SubscriberEventName = "change" | "done" | "transition" | "error" | "output";
105
+ type SubscriberHandler<C, E extends SubscriberEventName> = E extends "change" ? (snapshot: Snapshot<C>, prev: Snapshot<C>) => void : E extends "done" ? () => void : E extends "transition" ? (info: TransitionInfo) => void : E extends "error" ? (info: ErrorInfo) => void : E extends "output" ? (event: InternalEvent) => void : never;
166
106
  interface AnyActor<C = Record<string, unknown>> {
167
107
  state: AnyStateRef;
168
108
  clock: Clock;
169
109
  regions: Record<string, AnyActor>;
170
- send(event: AnyEventRef | InternalEvent): void;
110
+ send(event: InternalEvent): void;
171
111
  snapshot(): Snapshot<C>;
172
- on(event: "change", fn: (snapshot: Snapshot<C>, prev: Snapshot<C>) => void): () => void;
173
- on(event: "done", fn: () => void): () => void;
174
- on(event: "transition", fn: (info: TransitionInfo) => void): () => void;
175
- on(event: "error", fn: (info: ErrorInfo) => void): () => void;
176
- on(event: "output", fn: (event: InternalEvent) => void): () => void;
112
+ on<E extends SubscriberEventName>(event: E, options: {
113
+ fn: SubscriberHandler<C, E>;
114
+ }): () => void;
177
115
  recover(target: {
178
116
  state: AnyStateRef;
179
117
  context: C;
@@ -182,6 +120,7 @@ interface AnyActor<C = Record<string, unknown>> {
182
120
  context?: C;
183
121
  options?: {
184
122
  transitions?: Record<string, Record<string, unknown>>;
123
+ targets?: Record<string, Record<string, string[]>>;
185
124
  effects?: Record<string, unknown[]>;
186
125
  states?: ReadonlyArray<{
187
126
  name: string;
@@ -191,36 +130,91 @@ interface AnyActor<C = Record<string, unknown>> {
191
130
  inject(event: InternalEvent): void;
192
131
  dispose(): void;
193
132
  }
194
- //#endregion
195
- //#region src/builder.d.ts
196
- type EventTypeOf<E extends AnyEventRef> = E extends EventRef<infer Type, object | void> ? Type : never;
197
- type PayloadOf<S extends AnyStateRef> = S extends StateRef<infer _Name, infer Payload, infer _IsFinal> ? Payload : never;
198
- type TransitionHandler<States extends readonly AnyStateRef[], ActorContext> = (event: InternalEvent, opts: {
133
+ type RuntimeTransitionHandler<States extends readonly AnyStateRef[], ActorContext> = (event: InternalEvent, options: {
199
134
  context: Context<ActorContext>;
200
- actor: AnyActor;
135
+ actor: AnyActor<ActorContext>;
201
136
  }) => TransitionResult<States[number], string>;
137
+ type EventTypeOf<E extends AnyEventRef> = E extends EventRef<infer Type, object | void> ? Type : never;
138
+ type PayloadOf<S extends AnyStateRef> = S extends StateRef<infer _Name, infer Payload, infer _IsFinal> ? Payload : never;
139
+ type TransitionHandler<States extends readonly AnyStateRef[], ActorContext> = RuntimeTransitionHandler<States, ActorContext>;
140
+ interface EffectEntry<ActorContext> {
141
+ name: string;
142
+ sends?: string[];
143
+ fn: EffectFn<ActorContext>;
144
+ }
145
+ type EmitOf<E> = E extends readonly AnyEventRef[] ? CreatedOf<E[number]> : InternalEvent;
146
+ type SendList<I extends readonly AnyEventRef[]> = readonly I[number][] | undefined;
202
147
  interface BuiltMaps<States extends readonly AnyStateRef[], ActorContext> {
203
148
  transitions: Record<string, Record<string, TransitionHandler<States, ActorContext>>>;
204
- effects: Record<string, Array<EffectFn<ActorContext>>>;
149
+ /** Declared `to` state names per `[state][event]`; `Any` keys `onAny`. Static graph for tooling. */
150
+ targets: Record<string, Record<string, string[]>>;
151
+ effects: Record<string, Array<EffectEntry<ActorContext>>>;
152
+ }
153
+ /** Handler may only return states listed in `to`; no `to` means it can only stay. */
154
+ type HandlerFn<To extends AnyStateRef, Outputs extends readonly AnyEventRef[], ActorContext, E extends AnyEventRef> = (event: E extends EventRef<infer Type, infer P> ? CreatedOfEvent<Type, P> : never, opts: {
155
+ context: Context<ActorContext>;
156
+ actor: AnyActor<ActorContext>;
157
+ }) => TransitionResult<To, EventTypeOf<Outputs[number]>>;
158
+ interface HandlerOptions<To extends readonly AnyStateRef[], Outputs extends readonly AnyEventRef[], ActorContext, E extends AnyEventRef> {
159
+ event: E;
160
+ to?: To;
161
+ handler: HandlerFn<NoInfer<To[number]>, Outputs, ActorContext, E>;
205
162
  }
206
- declare class ActorBuilder<States extends readonly AnyStateRef[], Inputs extends readonly AnyEventRef[], Internal extends readonly AnyEventRef[], Outputs extends readonly AnyEventRef[], ActorContext> {
207
- #private;
208
- on<S extends States[number], E extends Inputs[number] | Internal[number]>(stateRef: S, eventRef: E, fn: (event: E extends EventRef<infer Type, infer P> ? CreatedOfEvent<Type, P> : never, opts: {
209
- context: Context<ActorContext>;
210
- actor: AnyActor;
211
- }) => TransitionResult<States[number], EventTypeOf<Outputs[number]>>): this;
212
- onAny<E extends Inputs[number] | Internal[number]>(eventRef: E, fn: (event: E extends EventRef<infer Type, infer P> ? CreatedOfEvent<Type, P> : never, opts: {
213
- context: Context<ActorContext>;
214
- actor: AnyActor;
215
- }) => TransitionResult<States[number], EventTypeOf<Outputs[number]>>): this;
216
- effect<S extends States[number]>(stateRef: S, fn: EffectFn<ActorContext, PayloadOf<S>>): this;
217
- /** @internal */ build(): BuiltMaps<States, ActorContext>;
163
+ interface ActorBuilder<States extends readonly AnyStateRef[], Inputs extends readonly AnyEventRef[], Internal extends readonly AnyEventRef[], Outputs extends readonly AnyEventRef[], ActorContext> {
164
+ on<S extends States[number], E extends Inputs[number] | Internal[number], const To extends readonly States[number][] = readonly []>(stateRef: S, options: HandlerOptions<To, Outputs, ActorContext, E>): ActorBuilder<States, Inputs, Internal, Outputs, ActorContext>;
165
+ onAny<E extends Inputs[number] | Internal[number], const To extends readonly States[number][] = readonly []>(options: HandlerOptions<To, Outputs, ActorContext, E>): ActorBuilder<States, Inputs, Internal, Outputs, ActorContext>;
166
+ /** `sends`: internal events `fn` may emit; `emit` only accepts those. Omit for untyped `emit`. */
167
+ effect<S extends States[number], const E extends SendList<Internal> = undefined>(stateRef: S, definition: {
168
+ name: string;
169
+ sends?: E;
170
+ fn: EffectFn<ActorContext, PayloadOf<S>, EmitOf<E>>;
171
+ }): ActorBuilder<States, Inputs, Internal, Outputs, ActorContext>;
172
+ /** @internal */
173
+ build(): BuiltMaps<States, ActorContext>;
218
174
  }
175
+ declare function ActorBuilder<States extends readonly AnyStateRef[], Inputs extends readonly AnyEventRef[], Internal extends readonly AnyEventRef[], Outputs extends readonly AnyEventRef[], ActorContext>(): ActorBuilder<States, Inputs, Internal, Outputs, ActorContext>;
219
176
  type SetupFn<States extends readonly AnyStateRef[], Inputs extends readonly AnyEventRef[], Internal extends readonly AnyEventRef[], Outputs extends readonly AnyEventRef[], ActorContext> = (m: ActorBuilder<States, Inputs, Internal, Outputs, ActorContext>) => void;
220
- //#endregion
221
- //#region src/actor.d.ts
222
- type CreatedOf$1<E extends AnyEventRef> = E extends EventRef<infer Type, infer P> ? CreatedOfEvent<Type, P> : never;
223
- type InitialState<S extends AnyStateRef> = S extends StateRef<infer _N extends string, infer P> ? [unknown] extends [P] ? S | {
177
+ export declare function event<const T extends string>(type: T): <Payload extends object | void = void>() => EventRef<T, Payload>;
178
+ export interface EventRef<T extends string = string, Payload extends object | void = void> {
179
+ readonly type: T;
180
+ is(anyEvent: unknown): anyEvent is CreatedOfEvent<T, Payload>;
181
+ create(): Payload extends void ? {
182
+ type: T;
183
+ } : void;
184
+ create(payload: Payload): Payload extends void ? {
185
+ type: T;
186
+ } : {
187
+ type: T;
188
+ payload: Payload;
189
+ };
190
+ }
191
+ type AnyEventRef = EventRef<string, object | void>;
192
+ type CreatedOfEvent<T extends string, P> = P extends void ? {
193
+ type: T;
194
+ } : {
195
+ type: T;
196
+ payload: P;
197
+ };
198
+ export declare function EventRef<const T extends string, Payload extends object | void = void>(type: T): EventRef<T, Payload>;
199
+ export declare function RealClock(): Clock;
200
+ export interface VirtualClock {
201
+ now(): number;
202
+ setTimeout(ms: number, options: ClockTimerOptions): number;
203
+ clearTimeout(id: number): void;
204
+ setInterval(ms: number, options: ClockIntervalOptions): number;
205
+ clearInterval(id: number): void;
206
+ advance(ms: number): void;
207
+ hasPending(): boolean;
208
+ pendingTimers(): Array<{
209
+ id: number;
210
+ deadline: number;
211
+ ms: number;
212
+ eventName?: string;
213
+ }>;
214
+ setDrain(fn: () => void): void;
215
+ }
216
+ export declare function VirtualClock(): VirtualClock;
217
+ type InitialState<S extends AnyStateRef> = S extends StateRef<infer _N extends string, infer P, infer _I extends boolean> ? [unknown] extends [P] ? S | {
224
218
  state: S;
225
219
  payload?: P;
226
220
  } : {
@@ -241,40 +235,37 @@ interface ActorOptions<States extends readonly AnyStateRef[], Inputs extends rea
241
235
  }
242
236
  interface InternalActorOptions<States extends readonly AnyStateRef[], Inputs extends readonly AnyEventRef[], Internal extends readonly AnyEventRef[], Outputs extends readonly AnyEventRef[], ActorContext = Record<string, unknown>> extends Omit<ActorOptions<States, Inputs, Internal, Outputs, ActorContext>, "setup"> {
243
237
  transitions: TransitionDispatch<States, ActorContext>;
244
- effects: Record<string, Array<EffectFn<ActorContext>>>;
238
+ targets: Record<string, Record<string, string[]>>;
239
+ effects: Record<string, Array<EffectEntry<ActorContext>>>;
245
240
  }
246
- type TransitionDispatch<States extends readonly AnyStateRef[], ActorContext> = Record<string, Record<string, ((event: InternalEvent, options: {
241
+ type TransitionDispatch<States extends readonly AnyStateRef[], ActorContext> = Record<string, Record<string, TransitionStepFn<States, ActorContext> | undefined>>;
242
+ type TransitionStepFn<States extends readonly AnyStateRef[], ActorContext> = (event: InternalEvent, options: {
247
243
  context: Context<ActorContext>;
248
- actor: AnyActor;
249
- }) => TransitionResult<States[number], string>) | undefined>>;
250
- declare class Actor<const States extends readonly AnyStateRef[], const Inputs extends readonly AnyEventRef[], const Internal extends readonly AnyEventRef[] = readonly [], const Outputs extends readonly AnyEventRef[] = readonly [], ActorContext = Record<string, unknown>> {
251
- #private;
244
+ actor: AnyActor<ActorContext>;
245
+ }) => TransitionResult<States[number], string>;
246
+ export interface Actor<States extends readonly AnyStateRef[], Inputs extends readonly AnyEventRef[], Internal extends readonly AnyEventRef[] = readonly [], Outputs extends readonly AnyEventRef[] = readonly [], ActorContext = Record<string, unknown>> extends AnyActor<ActorContext> {
252
247
  state: States[number] | ErrorState;
253
248
  readonly clock: Clock;
254
- get context(): ActorContext;
255
- get regions(): Record<string, AnyActor>;
256
- get options(): InternalActorOptions<States, Inputs, Internal, Outputs, ActorContext>;
257
- constructor(options: ActorOptions<States, Inputs, Internal, Outputs, ActorContext>);
258
- on(event: "change", fn: (snapshot: Snapshot<ActorContext>, prev: Snapshot<ActorContext>) => void): () => void;
259
- on(event: "done", fn: () => void): () => void;
260
- on(event: "transition", fn: (info: TransitionInfo) => void): () => void;
261
- on(event: "error", fn: (info: ErrorInfo) => void): () => void;
262
- on(event: "output", fn: (event: InternalEvent) => void): () => void;
249
+ readonly context: ActorContext;
250
+ readonly regions: Record<string, AnyActor>;
251
+ readonly options: InternalActorOptions<States, Inputs, Internal, Outputs, ActorContext>;
252
+ on<E extends SubscriberEventName>(event: E, options: {
253
+ fn: SubscriberHandler<ActorContext, E>;
254
+ }): () => void;
263
255
  settled(): Promise<void>;
264
- send(event: CreatedOf$1<Inputs[number]>): void;
265
- /**
266
- * Restore a live state after `__error`. DANGER: caller supplies state+context,
267
- * breaking determinism. Effects not re-run; processing resumes on next event.
268
- */
256
+ pendingEffectCount(): number;
257
+ send(event: CreatedOf<Inputs[number]>): void;
269
258
  recover(target: {
270
259
  state: States[number];
271
260
  context: ActorContext;
272
261
  }): void;
273
262
  snapshot(): Snapshot<ActorContext>;
274
- /** Push an internal event and drain. Unknown types route to output subscribers. */
275
263
  inject(event: InternalEvent): void;
276
- /** Stop for good: abort effect, clear queue and subscribers. Later sends/injects ignored. */
277
264
  dispose(): void;
278
265
  }
279
- //#endregion
280
- export { Actor, type ActorBuilder, type ActorOptions, type AnyActor, type AnyEventRef, type AnyStateRef, type BuiltMaps, type Clock, Context, type CreatedOf, type CreatedOfEvent, type EffectFn, type EffectInput, type ErrorInfo, type ErrorReason, type ErrorState, EventRef, type EventTypeOf, type InitialState, type InternalEvent, type NonFinalStateRef, type PayloadOf, RealClock, type SetupFn, type Snapshot, StateRef, type TransitionHandler, type TransitionInfo, type TransitionResult, VirtualClock, event, state };
266
+ export declare function Actor<const States extends readonly AnyStateRef[], const Inputs extends readonly AnyEventRef[], const Internal extends readonly AnyEventRef[] = readonly [], const Outputs extends readonly AnyEventRef[] = readonly [], ActorContext = Record<string, unknown>>(options: ActorOptions<States, Inputs, Internal, Outputs, ActorContext>): Actor<States, Inputs, Internal, Outputs, ActorContext>;
267
+ export type InternalEvent = {
268
+ type: string;
269
+ payload?: unknown;
270
+ };
271
+ export type { ActorBuilder, ActorOptions, AnyActor, AnyEventRef, AnyStateRef, BuiltMaps, Clock, CreatedOf, CreatedOfEvent, EffectFn, EffectInput, ErrorInfo, ErrorReason, ErrorState, EventTypeOf, InitialState, NonFinalStateRef, PayloadOf, SetupFn, Snapshot, TransitionHandler, TransitionInfo, TransitionResult };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{Either as e}from"@mantaq/utils";function t(e){return()=>new n(e,!1)}var n=class e{name;isFinal;_regions;constructor(e,t){this.name=e,this.isFinal=t}regions(e){return this._regions=e,this}final(){let t=new e(this.name,!0);return t._regions=this._regions,t}create(e){return{state:this,payload:e}}},r=class{#e=Date.now();now(){return Date.now()-this.#e}setTimeout(e,t,n){if(n?.signal?.aborted)return-1;let r,i=Number(globalThis.setTimeout(()=>{r&&n?.signal&&n.signal.removeEventListener(`abort`,r),t()},e));return n?.signal&&(r=()=>globalThis.clearTimeout(i),n.signal.addEventListener(`abort`,r,{once:!0})),i}clearTimeout(e){globalThis.clearTimeout(e)}setInterval(e,t,n){let r=Number(globalThis.setInterval(t,e));return n?.signal&&n.signal.addEventListener(`abort`,()=>globalThis.clearInterval(r),{once:!0}),r}clearInterval(e){globalThis.clearInterval(e)}};function i(e,t,n){let r=e?()=>{n.delete(t)}:void 0;return e&&r&&e.addEventListener(`abort`,r,{once:!0}),r}function a(e){e.signal&&e.onAbort&&e.signal.removeEventListener(`abort`,e.onAbort)}var o=class{#e=0;#t=new Map;#n=new Map;#r=1;#i=null;now(){return this.#e}#a(e,t,n){if(!Number.isFinite(e))throw RangeError(`[VirtualClock] invalid ${t} ms value: ${e}`);return e<=0?+!!n:e}setTimeout(e,t,n){let r=n?.signal;if(r?.aborted)return-1;let a=this.#r++,o=i(r,a,this.#t);return this.#t.set(a,{deadline:this.#e+this.#a(e,`setTimeout`,!1),cb:t,signal:r,onAbort:o,eventName:n?.eventName}),a}clearTimeout(e){let t=this.#t.get(e);t&&(a(t),this.#t.delete(e))}setInterval(e,t,n){let r=n?.signal;if(r?.aborted)return-1;let a=this.#r++,o=i(r,a,this.#n),s=this.#a(e,`setInterval`,!0);return this.#n.set(a,{ms:s,next:this.#e+s,cb:t,signal:r,onAbort:o}),a}clearInterval(e){let t=this.#n.get(e);t&&(a(t),this.#n.delete(e))}#o(e){let t=e,n=!1;for(let r of this.#t.values())r.deadline<=e&&r.deadline<=t&&(t=r.deadline,n=!0);for(let r of this.#n.values())r.next<=e&&r.next<=t&&(t=r.next,n=!0);return n?t:null}#s(e){let t=[];for(let[n,r]of this.#t)r.deadline===e&&t.push(n);for(let e of t){let t=this.#t.get(e);t&&(a(t),this.#t.delete(e),t.cb())}}#c(e){let t=[];for(let[n,r]of this.#n)r.next===e&&t.push(n);for(let e of t){let t=this.#n.get(e);t&&(t.cb(),this.#n.has(e)&&(t.next=this.#e+t.ms))}}advance(e){if(!Number.isFinite(e))throw RangeError(`[VirtualClock] invalid advance ms value: ${e}`);let t=this.#e+Math.max(0,e);for(;;){let e=this.#o(t);if(e===null)break;this.#e=e,this.#s(e),this.#c(e)}this.#e=t,this.#i?.()}hasPending(){return this.#t.size>0||this.#n.size>0}pendingTimers(){let e=[];for(let[t,n]of this.#t)e.push({id:t,deadline:n.deadline,ms:n.deadline-this.#e,eventName:n.eventName});return e}setDrain(e){this.#i=e}},s=class{#e=[];#t=0;#n=!1;#r=!1;#i=[];get length(){return this.#e.length-this.#t}push(...e){this.#r||this.#e.push(...e)}clear(){let e=this.#i.splice(0);this.#e.length=0,this.#t=0;for(let t of e)t()}settled(){return this.length===0&&!this.#n?Promise.resolve():new Promise(e=>{this.#i.push(e)})}processCancellable(e){this.#a(e)}#a(e){if(!this.#n){this.#n=!0;try{for(;this.#t<this.#e.length&&!this.#r;){let t=this.#e[this.#t++];if(!e(t)){this.#r=!0;break}}}finally{this.#e.length=0,this.#t=0,this.#n=!1,this.#r=!1;let e=this.#i.splice(0);for(let t of e)t()}}}},c=class{change=new Set;done=new Set;transition=new Set;error=new Set;#e=null;seed(e){this.#e=e}addChange(e){this.change.add(e);let t=this.#e;return t&&this.#t(()=>e(t,t)),()=>this.change.delete(e)}addDone(e){return this.done.add(e),()=>this.done.delete(e)}addTransition(e){return this.transition.add(e),()=>this.transition.delete(e)}addError(e){this.error.add(e);let t=this.#e?.error;return t&&this.#t(()=>e(t)),()=>this.error.delete(e)}emitChange(e){let t=this.#e??e;this.#e=e;for(let n of this.change)this.#t(()=>n(e,t))}emitDone(){for(let e of this.done)this.#t(e)}emitTransition(e){for(let t of this.transition)this.#t(()=>t(e))}emitError(e){for(let t of this.error)this.#t(()=>t(e))}clear(){this.change.clear(),this.done.clear(),this.transition.clear(),this.error.clear(),this.output.clear()}#t(t){e.from(t)}output=new Set;addOutput(e){return this.output.add(e),()=>this.output.delete(e)}emitOutput(e){for(let t of this.output)t(e)}};function l(e,t,n,r={}){let i=[e.name],a={};for(let[e,n]of Object.entries(t))a[e]=n.snapshot();let o={path:i,context:n,regions:a};return r.payload!==void 0&&(o.payload=r.payload),e.isFinal&&(o.done=!0),r.error!==void 0&&(o.error=r.error),o}function u(t){let n=[],r=t.effects[t.state.name];if(!r)return{pending:n};for(let i of r){if(t.abort.signal.aborted)break;let r=e.from(()=>i({signal:t.abort.signal,state:{name:t.state.name,payload:t.statePayload},event:t.event,context:t.context,emit:t.emit,clock:t.clock}));if(r[0]!==void 0){t.onError(r[0],t.lastGood);break}let a=r[1];a instanceof Promise&&n.push(a.catch(e=>{t.abort.signal.aborted||t.onError(e,t.lastGood)}))}return{pending:n}}function d(e){if(e.state)return e.state instanceof n?{state:e.state,payload:e.payload}:{state:e.state.state,payload:e.state.payload}}var f=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}get(){return this.#e()}set(e){this.#t(e)}},p=class{#e={};#t={};on(e,t,n){let r=e.name,i=t.type;return(this.#e[r]??={})[i]=n,this}onAny(e,t){let n=e.type;return(this.#e.Any??={})[n]=t,this}effect(e,t){return(this.#t[e.name]??=[]).push(t),this}build(){return{transitions:this.#e,effects:this.#t}}};function m(e){return e instanceof n?e:e.state}var h=class{state;clock;#e;#t;#n=!1;#r;#i;#a={};#o=new s;#s=new c;#c=null;#l;#u;#d;#f=!1;get context(){return this.#e}get regions(){return this.#a}get options(){return this.#i}constructor(e){let t=new p;e.setup(t);let i=t.build();this.#i={...e,transitions:i.transitions,effects:i.effects},this.#l=new Set((e.internal??[]).map(e=>e.type)),this.#u=new Set(e.inputs.map(e=>e.type)),this.#d=e.internalBudget??1e4,this.clock=e.clock??new r,this.clock.setDrain?.(()=>this.#x());let a=m(e.initial),o=new Set(e.states.map(e=>e.name));if(!o.has(a.name))throw Error(`[Actor] initial state "${a.name}" not found in declared states [${[...o].join(`, `)}]`);if(this.state=a,this.#S=e.initial instanceof n?void 0:e.initial.payload,this.#e=e.context??{},this.#r=a,this.#t=new f(()=>this.#e,e=>{this.#e=e,this.#n=!0}),e.regions)for(let[t,n]of Object.entries(e.regions))this.#a[t]=n,n.on(`output`,e=>{this.#o.push(e),this.#x()});this.#b({type:`__init`},this.#S),this.#s.seed(this.snapshot())}on(e,t){return{change:e=>this.#s.addChange(e),done:e=>this.#s.addDone(e),transition:e=>this.#s.addTransition(e),error:e=>this.#s.addError(e),output:e=>this.#s.addOutput(e)}[e](t)}settled(){return Promise.all([this.#o.settled(),...this.#C]).then(()=>void 0)}send(e){this.#k||(this.#m(e),this.#p())}recover(e){this.#T===null||this.#k||(this.#T=null,this.#o=new s,this.#c=null,this.state=e.state,this.#S=void 0,this.#e=e.context,this.#r=e.state,this.#n=!1,this.#E=null,this.#C=[],this.#s.emitChange(this.snapshot()))}#p(){this.state===this.#r&&!this.#n||(this.#r=this.state,this.#n=!1,this.#s.emitChange(this.snapshot()))}#m(e){if(this.#T!==null||this.state.isFinal)return;let t={...e,payload:e.payload??{}},n=this.state.name,r=this.#E;this.#E={state:this.state,context:this.#e};try{let r=this.#i.transitions,i=r[this.state.name]?.[e.type],a=r.Any?.[e.type],o=this.#h(t,i,a);this.#g(t,n,!!(i||a),o),o.emitted||this.#o.length>0?this.#x():!i&&!a&&this.#l.has(e.type)&&this.#O(`unhandled`,e,Error(`[Actor] internal event "${e.type}" emitted but no handler in state "${this.state.name}"`))}finally{this.#E=r}}#h(e,t,n){let r=t?this.#_(e,t):{applied:!1};if(this.#T===null){let t=this.#v(e,n,!r.applied);return{emitted:t.emitted,transitioned:r.applied||t.transitioned,target:r.target??t.target}}return{emitted:!1,transitioned:!1}}#g(e,t,n,r){this.#T===null&&n&&this.#s.emitTransition({event:e,from:t,to:r.target??this.state.name,transitioned:r.transitioned})}#_(e,t){let n=!1,r;return this.#D(`transition`,e,()=>{let i=t(e,{context:this.#t,actor:this});i.emit&&this.#o.push(...i.emit),i.state&&(r=this.#y(e,i),n=!0)}),{applied:n,target:r}}#v(e,t,n){if(t===void 0)return{emitted:!1,transitioned:!1};let r=!1,i=!1,a;return this.#D(`transition`,e,()=>{let o=t(e,{context:this.#t,actor:this});o.state&&n&&(a=this.#y(e,o),i=!0),o.emit&&(this.#o.push(...o.emit),r=!0)}),{emitted:r,transitioned:i,target:a}}snapshot(){return l(this.state,this.#a,this.#e,{error:this.#T??void 0,payload:this.#S})}#y(e,t){if(this.#T!==null||!t.state)return;let r=d(t);if(!r)return;if(!(r.state instanceof n)){this.#O(`transition`,e,Error(`invalid transition target`));return}this.#c?.abort(),this.state=r.state;let i=this.state.name;return this.#S=r.payload,this.#E={state:this.state,context:this.#e},this.#b(e,r.payload),r.state.isFinal&&this.#T===null&&this.#s.emitDone(),i}#b(e,t){if(this.#k)return;let n=this.#i.effects[this.state.name];if(!n){this.#c=null;return}let r=new AbortController;this.#c=r;let i=this.#E??{state:this.state,context:this.#e},a=u({effects:{[this.state.name]:n},state:this.state,statePayload:t,event:e,context:this.#t,emit:e=>{this.#T!==null||r.signal.aborted||(this.#o.push(e),this.#x())},clock:this.clock,abort:r,lastGood:i,onError:t=>this.#O(`effect`,e,t,i)});this.#C.push(...a.pending)}#x(){if(!(this.#f||this.#k)){this.#f=!0;try{let e=this.#d,t=0;this.#o.processCancellable(n=>{if(this.#T!==null)return!1;if(t>=e)return this.#O(`budget`,n,Error(`internal event budget exceeded`)),!1;if(t++,this.#l.has(n.type)||this.#u.has(n.type))this.#m(n);else if(!this.#D(`output`,n,()=>this.#s.emitOutput(n)))return!1;return!0})}finally{this.#f=!1,this.#p()}}}#S;#C=[];#w=new n(`__error`,!0);#T=null;#E=null;#D(t,n,r){let i=e.from(r);return i[0]===void 0||(this.#O(t,n,i[0]),!1)}#O(e,t,n,r){if(this.#T!==null)return;let i=r??this.#E??{state:this.state,context:this.#e};this.#T={error:n,state:i.state,context:i.context,event:t,reason:e},this.#c?.abort(),this.state=this.#w,this.#S=void 0,this.#r=this.#w,this.#n=!1,this.#s.emitError(this.#T),this.#s.emitChange(this.snapshot()),this.#s.emitDone()}inject(e){this.#k||(this.#o.push(e),this.#x())}dispose(){this.#k||(this.#k=!0,this.#c?.abort(),this.#c=null,this.#o.clear(),this.#s.clear())}#k=!1};function g(e){return()=>new _(e)}var _=class{type;constructor(e){this.type=e}is(e){return!!e&&typeof e==`object`&&`type`in e&&e.type===this.type}create(e){return e===void 0?{type:this.type}:{type:this.type,payload:e}}};export{h as Actor,f as Context,_ as EventRef,r as RealClock,n as StateRef,o as VirtualClock,g as event,t as state};
1
+ import{Either as e}from"@mantaq/utils";function t(e){let t=e;return typeof t==`function`?t:()=>({})}function n(e){return e}function r(){let e={},r={},i={};function a(n,i){let a=i.event.type;(e[n]??={})[a]=t(i.handler),(r[n]??={})[a]=(i.to??[]).map(e=>e.name)}let o={on(e,t){return a(e.name,t),o},onAny(e){return a(`Any`,e),o},effect(e,{name:t,sends:r,fn:a}){return(i[e.name]??=[]).push({name:t,...r&&{sends:r.map(e=>e.type)},fn:n(a)}),o},build(){return{transitions:e,targets:r,effects:i}}};return o}function i(e){return{get:e.get,set:e.set}}function a(e){return()=>c(e,{isFinal:!1})}const o=/* @__PURE__ */ new WeakSet;function s(e){return typeof e==`object`&&!!e&&o.has(e)}function c(e,t){let n={name:e,isFinal:t.isFinal,regions(e){return n._regions=e,n},final(){let e=c(n.name,{isFinal:!0});return e._regions=n._regions,e},create(e){return{state:n,payload:e}}};return o.add(n),n}function l(e){let t=e.state;if(t)return`state`in t?{state:t.state,payload:t.payload}:{state:t,payload:e.payload}}function u(e){if(e instanceof Promise)return!0;let t=e;return typeof t==`object`&&!!t&&typeof t.then==`function`}function ee(t){let n=[],r=[],i=t.effects[t.state.name];if(!i)return{pending:n,ran:r};for(let{name:a,fn:o}of i){if(t.abort.signal.aborted)break;let i,s=e.from(()=>(i=o({signal:t.abort.signal,state:{name:t.state.name,payload:t.statePayload},event:t.event,context:t.context,emit:t.emit,clock:t.clock}),!0));if(s[0]!==void 0){t.onError(s[0],t.lastGood);break}if(r.push(a),u(i)){let e=Promise.resolve(i).then(()=>void 0,e=>{t.abort.signal.aborted||t.onError(e,t.lastGood)});n.push(e)}}return{pending:n,ran:r}}function d(e){return e??{}}function f(){let e=[],t=0,n=!1,r=!1,i=[];function a(){let e=i.splice(0);for(let t of e)t()}function o(i){if(!n){n=!0;try{for(;t<e.length&&!r;){let n=e[t];if(t++,!i(n)){r=!0;break}}}finally{e.length=0,t=0,n=!1,r=!1,a()}}}return{get length(){return e.length-t},push(...t){r||e.push(...t)},clear(){e.length=0,t=0,a()},settled(){return e.length-t===0&&!n?Promise.resolve():new Promise(e=>{i.push(e)})},processCancellable(e){o(e)}}}function p(){let e=Date.now(),t=/* @__PURE__ */ new Map;function n(e){let n=t.get(e);n&&(n.signal.removeEventListener(`abort`,n.onAbort),t.delete(e))}return{now(){return Date.now()-e},setTimeout(e,{signal:r,cb:i}){if(r?.aborted)return-1;let a=Number(globalThis.setTimeout(()=>{n(a),i()},e));if(r){function e(){globalThis.clearTimeout(a),n(a)}t.set(a,{signal:r,onAbort:e}),r.addEventListener(`abort`,e,{once:!0})}return a},clearTimeout(e){globalThis.clearTimeout(e),n(e)},setInterval(e,{signal:r,cb:i}){if(r?.aborted)return-1;let a=Number(globalThis.setInterval(i,e));if(r){function e(){globalThis.clearInterval(a),n(a)}t.set(a,{signal:r,onAbort:e}),r.addEventListener(`abort`,e,{once:!0})}return a},clearInterval(e){globalThis.clearInterval(e),n(e)}}}function te(e){let{stateRef:t,regions:n,context:r}=e,i=[t.name],a={};for(let[e,t]of Object.entries(n))a[e]=t.snapshot();let o={path:i,context:r,regions:a};return e.payload!==void 0&&(o.payload=e.payload),t.isFinal&&(o.done=!0),e.error!==void 0&&(o.error={...e.error,context:m(e.error.context)}),o}function m(e){return typeof structuredClone==`function`?structuredClone(e):JSON.parse(JSON.stringify(e))}function h(){let t=/* @__PURE__ */ new Set,n=/* @__PURE__ */ new Set,r=/* @__PURE__ */ new Set,i=/* @__PURE__ */ new Set,a=/* @__PURE__ */ new Set,o;function s(t){e.from(()=>(t(),!0))}return{change:t,done:n,transition:r,error:i,output:a,seed(e){o=e},addChange(e){t.add(e);let n=o;return n&&s(()=>e(n,n)),()=>t.delete(e)},addDone(e){return n.add(e),()=>n.delete(e)},addTransition(e){return r.add(e),()=>r.delete(e)},addError(e){i.add(e);let t=o?.error;return t&&s(()=>e(t)),()=>i.delete(e)},addOutput(e){return a.add(e),()=>a.delete(e)},emitChange(e){let n=o??e;o=e;for(let r of t)s(()=>r(e,n))},emitDone(){for(let e of n)s(e)},emitTransition(e){for(let t of r)s(()=>t(e))},emitError(e){for(let t of i)s(()=>t(e))},emitOutput(e){for(let t of a)t(e)},clear(){t.clear(),n.clear(),r.clear(),i.clear(),a.clear()}}}function g(e,t){let n=e?()=>{t.entries.delete(t.timerId)}:void 0;return e&&n&&e.addEventListener(`abort`,n,{once:!0}),n}function _(e){e.signal&&e.onAbort&&e.signal.removeEventListener(`abort`,e.onAbort)}function v(){let e=0,t=/* @__PURE__ */ new Map,n=/* @__PURE__ */ new Map,r=1,i=/* @__PURE__ */ new Set;function a(e,t){if(!Number.isFinite(e))throw RangeError(`[VirtualClock] invalid ${t.method} ms value: ${e}`);return!0}function o(e,t){return a(e,{method:t.method}),e<=0?+!!t.interval:e}function s(e){let r=e,i=!1;for(let n of t.values())n.deadline<=e&&n.deadline<=r&&(r=n.deadline,i=!0);for(let t of n.values())t.next<=e&&t.next<=r&&(r=t.next,i=!0);return i?r:void 0}function c(e){let n=[];for(let[r,i]of t)i.deadline===e&&n.push(r);for(let e of n){let n=t.get(e);n&&(_(n),t.delete(e),n.cb())}}function l(t){let r=[];for(let[e,i]of n)i.next===t&&r.push(e);for(let t of r){let r=n.get(t);r&&(r.cb(),n.has(t)&&(r.next=e+r.ms))}}return{now(){return e},setTimeout(n,{signal:i,cb:a,eventName:s}){if(i?.aborted)return-1;let c=r++,l=g(i,{timerId:c,entries:t});return t.set(c,{deadline:e+o(n,{method:`setTimeout`,interval:!1}),cb:a,signal:i,onAbort:l,eventName:s}),c},clearTimeout(e){let n=t.get(e);n&&(_(n),t.delete(e))},setInterval(t,{signal:i,cb:a}){if(i?.aborted)return-1;let s=r++,c=g(i,{timerId:s,entries:n}),l=o(t,{method:`setInterval`,interval:!0});return n.set(s,{ms:l,next:e+l,cb:a,signal:i,onAbort:c}),s},clearInterval(e){let t=n.get(e);t&&(_(t),n.delete(e))},advance(t){a(t,{method:`advance`});let n=e+Math.max(0,t),r=-1,o=0;for(;;){let t=s(n);if(t===void 0)break;if(t===r){if(++o>=1e6)break}else o=0,r=t;e=t,c(t),l(t)}e=n;for(let e of i)e()},hasPending(){return t.size>0||n.size>0},pendingTimers(){let n=[];for(let[r,i]of t)n.push({id:r,deadline:i.deadline,ms:i.deadline-e,eventName:i.eventName});return n},setDrain(e){i.add(e)}}}function y(e){return`state`in e?e.state:e}function b(e,t){if(!e.has(t.candidate.name))throw Error(`[Actor] initial state "${t.candidate.name}" not found in declared states [${[...e].join(`, `)}]`);return!0}function x(t){let n=r();t.setup(n);let a=n.build(),o={...t,transitions:a.transitions,targets:a.targets,effects:a.effects},u=new Set((t.internal??[]).map(e=>e.type)),g=new Set(t.inputs.map(e=>e.type)),_=t.internalBudget??1e4,v=t.clock??p();v.setDrain?.(Y);let x,S,C=d(t.context),w,T=!0,E,D={},O=f(),k=h(),A,j=!1,M=[],N,P,F=!1,I=!1,L=c(`__error`,{isFinal:!0}),R=i({get:()=>C,set:e=>{C=e,I=!0,T=!0}}),z=y(t.initial);b(new Set(t.states.map(e=>e.name)),{candidate:z}),x=z;let B=t.initial;if(S=!s(B)&&`payload`in B?B.payload:void 0,E=z,t.regions)for(let[e,n]of Object.entries(t.regions))D[e]=n,n.on(`output`,{fn:e=>{O.push(e),Y()}});let V={get state(){return x},clock:v,get regions(){return D},get context(){return C},get options(){return o},on(e,t){return{change:e=>k.addChange(e),done:e=>k.addDone(e),transition:e=>k.addTransition(e),error:e=>k.addError(e),output:e=>k.addOutput(e)}[e](t.fn)},settled(){return Q()},pendingEffectCount(){return M.length},send(e){F||(W(e),H())},recover:U,snapshot(){return K()},inject(e){ae(e)},dispose(){oe()}};J({type:`__init`},{payloadForState:S}),k.seed(K());function H(){(x!==E||I)&&(E=x,I=!1,k.emitChange(K()))}function U(e){N===void 0||F||(A?.abort(),A=void 0,O.clear(),O=f(),N=void 0,x=e.state,S=void 0,C=e.context,E=e.state,I=!1,T=!0,w=void 0,P=void 0,M=[],k.emitChange(K()))}function W(e){if(N!==void 0||x.isFinal)return;let t={...e,payload:e.payload??{}},n=x.name,r=P;P={state:x,context:C};try{let r=o.transitions,i=r[x.name]?.[e.type],a=r.Any?.[e.type],s=G(t,{stateTransition:i,anyTransition:a});ne(t,{from:n,handled:!!(i||a),outcome:s}),s.emitted||O.length>0?Y():!i&&!a&&u.has(e.type)&&Z(`unhandled`,{event:e,error:/* @__PURE__ */ Error(`[Actor] internal event "${e.type}" emitted but no handler in state "${x.name}"`)})}finally{P=r}}function G(e,t){let n=t.stateTransition?re(e,{transition:t.stateTransition}):{applied:!1,target:void 0,effects:void 0};if(N===void 0){let r=ie(e,{transition:t.anyTransition,allowTransition:!n.applied});return{emitted:r.emitted,transitioned:n.applied||r.transitioned,target:n.target??r.target,effects:n.effects??r.effects}}return{emitted:!1,transitioned:!1}}function ne(e,t){N===void 0&&t.handled&&k.emitTransition({event:e,from:t.from,to:t.outcome.target??x.name,transitioned:t.outcome.transitioned,effects:t.outcome.effects??[]})}function re(e,t){let n=!1,r,i;return X(`transition`,{event:e,operation:()=>{let a=t.transition(e,{context:R,actor:$()});if(a.emit&&O.push(...a.emit),a.state){let t=q(e,{step:a});r=t.target,i=t.effects,n=!0}}}),{applied:n,target:r,effects:i}}function ie(e,t){if(t.transition===void 0)return{emitted:!1,transitioned:!1};let n=t.transition,r=!1,i=!1,a,o;return X(`transition`,{event:e,operation:()=>{let s=n(e,{context:R,actor:$()});if(s.state&&t.allowTransition){let t=q(e,{step:s});a=t.target,o=t.effects,i=!0}s.emit&&(O.push(...s.emit),r=!0)}}),{emitted:r,transitioned:i,target:a,effects:o}}function K(){return(w===void 0||T)&&(w=m(C),T=!1),te({stateRef:x,regions:D,context:w,error:N,payload:S})}function q(e,t){if(N!==void 0||!t.step.state)return{};let n=l(t.step);if(!n)return{};if(!s(n.state))return Z(`transition`,{event:e,error:/* @__PURE__ */ Error(`invalid transition target`)}),{};A?.abort(),x=n.state;let r=x.name;S=n.payload,P={state:x,context:C};let i=J(e,{payloadForState:n.payload});return n.state.isFinal&&N===void 0&&k.emitDone(),{target:r,effects:i}}function J(e,t){if(F)return[];let n=o.effects[x.name];if(!n)return A=void 0,[];let r=new AbortController;A=r;let i=P??{state:x,context:C},a=ee({effects:{[x.name]:n},state:x,statePayload:t.payloadForState,event:e,context:R,emit:e=>{N!==void 0||r.signal.aborted||(O.push(e),Y())},clock:v,abort:r,lastGood:i,onError:t=>Z(`effect`,{event:e,error:t})});for(let e of a.pending)M.push(e),e.finally(()=>{let t=M.indexOf(e);t!==-1&&M.splice(t,1)}).catch(()=>{});return a.ran}function Y(){if(!(j||F)){j=!0;try{let e=0;O.processCancellable(t=>{if(N!==void 0)return!1;if(e>=_)return Z(`budget`,{event:t,error:/* @__PURE__ */ Error(`internal event budget exceeded`)}),!1;if(e++,u.has(t.type)||g.has(t.type))W(t);else if(!X(`output`,{event:t,operation:()=>k.emitOutput(t)}))return!1;return!0})}finally{j=!1,H()}}}function X(t,n){let r=e.from(()=>(n.operation(),!0));return r[0]===void 0||(Z(t,{event:n.event,error:r[0]}),!1)}function Z(e,t){if(N!==void 0)return;let n=t.lastGood??P??{state:x,context:C};N={error:t.error,state:n.state,context:n.context,event:t.event,reason:e},A?.abort(),x=L,S=void 0,E=L,I=!1,k.emitError(N),k.emitChange(K()),k.emitDone()}function ae(e){F||(O.push(e),Y())}function oe(){if(!F){F=!0,A?.abort(),A=void 0;for(let e of Object.values(D))e.dispose();D={},O.clear(),k.clear(),M=[]}}function Q(){return O.settled().then(()=>{if(M.length!==0)return Promise.all(M).then(()=>Q())})}function $(){return V}return V}function S(e){return Symbol.for(`mantaq:event:${e}`)}function C(e){return()=>w(e)}function w(e){let t=S(e),n=function(e){return typeof e==`object`&&!!e&&Object.hasOwn(e,t)};function r(n){return Object.defineProperty(n===void 0?{type:e}:{type:e,payload:n},t,{value:!0})}return{type:e,is:n,create:r}}export{x as Actor,i as Context,w as EventRef,p as RealClock,c as StateRef,v as VirtualClock,C as event,s as isStateRef,a as state};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantaq/core",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "State machine runtime with actor model, event system, state hierarchy, effects, and virtual clock.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,19 +21,19 @@
21
21
  "access": "public"
22
22
  },
23
23
  "dependencies": {
24
- "@mantaq/utils": "0.0.3"
24
+ "@mantaq/utils": "0.0.4"
25
25
  },
26
26
  "devDependencies": {
27
- "@stryker-mutator/core": "^9",
28
- "@stryker-mutator/vitest-runner": "^9",
27
+ "@stryker-mutator/core": "^10",
28
+ "@stryker-mutator/vitest-runner": "^10",
29
29
  "@types/node": "^26.2.0",
30
30
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
31
- "@vitest/coverage-v8": "4.1.10",
32
- "typescript": "^6.0.3",
33
- "vite": "npm:@voidzero-dev/vite-plus-core@0.2.8",
34
- "vite-plus": "0.2.8",
35
- "vitest": "4.1.10",
36
- "@mantaq/pbt": "0.0.2"
31
+ "@vitest/coverage-v8": "5.0.5",
32
+ "typescript": "6.0.3",
33
+ "vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
34
+ "vite-plus": "1.4.0",
35
+ "vitest": "5.0.5",
36
+ "@mantaq/pbt": "0.0.3"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "vp pack",