ihsm 0.1.23 → 0.1.25

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
@@ -83,7 +83,7 @@ class Open extends DoorTop {
83
83
  }
84
84
  }
85
85
 
86
- const door = makeActor(DoorTop, { openCount: 0 }, new Port());
86
+ const door = makeActor(DoorTop, { openCount: 0 });
87
87
  await door.hsm.sync();
88
88
 
89
89
  door.notify.open();
@@ -140,7 +140,7 @@ class WalletTop extends TopState<WalletConfig> {
140
140
  @InitialState
141
141
  class Open extends WalletTop {}
142
142
 
143
- const wallet = makeActor(WalletTop, { balance: 100 }, new Port());
143
+ const wallet = makeActor(WalletTop, { balance: 100 });
144
144
  await wallet.hsm.sync();
145
145
 
146
146
  wallet.notify.deposit(50);
@@ -212,7 +212,7 @@ class Stopped extends PlayerTop {
212
212
  }
213
213
  }
214
214
 
215
- const player = makeActor(PlayerTop, { track: '' }, new Port());
215
+ const player = makeActor(PlayerTop, { track: '' });
216
216
  await player.hsm.sync();
217
217
 
218
218
  player.notify.play();
@@ -28,7 +28,7 @@ export interface ConsoleInstrumentationOptions {
28
28
  * ```ts
29
29
  * import { createConsoleInstrumentation, registerCollector, makeActor, Port } from 'ihsm';
30
30
  * registerCollector(createConsoleInstrumentation());
31
- * const actor = makeActor(Top, ctx, new Port(), { initialize: true });
31
+ * const actor = makeActor(Top, ctx, { initialize: true });
32
32
  * ```
33
33
  */
34
34
  export declare function createConsoleInstrumentation(options?: ConsoleInstrumentationOptions): Instrumentation;
@@ -23,7 +23,7 @@ function shortUuid(uuid) {
23
23
  * ```ts
24
24
  * import { createConsoleInstrumentation, registerCollector, makeActor, Port } from 'ihsm';
25
25
  * registerCollector(createConsoleInstrumentation());
26
- * const actor = makeActor(Top, ctx, new Port(), { initialize: true });
26
+ * const actor = makeActor(Top, ctx, { initialize: true });
27
27
  * ```
28
28
  */
29
29
  function createConsoleInstrumentation(options = {}) {
@@ -1,4 +1,3 @@
1
- /** @internal Consolidated ihsm runtime (no pure types — see ./types.ts). */
2
1
  import type { ActorConfig, ActorContextOf, ActorConfigOf, ActorOptions, ChildActor, ChildHsm, DispatchableMachine, EmbodimentKind, ErrorHost, ExternalActor, ExternalHsm, HandlerHsm, HsmWithTracing, InboundActor, InboundHsm, Instance, NotificationQueue, ParentActor, PlannedTransition, IPort, MachinePortInput, Properties, ProtocolIndex, RandomService, ReservedName, SelfNotifications, ServiceCallOptions, StateClass, StateEvents, Task, TimerService, TopStateArg, TraceWriter, DispatchErrorCallback, Transition, TransitionResolver, TransitionHost, TransitionRoutineExecuteOptions, TransitionRoutinePlan, TransitionTracer, Disposable, EventObserver, TransitionTraceHost, ActorIdentity, ActorLogger, Instrumentation, OutboundCallBegin, TraceFrame } from './types';
3
2
  export declare enum TraceLevel {
4
3
  PRODUCTION = 0,
@@ -252,6 +251,10 @@ export declare class HsmObject<C extends ActorConfig> implements HsmWithTracing<
252
251
  private _traceDomainStack;
253
252
  protected _instrumentationHost?: InstrumentationHost;
254
253
  private _drainWaiters;
254
+ private _tasksSinceYield;
255
+ private _sliceStart;
256
+ /** @internal Pending actor initialization — always runs before hi-priority work. */
257
+ _initTask?: Task;
255
258
  constructor(TopState: StateClass<C>, instance: Instance<C>, traceWriter: TraceWriter, traceLevel: TraceLevel, dispatchErrorCallback: DispatchErrorCallback<C>, identity: ActorIdentity);
256
259
  get ctx(): ActorContextOf<C>;
257
260
  set ctx(ctx: ActorContextOf<C>);
@@ -282,10 +285,12 @@ export declare class HsmObject<C extends ActorConfig> implements HsmWithTracing<
282
285
  /** Invoke the user dispatch-error callback, first notifying instrumentation (pure observer). */
283
286
  reportDispatchError(err: Error): void;
284
287
  private flushDrainWaiters;
288
+ protected enqueueInitTask(task: Task): void;
285
289
  pushTask(t: Task): void;
286
290
  pushHiPriorityTask(t: Task): void;
287
291
  unshiftHiPriorityTask(t: Task): void;
288
292
  private enqueueTask;
293
+ private scheduleKickoff;
289
294
  restore(state: StateClass<C>, ctx: ActorContextOf<C>): void;
290
295
  private dequeue;
291
296
  private exec;
@@ -363,13 +368,24 @@ type ActorHandleFor<C extends ActorConfig, K extends EmbodimentKind> = K extends
363
368
  type SpawnContext<C extends ActorConfig = ActorConfig> = {
364
369
  readonly parentMachine?: Machine<C>;
365
370
  };
371
+ /** @internal Distinguish {@link ActorOptions} from a port when the port argument is omitted. */
372
+ export declare function isActorOptions(value: unknown): value is ActorOptions<ActorConfig>;
373
+ /** @internal */
374
+ export declare function resolveFactoryPortAndOptions<C extends ActorConfig>(portOrOptions?: MachinePortInput<C> | ActorOptions<C>, maybeOptions?: ActorOptions<C>): {
375
+ port: MachinePortInput<C> | undefined;
376
+ options: ActorOptions<C>;
377
+ };
366
378
  /** @internal Spawn with embodiment kind — used by factories and `ihsm/testing`. */
367
379
  export declare function spawnActor<C extends ActorConfig, K extends EmbodimentKind>(kind: K, topState: TopStateArg<C>, ctx: ActorContextOf<C>, port: MachinePortInput<C> | undefined, options: ActorOptions<C>, spawnContext?: SpawnContext<C>): ActorHandleFor<C, K>;
368
380
  /** Production black-box — public protocol only (generated handle). */
369
- export declare function makeActor<T extends TopStateArg<ActorConfig>>(topState: T, ctx: ActorContextOf<ActorConfigOf<T>>, port?: MachinePortInput<ActorConfigOf<T>>, options?: ActorOptions<ActorConfigOf<T>>): ExternalActor<ActorConfigOf<T>>;
381
+ export declare function makeActor<T extends TopStateArg<ActorConfig>>(topState: T, ctx: ActorContextOf<ActorConfigOf<T>>, options?: ActorOptions<ActorConfigOf<T>>): ExternalActor<ActorConfigOf<T>>;
382
+ export declare function makeActor<T extends TopStateArg<ActorConfig>>(topState: T, ctx: ActorContextOf<ActorConfigOf<T>>, port: MachinePortInput<ActorConfigOf<T>>, options?: ActorOptions<ActorConfigOf<T>>): ExternalActor<ActorConfigOf<T>>;
370
383
  export declare function asParentActor<T extends TopStateArg<ActorConfig>>(handler: TopState<ActorConfigOf<T>>): ParentActor<T>;
371
384
  /** Parent composes a child machine — returns full child protocol shell with `parent` set. */
372
- export declare function makeChildActor<ParentT extends TopStateArg<ActorConfig>, ChildT extends TopStateArg<ActorConfig>>(parent: ParentActor<ParentT>, childTop: ChildT, childCtx: ActorContextOf<ActorConfigOf<ChildT>>, port?: MachinePortInput<ActorConfigOf<ChildT>>, options?: ActorOptions<ActorConfigOf<ChildT>>): ChildActor<ActorConfigOf<ChildT>> & {
385
+ export declare function makeChildActor<ParentT extends TopStateArg<ActorConfig>, ChildT extends TopStateArg<ActorConfig>>(parent: ParentActor<ParentT>, childTop: ChildT, childCtx: ActorContextOf<ActorConfigOf<ChildT>>, options?: ActorOptions<ActorConfigOf<ChildT>>): ChildActor<ActorConfigOf<ChildT>> & {
386
+ readonly parent: ParentActor<ParentT>;
387
+ };
388
+ export declare function makeChildActor<ParentT extends TopStateArg<ActorConfig>, ChildT extends TopStateArg<ActorConfig>>(parent: ParentActor<ParentT>, childTop: ChildT, childCtx: ActorContextOf<ActorConfigOf<ChildT>>, port: MachinePortInput<ActorConfigOf<ChildT>>, options?: ActorOptions<ActorConfigOf<ChildT>>): ChildActor<ActorConfigOf<ChildT>> & {
373
389
  readonly parent: ParentActor<ParentT>;
374
390
  };
375
391
  export { kHandlerMachine, kParentLink } from './types';
@@ -33,10 +33,15 @@ exports.createNotificationTask = createNotificationTask;
33
33
  exports.createServiceTask = createServiceTask;
34
34
  exports.isRequestingPort = isRequestingPort;
35
35
  exports.defaultDispatchErrorCallback = defaultDispatchErrorCallback;
36
+ exports.isActorOptions = isActorOptions;
37
+ exports.resolveFactoryPortAndOptions = resolveFactoryPortAndOptions;
36
38
  exports.spawnActor = spawnActor;
37
39
  exports.makeActor = makeActor;
38
40
  exports.asParentActor = asParentActor;
39
41
  exports.makeChildActor = makeChildActor;
42
+ /** @internal Consolidated ihsm runtime (no pure types — see ./types.ts). */
43
+ /// <reference types="node" />
44
+ const scheduler_1 = require("./scheduler");
40
45
  const types_1 = require("./types");
41
46
  const identity_1 = require("./identity");
42
47
  const instrumentation_1 = require("./instrumentation");
@@ -1614,6 +1619,10 @@ class HsmObject {
1614
1619
  _traceDomainStack;
1615
1620
  _instrumentationHost;
1616
1621
  _drainWaiters = [];
1622
+ _tasksSinceYield = 0;
1623
+ _sliceStart = 0;
1624
+ /** @internal Pending actor initialization — always runs before hi-priority work. */
1625
+ _initTask;
1617
1626
  constructor(TopState, instance, traceWriter, traceLevel, dispatchErrorCallback, identity) {
1618
1627
  this._instance = instance;
1619
1628
  this._transitionState = undefined;
@@ -1729,6 +1738,13 @@ class HsmObject {
1729
1738
  for (const resolve of waiters)
1730
1739
  resolve();
1731
1740
  }
1741
+ enqueueInitTask(task) {
1742
+ this._initTask = task;
1743
+ if (this._isRunning)
1744
+ return;
1745
+ this._isRunning = true;
1746
+ this.scheduleKickoff();
1747
+ }
1732
1748
  pushTask(t) {
1733
1749
  this.enqueueTask(t, this._jobs);
1734
1750
  }
@@ -1740,31 +1756,53 @@ class HsmObject {
1740
1756
  if (this._isRunning)
1741
1757
  return;
1742
1758
  this._isRunning = true;
1743
- this.dequeue();
1759
+ this.scheduleKickoff();
1744
1760
  }
1745
1761
  enqueueTask(t, queue) {
1746
1762
  queue.push(t);
1747
1763
  if (this._isRunning)
1748
1764
  return;
1749
1765
  this._isRunning = true;
1750
- this.dequeue();
1766
+ this.scheduleKickoff();
1767
+ }
1768
+ scheduleKickoff() {
1769
+ queueMicrotask(() => this.dequeue());
1751
1770
  }
1752
1771
  restore(state, ctx) {
1753
1772
  this.currentState = state;
1754
1773
  this.ctx = ctx;
1755
1774
  }
1756
1775
  dequeue() {
1757
- if (this._hiPriorityJobs.length == 0 && this._jobs.length == 0) {
1776
+ if (this._initTask === undefined && this._hiPriorityJobs.length == 0 && this._jobs.length == 0) {
1758
1777
  this._isRunning = false;
1759
1778
  this._instrumentationHost?.onQueuesDrained();
1760
1779
  this.flushDrainWaiters();
1761
1780
  return;
1762
1781
  }
1782
+ if (this._initTask !== undefined) {
1783
+ const task = this._initTask;
1784
+ this._initTask = undefined;
1785
+ this.exec(task);
1786
+ return;
1787
+ }
1763
1788
  const task = this._hiPriorityJobs.length > 0 ? this._hiPriorityJobs.shift() : this._jobs.shift();
1764
1789
  this.exec(task);
1765
1790
  }
1766
1791
  exec(task) {
1767
- setTimeout(() => this.runTask(task).then(() => this.dequeue()), 0);
1792
+ if (this._tasksSinceYield === 0) {
1793
+ this._sliceStart = (0, scheduler_1.nowMs)();
1794
+ }
1795
+ void this.runTask(task).then(() => {
1796
+ this._tasksSinceYield++;
1797
+ const overBudget = this._tasksSinceYield >= scheduler_1.YIELD_TASK_BUDGET || (0, scheduler_1.nowMs)() - this._sliceStart >= scheduler_1.YIELD_TIME_BUDGET_MS;
1798
+ if (overBudget) {
1799
+ this._tasksSinceYield = 0;
1800
+ (0, scheduler_1.yieldToMacrotask)(() => this.dequeue());
1801
+ }
1802
+ else {
1803
+ this.dequeue();
1804
+ }
1805
+ });
1768
1806
  }
1769
1807
  runTask(task) {
1770
1808
  this._instrumentationHost?.onTaskBegin(task);
@@ -1881,7 +1919,7 @@ class Machine extends HsmObject {
1881
1919
  if (initialize) {
1882
1920
  const initTask = createInitTask(this, this.transitionResolver);
1883
1921
  (0, instrumentation_1.setTaskMeta)(initTask, { event: 'initialize', queue: 'default', triggerKind: 'init', internal: false });
1884
- this.pushTask(initTask);
1922
+ this.enqueueInitTask(initTask);
1885
1923
  }
1886
1924
  }
1887
1925
  allocateChildSpawnIndex(childTopName) {
@@ -2453,6 +2491,31 @@ function defaultDispatchErrorCallback(hsm, err) {
2453
2491
  hsm.traceWriter.write(hsm, err);
2454
2492
  throw err;
2455
2493
  }
2494
+ const ACTOR_OPTION_KEYS = new Set(['initialize', 'traceLevel', 'traceWriter', 'dispatchErrorCallback', 'transitions']);
2495
+ /** @internal Distinguish {@link ActorOptions} from a port when the port argument is omitted. */
2496
+ function isActorOptions(value) {
2497
+ if (value === null || typeof value !== 'object') {
2498
+ return false;
2499
+ }
2500
+ if (value instanceof Port || isRequestingPort(value)) {
2501
+ return false;
2502
+ }
2503
+ const keys = Object.keys(value);
2504
+ if (keys.length === 0) {
2505
+ return true;
2506
+ }
2507
+ return keys.every(key => ACTOR_OPTION_KEYS.has(key));
2508
+ }
2509
+ /** @internal */
2510
+ function resolveFactoryPortAndOptions(portOrOptions, maybeOptions) {
2511
+ if (maybeOptions !== undefined) {
2512
+ return { port: portOrOptions, options: maybeOptions };
2513
+ }
2514
+ if (portOrOptions !== undefined && isActorOptions(portOrOptions)) {
2515
+ return { port: undefined, options: portOrOptions };
2516
+ }
2517
+ return { port: portOrOptions, options: {} };
2518
+ }
2456
2519
  /** @internal Spawn with embodiment kind — used by factories and `ihsm/testing`. */
2457
2520
  function spawnActor(kind, topState, ctx, port, options, spawnContext = {}) {
2458
2521
  const { initialize = exports.defaultInitialize, traceLevel = TraceLevel.DEBUG, traceWriter = exports.defaultTraceWriter, dispatchErrorCallback = defaultDispatchErrorCallback, transitions } = options;
@@ -2495,9 +2558,9 @@ function spawnActor(kind, topState, ctx, port, options, spawnContext = {}) {
2495
2558
  boundPort.actor = createActorHandle(machine, topState, protocolIndex, portKind);
2496
2559
  return createActorHandle(machine, topState, protocolIndex, kind);
2497
2560
  }
2498
- /** Production black-box public protocol only (generated handle). */
2499
- function makeActor(topState, ctx, port, options = {}) {
2500
- return spawnActor('root', topState, ctx, port, options);
2561
+ function makeActor(topState, ctx, portOrOptions, options) {
2562
+ const { port, options: resolvedOptions } = resolveFactoryPortAndOptions(portOrOptions, options);
2563
+ return spawnActor('root', topState, ctx, port, resolvedOptions);
2501
2564
  }
2502
2565
  function asParentActor(handler) {
2503
2566
  const machine = handler[types_1.kHandlerMachine];
@@ -2509,10 +2572,10 @@ function asParentActor(handler) {
2509
2572
  [types_1.kParentLink]: machine,
2510
2573
  };
2511
2574
  }
2512
- /** Parent composes a child machine — returns full child protocol shell with `parent` set. */
2513
- function makeChildActor(parent, childTop, childCtx, port, options = {}) {
2575
+ function makeChildActor(parent, childTop, childCtx, portOrOptions, options) {
2576
+ const { port, options: resolvedOptions } = resolveFactoryPortAndOptions(portOrOptions, options);
2514
2577
  const parentMachine = parent[types_1.kParentLink];
2515
- const child = spawnActor('child', childTop, childCtx, port, options, { parentMachine: parentMachine });
2578
+ const child = spawnActor('child', childTop, childCtx, port, resolvedOptions, { parentMachine: parentMachine });
2516
2579
  Object.defineProperty(child, 'parent', { value: parent, enumerable: true, writable: false, configurable: true });
2517
2580
  return child;
2518
2581
  }