@mantaq/core 0.0.1 → 0.0.2

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
@@ -177,8 +177,8 @@ const actor = new Actor({
177
177
  initial: idleState,
178
178
  setup: (m) => {
179
179
  m.effect(loadingState, ({ state, emit, clock }) => {
180
- // state.payload is unknown — cast to the known shape
181
- const { url } = state.payload as { url: string };
180
+ // state.payload is typed to the state's declared payload: { url: string }
181
+ const url = state.payload.url;
182
182
  clock.setTimeout(1000, () => {
183
183
  emit(doneEvent.create());
184
184
  });
@@ -61,21 +61,22 @@ interface Clock {
61
61
  }
62
62
  //#endregion
63
63
  //#region src/actor-internal.d.ts
64
- interface Snapshot {
64
+ interface Snapshot<C = unknown> {
65
65
  path: string[];
66
- regions: Record<string, Snapshot>;
66
+ context: C;
67
+ regions: Record<string, Snapshot<unknown>>;
67
68
  done?: boolean;
68
69
  }
69
- interface AnyActor {
70
+ interface AnyActor<C = Record<string, unknown>> {
70
71
  state: AnyStateRef;
71
72
  clock: Clock;
72
73
  regions: Record<string, AnyActor>;
73
74
  send(event: AnyEventRef | InternalEvent): void;
74
- snapshot(): Snapshot;
75
- on(event: "change", fn: (snapshot: Snapshot) => void): () => void;
75
+ snapshot(): Snapshot<C>;
76
+ on(event: "change", fn: (snapshot: Snapshot<C>, prev: Snapshot<C>) => void): () => void;
76
77
  on(event: "done", fn: () => void): () => void;
77
78
  settled(): Promise<void>;
78
- context?: Record<string, unknown>;
79
+ context?: C;
79
80
  options?: {
80
81
  transitions?: Record<string, Record<string, unknown>>;
81
82
  effects?: Record<string, unknown[]>;
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as CreatedOfEvent, c as event, d as state, i as AnyEventRef, l as AnyStateRef, n as Snapshot, o as EventRef, r as Clock, s as InternalEvent, t as AnyActor, u as StateRef } from "./actor-internal-CfZDmHJ3.mjs";
1
+ import { a as CreatedOfEvent, c as event, d as state, i as AnyEventRef, l as AnyStateRef, n as Snapshot, o as EventRef, r as Clock, s as InternalEvent, t as AnyActor, u as StateRef } from "./actor-internal-CBU_EDPZ.mjs";
2
2
  //#region src/real-clock.d.ts
3
3
  declare class RealClock implements Clock {
4
4
  #private;
@@ -38,23 +38,31 @@ declare class VirtualClock implements Clock {
38
38
  setDrain(fn: () => void): void;
39
39
  }
40
40
  //#endregion
41
+ //#region src/context.d.ts
42
+ declare class Context<T> {
43
+ #private;
44
+ constructor(get: () => T, set: (value: T) => void);
45
+ get(): T;
46
+ set(value: T): void;
47
+ }
48
+ //#endregion
41
49
  //#region src/actor-types.d.ts
42
50
  type CreatedOf<E extends AnyEventRef> = E extends EventRef<infer Id, infer P> ? CreatedOfEvent<Id, P> : never;
43
51
  type NonFinalStateRef<States extends AnyStateRef[]> = Extract<States[number], {
44
52
  isFinal: false;
45
53
  }>;
46
- interface EffectInput<ActorContext> {
54
+ interface EffectInput<ActorContext, Payload = unknown> {
47
55
  signal: AbortSignal;
48
56
  state: {
49
57
  name: string;
50
- payload: unknown;
58
+ payload: Payload;
51
59
  };
52
60
  event: InternalEvent;
53
- context: ActorContext;
61
+ context: Context<ActorContext>;
54
62
  emit: (event: InternalEvent) => void;
55
63
  clock: Clock;
56
64
  }
57
- type EffectFn<ActorContext> = (input: EffectInput<ActorContext>) => void;
65
+ type EffectFn<ActorContext, Payload = unknown> = (input: EffectInput<ActorContext, Payload>) => void;
58
66
  type TransitionResult<AllowedState extends AnyStateRef = AnyStateRef, AllowedEmit extends string = string> = {
59
67
  state?: AllowedState | {
60
68
  state: AllowedState;
@@ -68,8 +76,9 @@ type TransitionResult<AllowedState extends AnyStateRef = AnyStateRef, AllowedEmi
68
76
  //#endregion
69
77
  //#region src/builder.d.ts
70
78
  type EventIdOf<E extends AnyEventRef> = E extends EventRef<infer Id, object | void> ? Id : never;
79
+ type PayloadOf<S extends AnyStateRef> = S extends StateRef<infer _Name, infer Payload, infer _IsFinal> ? Payload : never;
71
80
  type TransitionHandler<States extends readonly AnyStateRef[], ActorContext> = (event: InternalEvent, opts: {
72
- context: ActorContext;
81
+ context: Context<ActorContext>;
73
82
  actor: AnyActor;
74
83
  }) => TransitionResult<States[number], string>;
75
84
  interface BuiltMaps<States extends readonly AnyStateRef[], ActorContext> {
@@ -79,14 +88,14 @@ interface BuiltMaps<States extends readonly AnyStateRef[], ActorContext> {
79
88
  declare class ActorBuilder<States extends readonly AnyStateRef[], Inputs extends readonly AnyEventRef[], Internal extends readonly AnyEventRef[], Outputs extends readonly AnyEventRef[], ActorContext> {
80
89
  #private;
81
90
  on<S extends States[number], E extends Inputs[number] | Internal[number]>(stateRef: S, eventRef: E, fn: (event: E extends EventRef<infer Id, infer P> ? CreatedOfEvent<Id, P> : never, opts: {
82
- context: ActorContext;
91
+ context: Context<ActorContext>;
83
92
  actor: AnyActor;
84
93
  }) => TransitionResult<States[number], EventIdOf<Outputs[number]>>): this;
85
94
  onAny<E extends Inputs[number] | Internal[number]>(eventRef: E, fn: (event: E extends EventRef<infer Id, infer P> ? CreatedOfEvent<Id, P> : never, opts: {
86
- context: ActorContext;
95
+ context: Context<ActorContext>;
87
96
  actor: AnyActor;
88
97
  }) => TransitionResult<States[number], EventIdOf<Outputs[number]>>): this;
89
- effect<S extends States[number]>(stateRef: S, fn: EffectFn<ActorContext>): this;
98
+ effect<S extends States[number]>(stateRef: S, fn: EffectFn<ActorContext, PayloadOf<S>>): this;
90
99
  /** @internal */ build(): BuiltMaps<States, ActorContext>;
91
100
  }
92
101
  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;
@@ -130,7 +139,7 @@ interface InternalActorOptions<States extends readonly AnyStateRef[], Inputs ext
130
139
  regions?: Record<string, AnyActor>;
131
140
  }
132
141
  type TransitionDispatch<States extends readonly AnyStateRef[], ActorContext> = Record<string, Record<string, ((event: InternalEvent, options: {
133
- context: ActorContext;
142
+ context: Context<ActorContext>;
134
143
  actor: AnyActor;
135
144
  }) => TransitionResult<States[number], string>) | undefined>>;
136
145
  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>> {
@@ -141,11 +150,11 @@ declare class Actor<const States extends readonly AnyStateRef[], const Inputs ex
141
150
  get regions(): Record<string, AnyActor>;
142
151
  get options(): InternalActorOptions<States, Inputs, Internal, Outputs, ActorContext>;
143
152
  constructor(options: ActorOptions<States, Inputs, Internal, Outputs, ActorContext>);
144
- on(event: "change", fn: (snapshot: Snapshot) => void): () => void;
153
+ on(event: "change", fn: (snapshot: Snapshot<ActorContext>, prev: Snapshot<ActorContext>) => void): () => void;
145
154
  on(event: "done", fn: () => void): () => void;
146
155
  settled(): Promise<void>;
147
156
  send(event: CreatedOf$1<Inputs[number]>): void;
148
- snapshot(): Snapshot;
157
+ snapshot(): Snapshot<ActorContext>;
149
158
  }
150
159
  //#endregion
151
- export { Actor, type ActorBuilder, type AnyActor, type AnyEventRef, type AnyStateRef, type Clock, type CreatedOf, type CreatedOfEvent, type EffectFn, type EffectInput, EventRef, type InternalEvent, type NonFinalStateRef, RealClock, type SetupFn, type Snapshot, StateRef, VirtualClock, event, state };
160
+ export { Actor, type ActorBuilder, type AnyActor, type AnyEventRef, type AnyStateRef, type Clock, Context, type CreatedOf, type CreatedOfEvent, type EffectFn, type EffectInput, EventRef, type InternalEvent, type NonFinalStateRef, RealClock, type SetupFn, type Snapshot, StateRef, VirtualClock, event, state };
package/dist/index.mjs CHANGED
@@ -254,8 +254,13 @@ var InternalQueue = class {
254
254
  var Subscribers = class {
255
255
  change = /* @__PURE__ */ new Set();
256
256
  done = /* @__PURE__ */ new Set();
257
+ #last = null;
258
+ seed(snapshot) {
259
+ this.#last = snapshot;
260
+ }
257
261
  addChange(fn) {
258
262
  this.change.add(fn);
263
+ if (this.#last) fn(this.#last, this.#last);
259
264
  return () => this.change.delete(fn);
260
265
  }
261
266
  addDone(fn) {
@@ -263,7 +268,9 @@ var Subscribers = class {
263
268
  return () => this.done.delete(fn);
264
269
  }
265
270
  emitChange(snapshot) {
266
- for (const fn of this.change) fn(snapshot);
271
+ const prev = this.#last ?? snapshot;
272
+ this.#last = snapshot;
273
+ for (const fn of this.change) fn(snapshot, prev);
267
274
  }
268
275
  emitDone() {
269
276
  for (const fn of this.done) fn();
@@ -275,12 +282,13 @@ var Subscribers = class {
275
282
  };
276
283
  //#endregion
277
284
  //#region src/snapshot.ts
278
- function buildSnapshot(s, regions) {
285
+ function buildSnapshot(s, regions, context) {
279
286
  const path = [s.name];
280
287
  const regionSnapshots = {};
281
288
  for (const [regionName, child] of Object.entries(regions)) regionSnapshots[regionName] = child.snapshot();
282
289
  const snap = {
283
290
  path,
291
+ context,
284
292
  regions: regionSnapshots
285
293
  };
286
294
  if (s.isFinal) snap.done = true;
@@ -320,6 +328,22 @@ function parseTarget(step) {
320
328
  };
321
329
  }
322
330
  //#endregion
331
+ //#region src/context.ts
332
+ var Context = class {
333
+ #get;
334
+ #set;
335
+ constructor(get, set) {
336
+ this.#get = get;
337
+ this.#set = set;
338
+ }
339
+ get() {
340
+ return this.#get();
341
+ }
342
+ set(value) {
343
+ this.#set(value);
344
+ }
345
+ };
346
+ //#endregion
323
347
  //#region src/builder.ts
324
348
  var ActorBuilder = class {
325
349
  #transitions = {};
@@ -358,6 +382,9 @@ var Actor = class {
358
382
  state;
359
383
  clock;
360
384
  #context;
385
+ #contextHandle;
386
+ #lastState;
387
+ #lastContext;
361
388
  #options;
362
389
  #regions = {};
363
390
  #children = /* @__PURE__ */ new Map();
@@ -407,6 +434,12 @@ var Actor = class {
407
434
  if (!stateNames.has(initState.name)) console.warn(`[Actor] initial state "${initState.name}" not found in declared states [${[...stateNames].join(", ")}]`);
408
435
  this.state = initState;
409
436
  this.#context = options.context ?? {};
437
+ this.#lastState = initState;
438
+ this.#lastContext = this.#context;
439
+ this.#contextHandle = new Context(() => this.#context, (value) => {
440
+ this.#context = value;
441
+ });
442
+ this.#subs.seed(this.snapshot());
410
443
  if (options.regions) for (const [key, child] of Object.entries(options.regions)) {
411
444
  this.#regions[key] = child;
412
445
  const result = setOutputHandler(child, (event) => {
@@ -420,19 +453,23 @@ var Actor = class {
420
453
  on(event, fn) {
421
454
  if (event === "change") {
422
455
  const cb = fn;
423
- this.#subs.change.add(cb);
424
- cb(this.snapshot());
425
- return () => this.#subs.change.delete(cb);
456
+ return this.#subs.addChange(cb);
426
457
  }
427
458
  const cb = fn;
428
- this.#subs.done.add(cb);
429
- return () => this.#subs.done.delete(cb);
459
+ return this.#subs.addDone(cb);
430
460
  }
431
461
  settled() {
432
462
  return this.#queue.settled();
433
463
  }
434
464
  send(event) {
435
465
  this.#dispatch(event);
466
+ this.#emitChangeIfDirty();
467
+ }
468
+ #emitChangeIfDirty() {
469
+ if (this.state === this.#lastState && this.#context === this.#lastContext) return;
470
+ this.#lastState = this.state;
471
+ this.#lastContext = this.#context;
472
+ this.#subs.emitChange(this.snapshot());
436
473
  }
437
474
  #dispatch(event) {
438
475
  if (this.state.isFinal) return;
@@ -445,7 +482,7 @@ var Actor = class {
445
482
  }
446
483
  #applyStateStep(event, transition) {
447
484
  const step = transition(event, {
448
- context: this.#context,
485
+ context: this.#contextHandle,
449
486
  actor: this
450
487
  });
451
488
  if (step.emit) this.#queue.push(...step.emit);
@@ -457,7 +494,7 @@ var Actor = class {
457
494
  }
458
495
  #applyAnyStep(event, transition, allowTransition) {
459
496
  const step = transition(event, {
460
- context: this.#context,
497
+ context: this.#contextHandle,
461
498
  actor: this
462
499
  });
463
500
  if (step.state && allowTransition) this.#applyTransition(event, step);
@@ -468,7 +505,7 @@ var Actor = class {
468
505
  return false;
469
506
  }
470
507
  snapshot() {
471
- return buildSnapshot(this.state, this.#regions);
508
+ return buildSnapshot(this.state, this.#regions, this.#context);
472
509
  }
473
510
  #pushEvent(event) {
474
511
  this.#queue.push(event);
@@ -484,7 +521,6 @@ var Actor = class {
484
521
  if (!resolved) return;
485
522
  this.state = resolved.state;
486
523
  this.#runEffects(event, resolved.payload);
487
- this.#subs.emitChange(this.snapshot());
488
524
  if (this.state.isFinal) this.#subs.emitDone();
489
525
  }
490
526
  #runEffects(event, statePayload) {
@@ -498,7 +534,7 @@ var Actor = class {
498
534
  state: this.state,
499
535
  statePayload,
500
536
  event,
501
- context: this.#context,
537
+ context: this.#contextHandle,
502
538
  emit: (e) => {
503
539
  this.#queue.push(e);
504
540
  this.#drainInternal();
@@ -528,6 +564,7 @@ var Actor = class {
528
564
  });
529
565
  } finally {
530
566
  this.#draining = false;
567
+ this.#emitChangeIfDirty();
531
568
  }
532
569
  if (budgetExceeded) console.warn(`[Actor] internal event budget (${this.#internalBudget}) exceeded — possible emit loop. Actor halting.`);
533
570
  }
@@ -554,4 +591,4 @@ var EventRef = class {
554
591
  }
555
592
  };
556
593
  //#endregion
557
- export { Actor, EventRef, RealClock, StateRef, VirtualClock, event, state };
594
+ export { Actor, Context, EventRef, RealClock, StateRef, VirtualClock, event, state };
@@ -1,4 +1,4 @@
1
- import { s as InternalEvent, t as AnyActor } from "./actor-internal-CfZDmHJ3.mjs";
1
+ import { s as InternalEvent, t as AnyActor } from "./actor-internal-CBU_EDPZ.mjs";
2
2
  import { Either } from "@mantaq/utils";
3
3
  //#region src/internal-registry.d.ts
4
4
  interface ActorInternal {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantaq/core",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "State machine runtime with actor model, event system, state hierarchy, effects, and virtual clock.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,7 +33,7 @@
33
33
  "vite": "npm:@voidzero-dev/vite-plus-core@0.2.8",
34
34
  "vite-plus": "0.2.8",
35
35
  "vitest": "4.1.10",
36
- "@mantaq/pbt": "0.0.1"
36
+ "@mantaq/pbt": "0.0.2"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "vp pack",