ihsm 0.0.26 → 0.1.1
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 +105 -113
- package/lib/cjs/index.d.ts +5 -1394
- package/lib/cjs/index.js +53 -764
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/internal/runtime.d.ts +293 -0
- package/lib/cjs/internal/runtime.js +1906 -0
- package/lib/cjs/internal/runtime.js.map +1 -0
- package/lib/cjs/internal/types.d.ts +348 -0
- package/lib/cjs/internal/types.js +9 -0
- package/lib/cjs/internal/types.js.map +1 -0
- package/lib/cjs/test-only.d.ts +5 -0
- package/lib/cjs/test-only.js +21 -0
- package/lib/cjs/test-only.js.map +1 -0
- package/lib/cjs/testing.d.ts +38 -91
- package/lib/cjs/testing.js +72 -38
- package/lib/cjs/testing.js.map +1 -1
- package/lib/cjs/transition-routines.d.ts +3 -0
- package/lib/cjs/transition-routines.js +11 -0
- package/lib/cjs/transition-routines.js.map +1 -0
- package/lib/cjs/types.d.ts +5 -0
- package/lib/cjs/{internal/defs.private.js → types.js} +1 -1
- package/lib/cjs/types.js.map +1 -0
- package/lib/esm/index.d.ts +5 -1394
- package/lib/esm/index.js +3 -742
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/internal/runtime.d.ts +293 -0
- package/lib/esm/internal/runtime.js +1847 -0
- package/lib/esm/internal/runtime.js.map +1 -0
- package/lib/esm/internal/types.d.ts +348 -0
- package/lib/esm/internal/types.js +6 -0
- package/lib/esm/internal/types.js.map +1 -0
- package/lib/esm/test-only.d.ts +5 -0
- package/lib/esm/test-only.js +15 -0
- package/lib/esm/test-only.js.map +1 -0
- package/lib/esm/testing.d.ts +38 -91
- package/lib/esm/testing.js +72 -38
- package/lib/esm/testing.js.map +1 -1
- package/lib/esm/transition-routines.d.ts +3 -0
- package/lib/esm/transition-routines.js +3 -0
- package/lib/esm/transition-routines.js.map +1 -0
- package/lib/esm/types.d.ts +5 -0
- package/lib/esm/types.js +2 -0
- package/lib/esm/types.js.map +1 -0
- package/package.json +22 -4
- package/lib/cjs/internal/defs.private.d.ts +0 -41
- package/lib/cjs/internal/defs.private.js.map +0 -1
- package/lib/cjs/internal/dispatch.debug.d.ts +0 -4
- package/lib/cjs/internal/dispatch.debug.js +0 -332
- package/lib/cjs/internal/dispatch.debug.js.map +0 -1
- package/lib/cjs/internal/dispatch.production.d.ts +0 -6
- package/lib/cjs/internal/dispatch.production.js +0 -241
- package/lib/cjs/internal/dispatch.production.js.map +0 -1
- package/lib/cjs/internal/dispatch.trace.d.ts +0 -4
- package/lib/cjs/internal/dispatch.trace.js +0 -418
- package/lib/cjs/internal/dispatch.trace.js.map +0 -1
- package/lib/cjs/internal/hsm.d.ts +0 -60
- package/lib/cjs/internal/hsm.js +0 -215
- package/lib/cjs/internal/hsm.js.map +0 -1
- package/lib/cjs/internal/lookup.d.ts +0 -15
- package/lib/cjs/internal/lookup.js +0 -32
- package/lib/cjs/internal/lookup.js.map +0 -1
- package/lib/cjs/internal/utils.d.ts +0 -26
- package/lib/cjs/internal/utils.js +0 -63
- package/lib/cjs/internal/utils.js.map +0 -1
- package/lib/esm/internal/defs.private.d.ts +0 -41
- package/lib/esm/internal/defs.private.js +0 -2
- package/lib/esm/internal/defs.private.js.map +0 -1
- package/lib/esm/internal/dispatch.debug.d.ts +0 -4
- package/lib/esm/internal/dispatch.debug.js +0 -328
- package/lib/esm/internal/dispatch.debug.js.map +0 -1
- package/lib/esm/internal/dispatch.production.d.ts +0 -6
- package/lib/esm/internal/dispatch.production.js +0 -237
- package/lib/esm/internal/dispatch.production.js.map +0 -1
- package/lib/esm/internal/dispatch.trace.d.ts +0 -4
- package/lib/esm/internal/dispatch.trace.js +0 -414
- package/lib/esm/internal/dispatch.trace.js.map +0 -1
- package/lib/esm/internal/hsm.d.ts +0 -60
- package/lib/esm/internal/hsm.js +0 -211
- package/lib/esm/internal/hsm.js.map +0 -1
- package/lib/esm/internal/lookup.d.ts +0 -15
- package/lib/esm/internal/lookup.js +0 -29
- package/lib/esm/internal/lookup.js.map +0 -1
- package/lib/esm/internal/utils.d.ts +0 -26
- package/lib/esm/internal/utils.js +0 -52
- package/lib/esm/internal/utils.js.map +0 -1
package/lib/cjs/index.d.ts
CHANGED
|
@@ -1,1397 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ihsm — hierarchical state machines for TypeScript.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* `ctx` fields are checked at compile time.
|
|
6
|
-
*
|
|
7
|
-
* @category Factory
|
|
8
|
-
*/
|
|
9
|
-
export type Any = Record<string, any>;
|
|
10
|
-
/**
|
|
11
|
-
* Rejects an in-flight {@link Hsm.call} service by rejecting the client's `Promise`.
|
|
12
|
-
*
|
|
13
|
-
* The runtime injects this callback as the **second** parameter of service handlers.
|
|
14
|
-
* Call it with any `Error` (or subclass) when the service cannot complete successfully.
|
|
15
|
-
* After `reject` is invoked, the client's `await call(...)` throws; no further handler
|
|
16
|
-
* code should call `resolve`.
|
|
17
|
-
*
|
|
18
|
-
* @param error - Failure reason propagated to the `call()` caller
|
|
19
|
-
*
|
|
20
|
-
* @remarks
|
|
21
|
-
* Pair with {@link ResolveCallback} on the same handler signature. Service methods are
|
|
22
|
-
* recognized by the `(resolve, reject, ...payload)` parameter pattern on `Protocol`.
|
|
23
|
-
*
|
|
24
|
-
* @category Event handler
|
|
25
|
-
*/
|
|
26
|
-
export type RejectCallback = (error: Error) => void;
|
|
27
|
-
/**
|
|
28
|
-
* Resolves an in-flight {@link Hsm.call} service with a typed reply.
|
|
29
|
-
*
|
|
30
|
-
* The runtime injects this callback as the **first** parameter of service handlers.
|
|
31
|
-
* Call it exactly once with the successful result; the client's `Promise` settles with
|
|
32
|
-
* that value. The handler's own return value (including `Promise<void>` from `async`
|
|
33
|
-
* handlers) does **not** substitute for calling `resolve`.
|
|
34
|
-
*
|
|
35
|
-
* @typeParam Reply - Success type inferred from the service method's `resolve` parameter
|
|
36
|
-
* @param result - Value delivered to the `call()` caller
|
|
37
|
-
*
|
|
38
|
-
* @remarks
|
|
39
|
-
* For `async` service handlers, perform awaits first, then call `resolve(result)` before
|
|
40
|
-
* returning. If you forget `resolve`, the client's `Promise` never settles.
|
|
41
|
-
*
|
|
42
|
-
* @category Event handler
|
|
43
|
-
*/
|
|
44
|
-
export type ResolveCallback<Reply> = (result: Reply) => void;
|
|
45
|
-
/**
|
|
46
|
-
* Application hook invoked when the runtime cannot recover from a dispatch failure.
|
|
47
|
-
*
|
|
48
|
-
* Called **after** {@link StateEvents.onError} and {@link StateEvents.onUnhandled} have
|
|
49
|
-
* been tried and the error is still propagating, or when no recovery hook handled it.
|
|
50
|
-
* The default implementation logs via {@link TraceWriter} and **rethrows** the error.
|
|
51
|
-
*
|
|
52
|
-
* @param hsm - The machine handle (`Hsm` or handler `State` view) at failure time
|
|
53
|
-
* @param err - The error that terminated dispatch (often {@link EventHandlerError},
|
|
54
|
-
* {@link UnhandledEventError}, {@link TransitionError}, or {@link FatalError})
|
|
55
|
-
*
|
|
56
|
-
* @remarks
|
|
57
|
-
* Override at construction (`makeHsm(..., dispatchErrorCallback)`) or assign
|
|
58
|
-
* `hsm.dispatchErrorCallback` for integration tests that must assert failures.
|
|
59
|
-
* Note: {@link Hsm.sync} still resolves when the default callback throws — failures
|
|
60
|
-
* surface in logs, not as a rejected `sync()` Promise.
|
|
61
|
-
*
|
|
62
|
-
* @category Factory
|
|
63
|
-
*/
|
|
64
|
-
export interface DispatchErrorCallback<Context, Protocol extends {} | undefined> {
|
|
65
|
-
(hsm: Base<Context, Protocol>, err: Error): void;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Controls how much diagnostic detail the runtime emits through {@link TraceWriter}.
|
|
69
|
-
*
|
|
70
|
-
* Set at construction via {@link makeHsm} or mutate {@link Properties.traceLevel} on a
|
|
71
|
-
* live instance. Changing the level swaps the internal dispatch tracer implementation.
|
|
72
|
-
*
|
|
73
|
-
* @category Factory
|
|
74
|
-
*/
|
|
75
|
-
export declare enum TraceLevel {
|
|
76
|
-
/**
|
|
77
|
-
* Production mode: minimal tracing overhead, no verbose dispatch steps.
|
|
78
|
-
* Use in hot paths and shipped bundles when trace output is disabled.
|
|
79
|
-
*/
|
|
80
|
-
PRODUCTION = 0,
|
|
81
|
-
/**
|
|
82
|
-
* Debug mode: transition boundaries, handler entry/exit, and error summaries.
|
|
83
|
-
* Default for {@link makeHsm}. Suitable for development and integration tests.
|
|
84
|
-
*/
|
|
85
|
-
DEBUG = 1,
|
|
86
|
-
/**
|
|
87
|
-
* Verbose debug: includes prototype-chain lookup walks, cache hits/misses, and
|
|
88
|
-
* nested trace domains. Use when correlating handler code with tutorial trace panels.
|
|
89
|
-
*/
|
|
90
|
-
VERBOSE_DEBUG = 2
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Sink for structured or human-readable trace output from the runtime and handlers.
|
|
94
|
-
*
|
|
95
|
-
* Inject a custom implementation via {@link makeHsm} to collect traces in tests,
|
|
96
|
-
* forward to OpenTelemetry, or suppress console noise in CI.
|
|
97
|
-
*
|
|
98
|
-
* @category Factory
|
|
99
|
-
*/
|
|
100
|
-
export interface TraceWriter {
|
|
101
|
-
/**
|
|
102
|
-
* Record one trace line for a machine instance.
|
|
103
|
-
*
|
|
104
|
-
* @typeParam Context - Domain context type of the machine being traced
|
|
105
|
-
* @typeParam Protocol - Event/service vocabulary of the machine being traced
|
|
106
|
-
* @param hsm - Read-only machine properties ({@link Properties.currentStateName},
|
|
107
|
-
* {@link Properties.traceHeader}, etc.) at the time of the write
|
|
108
|
-
* @param msg - Payload to record. **Strings** are formatted as
|
|
109
|
-
* `` `${traceHeader}${currentStateName}: ${msg}` `` by the default console writer;
|
|
110
|
-
* non-strings are passed through unchanged (e.g. `Error` objects on failure paths)
|
|
111
|
-
*
|
|
112
|
-
* @remarks
|
|
113
|
-
* Handlers may call `this.traceWriter.write(this.hsm, 'my message')` for ad-hoc
|
|
114
|
-
* logging that respects the same header and state prefix as runtime traces.
|
|
115
|
-
*/
|
|
116
|
-
write<Context, Protocol extends {} | undefined>(hsm: Properties<Context, Protocol>, msg: any): void;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Read-only snapshot of runtime metadata shared by client handles and active handlers.
|
|
120
|
-
*
|
|
121
|
-
* Exposed on {@link State} (inside handlers as `this.hsm` properties and forwarded
|
|
122
|
-
* getters on {@link TopState}) and on {@link Hsm} (external client code). Values
|
|
123
|
-
* reflect the **current dispatch** when read from within an event handler; outside
|
|
124
|
-
* handlers, {@link eventName} and {@link eventPayload} are empty.
|
|
125
|
-
*
|
|
126
|
-
* @category State machine
|
|
127
|
-
*/
|
|
128
|
-
export interface Properties<Context, Protocol extends {} | undefined> {
|
|
129
|
-
/**
|
|
130
|
-
* Constructor (`Function`) of the **leaf** state class currently executing.
|
|
131
|
-
*
|
|
132
|
-
* Compare with {@link topState}, which is always the root composite passed to
|
|
133
|
-
* {@link makeHsm}. After a transition, this updates to the new leaf's constructor.
|
|
134
|
-
*/
|
|
135
|
-
readonly currentState: StateClass<Context, Protocol>;
|
|
136
|
-
/**
|
|
137
|
-
* Human-readable name of {@link currentState}.
|
|
138
|
-
*
|
|
139
|
-
* Sourced from {@link defineStateName} / {@link registerStateNames} when registered;
|
|
140
|
-
* otherwise `Class.name` (unreliable under minification — register names in browser builds).
|
|
141
|
-
*/
|
|
142
|
-
readonly currentStateName: string;
|
|
143
|
-
/**
|
|
144
|
-
* Constructor of the root state class supplied to {@link makeHsm}.
|
|
145
|
-
*
|
|
146
|
-
* Constant for the lifetime of the instance unless you replace the entire machine.
|
|
147
|
-
*/
|
|
148
|
-
readonly topState: StateClass<Context, Protocol>;
|
|
149
|
-
/** Display name of {@link topState} (same naming rules as {@link currentStateName}). */
|
|
150
|
-
readonly topStateName: string;
|
|
151
|
-
/**
|
|
152
|
-
* Runtime label derived from `ctx` constructor name, used as the first segment of
|
|
153
|
-
* {@link traceHeader} in verbose traces.
|
|
154
|
-
*/
|
|
155
|
-
readonly ctxTypeName: string;
|
|
156
|
-
/**
|
|
157
|
-
* Prefix for nested trace domains, built from internal dispatch stack frames.
|
|
158
|
-
*
|
|
159
|
-
* Empty at the top level; grows like `domain|subdomain|` during nested operations.
|
|
160
|
-
* Handlers rarely need to read this directly — it is prepended automatically by
|
|
161
|
-
* the default {@link TraceWriter}.
|
|
162
|
-
*/
|
|
163
|
-
readonly traceHeader: string;
|
|
164
|
-
/**
|
|
165
|
-
* Name of the event or service currently being dispatched.
|
|
166
|
-
*
|
|
167
|
-
* Matches the string passed to {@link Base.post}, {@link Hsm.call}, or
|
|
168
|
-
* {@link State.postNow}. Empty string when no handler is running.
|
|
169
|
-
*/
|
|
170
|
-
readonly eventName: string;
|
|
171
|
-
/**
|
|
172
|
-
* Arguments passed with the current dispatch, excluding injected `resolve` / `reject`
|
|
173
|
-
* for {@link Hsm.call} services.
|
|
174
|
-
*
|
|
175
|
-
* Empty array when idle. Typed as `any[]` at runtime; correlate with
|
|
176
|
-
* {@link EventPayload} / {@link ServiceRequest} at compile time on the client.
|
|
177
|
-
*/
|
|
178
|
-
readonly eventPayload: any[];
|
|
179
|
-
/**
|
|
180
|
-
* Active trace verbosity; changing this swaps dispatch tracing behavior immediately.
|
|
181
|
-
*
|
|
182
|
-
* @see TraceLevel
|
|
183
|
-
*/
|
|
184
|
-
traceLevel: TraceLevel;
|
|
185
|
-
/**
|
|
186
|
-
* Destination for runtime and handler-initiated trace lines.
|
|
187
|
-
*
|
|
188
|
-
* Replaceable at any time (e.g. swap in a test double before `post`/`call`).
|
|
189
|
-
*/
|
|
190
|
-
traceWriter: TraceWriter;
|
|
191
|
-
/**
|
|
192
|
-
* Last-resort error hook when {@link StateEvents.onError} / {@link StateEvents.onUnhandled}
|
|
193
|
-
* do not recover.
|
|
194
|
-
*
|
|
195
|
-
* @see DispatchErrorCallback
|
|
196
|
-
*/
|
|
197
|
-
dispatchErrorCallback: DispatchErrorCallback<Context, Protocol>;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Fire-and-forget event posting API available on both {@link Hsm} (clients) and {@link State} (handlers).
|
|
201
|
-
*
|
|
202
|
-
* Events enqueue on the actor and run **one at a time, to completion** — no re-entrancy while a
|
|
203
|
-
* handler is active.
|
|
204
|
-
*
|
|
205
|
-
* @category State machine
|
|
206
|
-
*/
|
|
207
|
-
export interface Base<Context, Protocol extends {} | undefined> extends Properties<Context, Protocol> {
|
|
208
|
-
/**
|
|
209
|
-
* Enqueue a **normal-priority** event for later dispatch on the active state.
|
|
210
|
-
*
|
|
211
|
-
* Returns immediately; the handler runs asynchronously when the actor reaches this job.
|
|
212
|
-
* Dispatch walks the **prototype chain** from the current leaf upward until a method named
|
|
213
|
-
* `eventName` is found.
|
|
214
|
-
*
|
|
215
|
-
* @typeParam EventName - Literal key of `Protocol` being posted
|
|
216
|
-
* @param eventName - Event or service name. Must be `keyof Protocol` and must **not** collide
|
|
217
|
-
* with reserved {@link State} method names (`transition`, `post`, `ctx`, …)
|
|
218
|
-
* @param eventPayload - Arguments tuple inferred from `Protocol[eventName]` handler parameters.
|
|
219
|
-
* For **events**, pass every parameter **except** `resolve` / `reject`. For fire-and-forget
|
|
220
|
-
* events, the handler return type must be `void` or `Promise<void>`
|
|
221
|
-
*
|
|
222
|
-
* @remarks
|
|
223
|
-
* **Client usage:** `door.post('open')` then `await door.sync()` to wait for completion.
|
|
224
|
-
*
|
|
225
|
-
* **Handler usage:** `this.post('tick')` schedules work **after** the current handler returns
|
|
226
|
-
* and after any {@link State.transition} it requested. Normal-priority posts run **after** all
|
|
227
|
-
* {@link State.postNow} hi-priority jobs drained for the current turn.
|
|
228
|
-
*
|
|
229
|
-
* **Ordering:** FIFO among normal-priority jobs. Multiple posts before one `sync()` are
|
|
230
|
-
* processed in submission order.
|
|
231
|
-
*
|
|
232
|
-
* **Typing:** With `Protocol extends undefined`, accepts any `string` and `any[]` (legacy mode).
|
|
233
|
-
*
|
|
234
|
-
* @example Client fire-and-forget
|
|
235
|
-
* ```ts
|
|
236
|
-
* door.post('open');
|
|
237
|
-
* await door.sync(); // handler + transition complete
|
|
238
|
-
* ```
|
|
239
|
-
*
|
|
240
|
-
* @example Handler chains follow-up work
|
|
241
|
-
* ```ts
|
|
242
|
-
* approve(): void {
|
|
243
|
-
* this.ctx.approved = true;
|
|
244
|
-
* this.post('notify'); // runs after this handler finishes
|
|
245
|
-
* }
|
|
246
|
-
* ```
|
|
247
|
-
*/
|
|
248
|
-
post<EventName extends keyof Protocol>(eventName: PostedEvent<Protocol, EventName>, ...eventPayload: EventPayload<Protocol, EventName>): void;
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Handler-facing API available inside state class methods (`this` / `this.hsm`).
|
|
252
|
-
*
|
|
253
|
-
* Extends {@link Base} with context access, transitions, async sleep, explicit unhandled
|
|
254
|
-
* signaling, and {@link postNow} hi-priority re-dispatch. Only code running **inside** an
|
|
255
|
-
* active handler should call {@link transition}, {@link unhandled}, or {@link postNow}.
|
|
256
|
-
*
|
|
257
|
-
* @category State machine
|
|
258
|
-
*/
|
|
259
|
-
export interface State<Context, Protocol extends {} | undefined> extends Base<Context, Protocol> {
|
|
260
|
-
/**
|
|
261
|
-
* Mutable domain data object shared across all states of this machine instance.
|
|
262
|
-
*
|
|
263
|
-
* Passed as the second argument to {@link makeHsm}; survives transitions unless replaced
|
|
264
|
-
* by {@link Hsm.restore}. Update fields freely for internal transitions (no `transition()`).
|
|
265
|
-
*
|
|
266
|
-
* @remarks
|
|
267
|
-
* Context is **not** the active state name — state is which class prototype is active;
|
|
268
|
-
* context is arbitrary application data (counters, buffers, IDs, flags).
|
|
269
|
-
*/
|
|
270
|
-
readonly ctx: Context;
|
|
271
|
-
/**
|
|
272
|
-
* Schedule an **external** transition to `nextState` after the current handler completes.
|
|
273
|
-
*
|
|
274
|
-
* Does **not** run exit/entry immediately. When the handler returns successfully (including
|
|
275
|
-
* after awaiting an `async` handler's Promise), the runtime:
|
|
276
|
-
*
|
|
277
|
-
* 1. Computes the **lowest common ancestor (LCA)** on the class prototype chain
|
|
278
|
-
* 2. Runs `onExit()` from the current leaf up to (but not including) the LCA
|
|
279
|
-
* 3. Switches the instance prototype to `nextState` (descending `@InitialState` chains for composites)
|
|
280
|
-
* 4. Runs `onEntry()` down from the LCA to the target leaf
|
|
281
|
-
*
|
|
282
|
-
* @param nextState - Destination state **class** constructor (not an instance)
|
|
283
|
-
*
|
|
284
|
-
* @throws {@link TransitionError} when `onExit` or `onEntry` throws along the path (may transition to {@link FatalErrorState})
|
|
285
|
-
* @throws {@link EventHandlerError} when the triggering handler threw before transition phase
|
|
286
|
-
*
|
|
287
|
-
* @remarks
|
|
288
|
-
* - **Self-transition** to the same leaf with unchanged initial descent: optimized to skip exit/entry
|
|
289
|
-
* - **Internal transition:** omit `transition()` — active class unchanged, no exit/entry
|
|
290
|
-
* - **`transition()` inside `onEntry`/`onExit`:** scheduled transition is **cleared** when that lifecycle dispatch ends — use {@link post} from `onEntry` for follow-up work
|
|
291
|
-
* - Transition paths are **cached** per `From=>To` pair for hot loops
|
|
292
|
-
* - Only the **last** `transition()` call wins if invoked multiple times in one handler
|
|
293
|
-
*
|
|
294
|
-
* @example
|
|
295
|
-
* ```ts
|
|
296
|
-
* open(): void {
|
|
297
|
-
* this.ctx.openCount += 1;
|
|
298
|
-
* this.transition(Open);
|
|
299
|
-
* }
|
|
300
|
-
* ```
|
|
301
|
-
*/
|
|
302
|
-
transition(nextState: StateClass<Context, Protocol>): void;
|
|
303
|
-
/**
|
|
304
|
-
* Declare that the current event has **no handler** on this state (explicit super-call pattern).
|
|
305
|
-
*
|
|
306
|
-
* Throws {@link UnhandledEventError} unless an ancestor's {@link StateEvents.onUnhandled}
|
|
307
|
-
* catches it. Prefer **omitting** the method entirely when a state should inherit a parent's
|
|
308
|
-
* handler — only call `unhandled()` when you intentionally defer to the error model.
|
|
309
|
-
*
|
|
310
|
-
* @returns `never` — always throws or redirects via `onUnhandled`
|
|
311
|
-
*
|
|
312
|
-
* @throws {@link UnhandledEventError} by default
|
|
313
|
-
*
|
|
314
|
-
* @remarks
|
|
315
|
-
* Runtime dispatch already throws when no method exists on the prototype chain; `unhandled()`
|
|
316
|
-
* is for handlers that exist but choose not to handle the event.
|
|
317
|
-
*/
|
|
318
|
-
unhandled(): never;
|
|
319
|
-
/**
|
|
320
|
-
* Pause the **current handler** without blocking the JavaScript event loop.
|
|
321
|
-
*
|
|
322
|
-
* Returns a Promise resolved after `millis` milliseconds via `setTimeout`. The actor
|
|
323
|
-
* remains **locked** to this handler until the Promise settles (run-to-completion) — other
|
|
324
|
-
* `post`/`call` jobs queue but do not run.
|
|
325
|
-
*
|
|
326
|
-
* @param millis - Sleep duration in milliseconds (≥ 0)
|
|
327
|
-
* @returns Promise that resolves (never rejects) when the delay elapses
|
|
328
|
-
*
|
|
329
|
-
* @remarks
|
|
330
|
-
* Use for simple delays inside handlers. For calendar-time deferral of **new** events,
|
|
331
|
-
* prefer {@link deferredPost}. Composable with `async` handlers: `await this.sleep(100)`.
|
|
332
|
-
*
|
|
333
|
-
* @example
|
|
334
|
-
* ```ts
|
|
335
|
-
* async pulse(): Promise<void> {
|
|
336
|
-
* await this.sleep(50);
|
|
337
|
-
* this.ctx.pulses += 1;
|
|
338
|
-
* }
|
|
339
|
-
* ```
|
|
340
|
-
*/
|
|
341
|
-
sleep(millis: number): Promise<void>;
|
|
342
|
-
/**
|
|
343
|
-
* Schedule a normal-priority {@link post} after a wall-clock delay. **Handler-only.**
|
|
344
|
-
*
|
|
345
|
-
* Backed by the machine's {@link Port} timer service: when no custom port is supplied the
|
|
346
|
-
* runtime instantiates a {@link Port} whose `setTimeout`-based timer is used here. When
|
|
347
|
-
* the timer fires, the event is enqueued like an ordinary `post`. This is intentionally **not**
|
|
348
|
-
* on the external {@link Hsm} / {@link Actor} surface — clients schedule work via {@link post}.
|
|
349
|
-
*
|
|
350
|
-
* @typeParam EventName - Literal key of `Protocol` being scheduled
|
|
351
|
-
* @param millis - Delay in milliseconds before enqueueing (≥ 0). Subject to timer granularity
|
|
352
|
-
* @param eventName - Event name (same constraints as {@link post})
|
|
353
|
-
* @param eventPayload - Handler arguments tuple (same as {@link post})
|
|
354
|
-
*
|
|
355
|
-
* @remarks
|
|
356
|
-
* Does **not** block the calling handler — returns as soon as the timer is registered. Timers
|
|
357
|
-
* are **not** cancelled if the machine transitions or the scheduling handler throws. (Richer
|
|
358
|
-
* timer services — cancellation, virtual clocks — will arrive later through the port.)
|
|
359
|
-
*
|
|
360
|
-
* @example
|
|
361
|
-
* ```ts
|
|
362
|
-
* scheduleReminder(text: string): void {
|
|
363
|
-
* this.deferredPost(50, 'deliver', text);
|
|
364
|
-
* }
|
|
365
|
-
* deliver(text: string): void {
|
|
366
|
-
* this.ctx.message = text;
|
|
367
|
-
* }
|
|
368
|
-
* ```
|
|
369
|
-
*/
|
|
370
|
-
deferredPost<EventName extends keyof Protocol>(millis: number, eventName: PostedEvent<Protocol, EventName>, ...eventPayload: EventPayload<Protocol, EventName>): void;
|
|
371
|
-
/**
|
|
372
|
-
* Enqueue a **hi-priority** event processed before normal {@link post} jobs from the same turn.
|
|
373
|
-
*
|
|
374
|
-
* Only valid **inside** a running handler (`this.postNow`). Clients must use {@link post}.
|
|
375
|
-
* Hi-priority jobs drain immediately after the current handler and its transition complete,
|
|
376
|
-
* **before** any normal-priority posts the handler enqueued (including chained `this.post`).
|
|
377
|
-
*
|
|
378
|
-
* @typeParam EventName - Literal key of `Protocol`
|
|
379
|
-
* @param eventName - Event name (same typing rules as {@link post})
|
|
380
|
-
* @param eventPayload - Handler arguments (same tuple as {@link post})
|
|
381
|
-
*
|
|
382
|
-
* @remarks
|
|
383
|
-
* Models **extended transitions**: multiple internal steps (lock, capture, validate) that
|
|
384
|
-
* must complete before deferred side effects. See tutorial `17-post-now`.
|
|
385
|
-
*
|
|
386
|
-
* Multiple `postNow` calls run in FIFO order within the hi-priority queue. You may need
|
|
387
|
-
* an extra {@link Hsm.sync} after the first to drain postNow follow-ups.
|
|
388
|
-
*
|
|
389
|
-
* @example
|
|
390
|
-
* ```ts
|
|
391
|
-
* confirm(): void {
|
|
392
|
-
* this.post('cancel'); // normal — runs last
|
|
393
|
-
* this.postNow('lockInventory'); // hi — runs first among follow-ups
|
|
394
|
-
* this.postNow('capturePayment');
|
|
395
|
-
* this.transition(Confirmed);
|
|
396
|
-
* }
|
|
397
|
-
* ```
|
|
398
|
-
*/
|
|
399
|
-
postNow<EventName extends keyof Protocol>(eventName: PostedEvent<Protocol, EventName>, ...eventPayload: EventPayload<Protocol, EventName>): void;
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Client handle returned by {@link makeHsm} — post events, await services, synchronize, restore.
|
|
403
|
-
*
|
|
404
|
-
* The same object is also the runtime actor (`HsmObject`); external code should treat it as
|
|
405
|
-
* an **actor reference**: send messages, never mutate internal queues directly.
|
|
406
|
-
*
|
|
407
|
-
* @category State machine
|
|
408
|
-
*/
|
|
409
|
-
export interface Hsm<Context = Any, Protocol extends {} | undefined = undefined> extends Base<Context, Protocol> {
|
|
410
|
-
/** @inheritdoc State.ctx */
|
|
411
|
-
readonly ctx: Context;
|
|
412
|
-
/**
|
|
413
|
-
* Wait until all previously enqueued actor work completes through a **sync marker**.
|
|
414
|
-
*
|
|
415
|
-
* Returns a Promise resolved when the marker job reaches the front of the queue and runs —
|
|
416
|
-
* meaning every job enqueued **before** this `sync()` call has finished (handlers, transitions,
|
|
417
|
-
* hi-priority drains, and previously scheduled timers that have already fired).
|
|
418
|
-
*
|
|
419
|
-
* @returns Promise that resolves when the queue drains up to the marker (does not reject on handler errors unless {@link dispatchErrorCallback} rethrows to caller)
|
|
420
|
-
*
|
|
421
|
-
* @remarks
|
|
422
|
-
* - **After `post`:** one `sync()` waits for that handler and its transition
|
|
423
|
-
* - **Batch posts:** single `sync()` waits for **all** jobs enqueued before it
|
|
424
|
-
* - **After chained handler `post`s:** call `sync()` again to drain follow-up work
|
|
425
|
-
* - **After `postNow` chains:** may require **two** `sync()` calls (handler + hi-priority drain)
|
|
426
|
-
* - **After `call`:** usually unnecessary — `await call(...)` already waits for `resolve`/`reject`
|
|
427
|
-
* - **Initialization:** `makeHsm(..., initialize: true)` enqueues init work; await `sync()` before asserting initial state
|
|
428
|
-
*
|
|
429
|
-
* @example
|
|
430
|
-
* ```ts
|
|
431
|
-
* door.post('open');
|
|
432
|
-
* await door.sync();
|
|
433
|
-
* expect(door.currentStateName).toBe('Open');
|
|
434
|
-
* ```
|
|
435
|
-
*/
|
|
436
|
-
sync(): Promise<void>;
|
|
437
|
-
/**
|
|
438
|
-
* Atomically replace the active leaf state and context **without** running `onExit` / `onEntry`.
|
|
439
|
-
*
|
|
440
|
-
* Used for persistence rehydration, snapshot restore, time-travel debugging, and test fixtures.
|
|
441
|
-
* Does not enqueue dispatch jobs — the next `post`/`call` runs from the restored configuration.
|
|
442
|
-
*
|
|
443
|
-
* @param state - Leaf or composite state **class** to activate (prototype switched immediately)
|
|
444
|
-
* @param ctx - New context object (replaces {@link ctx} reference entirely)
|
|
445
|
-
*
|
|
446
|
-
* @remarks
|
|
447
|
-
* Caller is responsible for consistency: restored `ctx` should match what `state` expects.
|
|
448
|
-
* Does not walk `@InitialState` — if you restore a composite class, you get that exact class,
|
|
449
|
-
* not its default child. Queued jobs from before `restore` are **not** cancelled.
|
|
450
|
-
*
|
|
451
|
-
* @example
|
|
452
|
-
* ```ts
|
|
453
|
-
* checkpoint.restore(SavedState, savedCtx);
|
|
454
|
-
* await checkpoint.sync(); // drain any pre-restore jobs first if needed
|
|
455
|
-
* ```
|
|
456
|
-
*/
|
|
457
|
-
restore(state: StateClass<Context, Protocol>, ctx: Context): void;
|
|
458
|
-
/**
|
|
459
|
-
* Invoke a **service** handler and await its typed result over the actor's run-to-completion dispatch.
|
|
460
|
-
*
|
|
461
|
-
* Enqueues a dispatch job like {@link post}, but the runtime prepends `resolve` and `reject`
|
|
462
|
-
* callbacks to the handler invocation. The returned Promise settles when the handler calls
|
|
463
|
-
* `resolve(value)` or `reject(error)` — **not** when the handler function returns.
|
|
464
|
-
*
|
|
465
|
-
* @typeParam EventName - Literal service name on `Protocol`
|
|
466
|
-
* @param eventName - Service key whose handler signature starts with
|
|
467
|
-
* `(resolve: ResolveCallback<T>, reject: RejectCallback, ...payload)`
|
|
468
|
-
* @param eventPayload - Request arguments **after** resolve/reject (client never passes callbacks)
|
|
469
|
-
* @returns Promise resolving to `T` inferred from the handler's `resolve` parameter type
|
|
470
|
-
*
|
|
471
|
-
* @throws Propagates any `Error` passed to `reject`, or {@link EventHandlerError} /
|
|
472
|
-
* {@link UnhandledEventError} if dispatch fails before the service runs
|
|
473
|
-
*
|
|
474
|
-
* @remarks
|
|
475
|
-
* - Same **serialized**, run-to-completion dispatch as `post` — no concurrent handler re-entrancy
|
|
476
|
-
* - Return type {@link ServiceResponse} is inferred from `Protocol[eventName]`
|
|
477
|
-
* - Use {@link ResolveCallback} / {@link RejectCallback} in handler signatures for clarity
|
|
478
|
-
* - `async` handlers should `await` work then call `resolve(result)`
|
|
479
|
-
*
|
|
480
|
-
* @example
|
|
481
|
-
* ```ts
|
|
482
|
-
* // Protocol: getBalance(resolve: ResolveCallback<number>, reject: RejectCallback): void
|
|
483
|
-
* const balance = await wallet.call('getBalance');
|
|
484
|
-
* ```
|
|
485
|
-
*/
|
|
486
|
-
call<EventName extends keyof Protocol>(eventName: ServiceName<Protocol, EventName>, ...eventPayload: ServiceRequest<Protocol, EventName>): Promise<ServiceResponse<Protocol, EventName>>;
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Valid first argument to {@link Base.post} / {@link State.postNow} — a protocol key that names
|
|
490
|
-
* a **void** handler (event), excluding reserved {@link State} method names.
|
|
491
|
-
*
|
|
492
|
-
* @typeParam Protocol - Machine vocabulary interface, or `undefined` for untyped `string` mode
|
|
493
|
-
* @typeParam EventName - Member key being constrained
|
|
494
|
-
*
|
|
495
|
-
* @remarks
|
|
496
|
-
* Collisions with `keyof State` become `never`, preventing `post('transition', …)` at compile time.
|
|
497
|
-
* Service-shaped keys (see {@link IsServiceMethod}) also become `never` — they must be invoked with
|
|
498
|
-
* {@link Hsm.call}, so `post('getBalance', …)` is a compile error (proposal T2).
|
|
499
|
-
*
|
|
500
|
-
* @category Event handler
|
|
501
|
-
*/
|
|
502
|
-
export type PostedEvent<Protocol extends {} | undefined, EventName extends keyof Protocol> = Protocol extends undefined ? string : EventName extends keyof State<any, any> ? never : IsServiceMethod<Protocol[EventName]> extends true ? never : EventName;
|
|
503
|
-
/**
|
|
504
|
-
* Tuple of arguments for {@link Base.post} after the event name, inferred from the handler signature.
|
|
505
|
-
*
|
|
506
|
-
* @typeParam Protocol - Machine vocabulary interface
|
|
507
|
-
* @typeParam EventName - Event key whose parameter list is extracted
|
|
508
|
-
*
|
|
509
|
-
* @remarks
|
|
510
|
-
* For `open(): void`, payload is `[]`. For `setTarget(celsius: number): void`, payload is `[number]`.
|
|
511
|
-
* Service-shaped methods (leading resolve/reject) are not valid events — payload becomes `never`.
|
|
512
|
-
*
|
|
513
|
-
* @category Event handler
|
|
514
|
-
*/
|
|
515
|
-
export type EventPayload<Protocol extends {} | undefined, EventName extends keyof Protocol> = Protocol extends undefined ? any[] : Protocol[EventName] extends (...payload: infer Payload) => Promise<void> | void ? (Payload extends any[] ? Payload : never) : never;
|
|
516
|
-
/**
|
|
517
|
-
* Constructor type for a state class participating in the hierarchy.
|
|
518
|
-
*
|
|
519
|
-
* Satisfied by any `Function` whose `prototype` derives from {@link TopState}. Used wherever
|
|
520
|
-
* the runtime expects a state **class**, not an instance:
|
|
521
|
-
* {@link makeHsm}, {@link State.transition}, {@link InitialState}, {@link Hsm.restore}.
|
|
522
|
-
*
|
|
523
|
-
* @typeParam Context - Domain context carried by the machine
|
|
524
|
-
* @typeParam Protocol - Event/service vocabulary
|
|
525
|
-
*
|
|
526
|
-
* @remarks
|
|
527
|
-
* The prototype constraint is the **port-less** handler contract ({@link State} &
|
|
528
|
-
* {@link StateEvents}) rather than {@link TopState} itself. This keeps the optional
|
|
529
|
-
* {@link TopState.port} member (which varies with the `Port` type parameter) out of
|
|
530
|
-
* transition / `restore` typing, so machines that declare a port remain assignable
|
|
531
|
-
* wherever a plain `StateClass` is expected.
|
|
532
|
-
*
|
|
533
|
-
* @category State machine
|
|
534
|
-
*/
|
|
535
|
-
export type StateClass<Context = Any, Protocol extends {} | undefined = undefined> = Function & {
|
|
536
|
-
prototype: State<Context, Protocol> & StateEvents<Context, Protocol>;
|
|
537
|
-
};
|
|
538
|
-
/**
|
|
539
|
-
* Resource teardown handle returned alongside subscription-style port results.
|
|
540
|
-
*
|
|
541
|
-
* `dispose()` must be **idempotent** — calling it more than once is a no-op. Ports hand
|
|
542
|
-
* one back via {@link ResultWithSubscription}; the state machine owns it and disposes it
|
|
543
|
-
* when the corresponding observation is no longer wanted.
|
|
544
|
-
*
|
|
545
|
-
* @category Port
|
|
546
|
-
*/
|
|
547
|
-
export interface Disposable {
|
|
548
|
-
/** Release the resource / detach listeners. Safe to call repeatedly. */
|
|
549
|
-
dispose(): void;
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* A port result paired with the {@link Disposable} that tears down its subscription.
|
|
553
|
-
*
|
|
554
|
-
* Returned by port methods that both produce a value (e.g. a process id) **and** wire
|
|
555
|
-
* ongoing observations. The machine stores `value` and is responsible for calling
|
|
556
|
-
* `subscription.dispose()` during teardown.
|
|
557
|
-
*
|
|
558
|
-
* @typeParam Result - The immediate value produced by the port call
|
|
559
|
-
*
|
|
560
|
-
* @example
|
|
561
|
-
* ```ts
|
|
562
|
-
* spawn(spec: SpawnSpec): ResultWithSubscription<number> {
|
|
563
|
-
* const child = spawnProcess(spec);
|
|
564
|
-
* const bag = wireListeners(child, this.hsm());
|
|
565
|
-
* return { value: child.pid, subscription: bag };
|
|
566
|
-
* }
|
|
567
|
-
* ```
|
|
568
|
-
*
|
|
569
|
-
* @category Port
|
|
570
|
-
*/
|
|
571
|
-
export interface ResultWithSubscription<Result> {
|
|
572
|
-
/** Immediate result of the port call. */
|
|
573
|
-
readonly value: Result;
|
|
574
|
-
/** Teardown handle for the observations the call established. */
|
|
575
|
-
readonly subscription: Disposable;
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* A single recorded interaction: an event name plus its payload.
|
|
579
|
-
*
|
|
580
|
-
* Produced both by {@link testing!TestPort} (the messages a test double records) and by the
|
|
581
|
-
* {@link testing!TestActor} `subscribe` observer stream (every event posted through the machine).
|
|
582
|
-
*
|
|
583
|
-
* @category Testing
|
|
584
|
-
*/
|
|
585
|
-
export interface TracedMessage {
|
|
586
|
-
/** Event/service name, or a free-form label recorded by a {@link testing!TestPort}. */
|
|
587
|
-
readonly event: string;
|
|
588
|
-
/** Arguments captured with the event (a defensive copy). */
|
|
589
|
-
readonly payload: readonly unknown[];
|
|
590
|
-
}
|
|
591
|
-
/**
|
|
592
|
-
* Observer invoked for **every** event posted through a {@link testing!TestActor}.
|
|
593
|
-
*
|
|
594
|
-
* Registered via {@link testing!TestActor} `subscribe` — a capability unique to the test surface.
|
|
595
|
-
* Wire it to {@link testing!TestPort.record} to build a golden trace on the port under test.
|
|
596
|
-
*
|
|
597
|
-
* @category Testing
|
|
598
|
-
*/
|
|
599
|
-
export type EventObserver = (message: TracedMessage) => void;
|
|
600
|
-
/**
|
|
601
|
-
* The effective protocol a machine **dispatches** over: the public {@link Protocol} merged
|
|
602
|
-
* with its `InternalProtocol`.
|
|
603
|
-
*
|
|
604
|
-
* Handlers (and the `Port` back-channel) see this union; external clients see only the
|
|
605
|
-
* public half. Legacy untyped machines (`Protocol extends undefined`) stay untyped.
|
|
606
|
-
*
|
|
607
|
-
* @typeParam Protocol - Public event/service vocabulary (client-callable)
|
|
608
|
-
* @typeParam InternalProtocol - Events only a port may post (e.g. `onSpawn`, `onExit`)
|
|
609
|
-
*
|
|
610
|
-
* @category State machine
|
|
611
|
-
*/
|
|
612
|
-
export type Dispatch<Protocol extends {} | undefined, InternalProtocol extends {}> = {} extends InternalProtocol ? Protocol : Protocol extends undefined ? undefined : Protocol & InternalProtocol;
|
|
613
|
-
/**
|
|
614
|
-
* Compile-time guard asserting the public and internal protocols share **no** event names.
|
|
615
|
-
*
|
|
616
|
-
* Resolves to `true` when `keyof Public` and `keyof Internal` are disjoint; otherwise to a
|
|
617
|
-
* descriptive tuple that fails the `extends true` constraint on {@link makeActor} /
|
|
618
|
-
* {@link testing!makeTestActor}, surfacing the overlapping keys at the call site.
|
|
619
|
-
*
|
|
620
|
-
* @typeParam Public - Public protocol
|
|
621
|
-
* @typeParam Internal - Internal protocol
|
|
622
|
-
*
|
|
623
|
-
* @category State machine
|
|
624
|
-
*/
|
|
625
|
-
export type Disjoint<Public, Internal> = Extract<keyof Public, keyof Internal> extends never ? true : ['ihsm: public and internal protocols must not share keys', Extract<keyof Public, keyof Internal>];
|
|
626
|
-
/**
|
|
627
|
-
* Phantom type carrier that lets a {@link TopState} subclass expose its four type parameters
|
|
628
|
-
* for extraction. It never exists at runtime — it is a `declare`d marker (see {@link TopState}).
|
|
629
|
-
*
|
|
630
|
-
* @typeParam Context - Domain context
|
|
631
|
-
* @typeParam Public - Public protocol
|
|
632
|
-
* @typeParam Internal - Internal (port-driven) protocol
|
|
633
|
-
* @typeParam Port - Port type
|
|
634
|
-
*
|
|
635
|
-
* @category State machine
|
|
636
|
-
*/
|
|
637
|
-
export interface MachineTypes<Context, Public, Internal, Port> {
|
|
638
|
-
readonly context: Context;
|
|
639
|
-
readonly public: Public;
|
|
640
|
-
readonly internal: Internal;
|
|
641
|
-
readonly port: Port;
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Extracts the **context** type from a {@link TopState} subclass — making the `TopState` the
|
|
645
|
-
* single point where the four machine types are declared.
|
|
646
|
-
*
|
|
647
|
-
* @typeParam T - A {@link TopState} subclass (instance type, e.g. `ConnTop`)
|
|
648
|
-
*
|
|
649
|
-
* @category State machine
|
|
650
|
-
*/
|
|
651
|
-
export type MachineContext<T> = T extends {
|
|
652
|
-
readonly __ihsm: MachineTypes<infer Context, any, any, any>;
|
|
653
|
-
} ? Context : never;
|
|
654
|
-
/**
|
|
655
|
-
* Extracts the **public** protocol from a {@link TopState} subclass.
|
|
656
|
-
*
|
|
657
|
-
* @typeParam T - A {@link TopState} subclass (instance type)
|
|
658
|
-
*
|
|
659
|
-
* @category State machine
|
|
660
|
-
*/
|
|
661
|
-
export type MachinePublic<T> = T extends {
|
|
662
|
-
readonly __ihsm: MachineTypes<any, infer Public, any, any>;
|
|
663
|
-
} ? Public : never;
|
|
664
|
-
/**
|
|
665
|
-
* Extracts the **internal** protocol from a {@link TopState} subclass. The result is always
|
|
666
|
-
* within `{} | undefined`, so it can drive {@link PostedEvent} / {@link EventPayload} directly.
|
|
667
|
-
*
|
|
668
|
-
* @typeParam T - A {@link TopState} subclass (instance type)
|
|
669
|
-
*
|
|
670
|
-
* @category State machine
|
|
671
|
-
*/
|
|
672
|
-
export type MachineInternal<T> = T extends {
|
|
673
|
-
readonly __ihsm: MachineTypes<any, any, infer Internal, any>;
|
|
674
|
-
} ? (Internal extends {} | undefined ? Internal : never) : never;
|
|
675
|
-
/**
|
|
676
|
-
* Extracts the **port** type from a {@link TopState} subclass.
|
|
677
|
-
*
|
|
678
|
-
* @typeParam T - A {@link TopState} subclass (instance type)
|
|
679
|
-
*
|
|
680
|
-
* @category State machine
|
|
681
|
-
*/
|
|
682
|
-
export type MachinePort<T> = T extends {
|
|
683
|
-
readonly __ihsm: MachineTypes<any, any, any, infer Port>;
|
|
684
|
-
} ? Port : never;
|
|
685
|
-
/**
|
|
686
|
-
* Restricted handle a {@link Port} uses to post **internal** events back into its machine.
|
|
687
|
-
*
|
|
688
|
-
* It is the {@link Base} surface narrowed to the `InternalProtocol`, so a port can only
|
|
689
|
-
* `post` the events it is allowed to raise — never public commands.
|
|
690
|
-
*
|
|
691
|
-
* @typeParam Context - Domain context
|
|
692
|
-
* @typeParam InternalProtocol - Events the port may post
|
|
693
|
-
*
|
|
694
|
-
* @category Port
|
|
695
|
-
*/
|
|
696
|
-
export type InboundPoster<Context, InternalProtocol extends {} | undefined> = Base<Context, InternalProtocol>;
|
|
697
|
-
/**
|
|
698
|
-
* Outbound boundary between a machine and the impure world (processes, sockets, timers).
|
|
699
|
-
*
|
|
700
|
-
* Passed as the `port` instance to {@link makeActor} / {@link testing!makeTestActor} (or defaulted to a
|
|
701
|
-
* {@link Port}), and surfaced to handlers as {@link TopState.port}. The factory binds the
|
|
702
|
-
* port's {@link PortHandle.actor | actor} lazily, so the port can post internal events back via
|
|
703
|
-
* {@link PortHandle.hsm}.
|
|
704
|
-
*
|
|
705
|
-
* @typeParam Context - Domain context
|
|
706
|
-
* @typeParam InternalProtocol - Events this port may post inward
|
|
707
|
-
*
|
|
708
|
-
* @category Port
|
|
709
|
-
*/
|
|
710
|
-
export interface PortHandle<Context = Any, InternalProtocol extends {} | undefined = undefined> {
|
|
711
|
-
/**
|
|
712
|
-
* The machine handle this port posts internal events through. **Bound lazily** by the runtime
|
|
713
|
-
* ({@link makeHsm} / {@link makeActor} / {@link testing!makeTestActor}) right after the actor is
|
|
714
|
-
* constructed — so a port is created with no constructor arguments and wired up afterwards.
|
|
715
|
-
* `undefined` before binding / after teardown.
|
|
716
|
-
*/
|
|
717
|
-
actor: InboundPoster<Context, InternalProtocol> | undefined;
|
|
718
|
-
/** The bound machine handle (same as {@link PortHandle.actor}); `undefined` before binding. */
|
|
719
|
-
hsm(): InboundPoster<Context, InternalProtocol> | undefined;
|
|
720
|
-
}
|
|
721
|
-
/** Opaque timer handle returned by {@link Port.setTimeout} / {@link Port.setInterval}. */
|
|
722
|
-
export type TimerHandle = number;
|
|
723
|
-
/**
|
|
724
|
-
* Standard JavaScript random-generation surface exposed by {@link Port} and mocked by
|
|
725
|
-
* {@link testing!TestPort}.
|
|
726
|
-
*
|
|
727
|
-
* Route every nondeterministic draw through the machine's port — never `Math.random()` or
|
|
728
|
-
* `crypto.*` directly in handlers — so tests can script values with
|
|
729
|
-
* {@link testing!TestPort.feedRandom | feedRandom} / {@link testing!TestPort.feedCryptoRandom | feedCryptoRandom} /
|
|
730
|
-
* {@link testing!TestPort.feedUUID | feedUUID} / {@link testing!TestPort.feedRandomBytes | feedRandomBytes}.
|
|
731
|
-
*
|
|
732
|
-
* @category Port
|
|
733
|
-
*/
|
|
734
|
-
export interface RandomService {
|
|
735
|
-
/** Uniform in `[0, 1)` — `Math.random()`. */
|
|
736
|
-
random(): number;
|
|
737
|
-
/** Uniform in `[0, 1)` — `crypto.random()` when available, otherwise `Math.random()`. */
|
|
738
|
-
cryptoRandom(): number;
|
|
739
|
-
/** RFC 4122 UUID — `crypto.randomUUID()`. */
|
|
740
|
-
randomUUID(): string;
|
|
741
|
-
/** In-place fill — `crypto.getRandomValues()`. */
|
|
742
|
-
getRandomValues<T extends ArrayBufferView>(array: T): T;
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* External, public-only view of a machine returned by {@link makeActor}.
|
|
746
|
-
*
|
|
747
|
-
* Identical to {@link Hsm} over the **public** protocol: clients can `post` / `call` only
|
|
748
|
-
* public events — internal (port-driven) events are not in the callable surface.
|
|
749
|
-
*
|
|
750
|
-
* @typeParam Context - Domain context
|
|
751
|
-
* @typeParam Protocol - Public protocol
|
|
752
|
-
*
|
|
753
|
-
* @category State machine
|
|
754
|
-
*/
|
|
755
|
-
export type Actor<Context = Any, Protocol extends {} | undefined = undefined> = Hsm<Context, Protocol>;
|
|
756
|
-
/**
|
|
757
|
-
* Abstract base class for **any** port — production or test.
|
|
758
|
-
*
|
|
759
|
-
* It takes the machine's root {@link TopState} as its single type argument and derives the
|
|
760
|
-
* context and internal protocol from it (via {@link MachineContext} / {@link MachineInternal}),
|
|
761
|
-
* so the `TopState` is the one place those types are declared. Extend {@link Port} (not `BasePort`
|
|
762
|
-
* directly) for production ports so timer and random services are available.
|
|
763
|
-
*
|
|
764
|
-
* The {@link BasePort.actor | actor} link is **bound lazily** by the runtime: construct the port
|
|
765
|
-
* with no arguments and pass the instance to a factory, which wires the actor in afterwards.
|
|
766
|
-
*
|
|
767
|
-
* @typeParam T - The machine's root {@link TopState} subclass (e.g. `ConnTop`)
|
|
768
|
-
*
|
|
769
|
-
* @category Port
|
|
770
|
-
*/
|
|
771
|
-
export declare abstract class BasePort<T> implements PortHandle<MachineContext<T>, MachineInternal<T>> {
|
|
772
|
-
/**
|
|
773
|
-
* Phantom carrier of the root {@link TopState} type, so {@link testing!makeTestPort} can recover `T`
|
|
774
|
-
* (and therefore the port surface, via {@link MachinePort}) from a mock class. Type-only.
|
|
775
|
-
*/
|
|
776
|
-
readonly __topState: T;
|
|
777
|
-
/**
|
|
778
|
-
* @inheritdoc PortHandle.actor
|
|
779
|
-
*
|
|
780
|
-
* Set once by the runtime right after the machine is built; `undefined` before binding.
|
|
781
|
-
*/
|
|
782
|
-
actor: InboundPoster<MachineContext<T>, MachineInternal<T>> | undefined;
|
|
783
|
-
/** @inheritdoc PortHandle.hsm */
|
|
784
|
-
hsm(): InboundPoster<MachineContext<T>, MachineInternal<T>> | undefined;
|
|
785
|
-
/**
|
|
786
|
-
* Post an internal event inward through the bound {@link BasePort.actor | actor}.
|
|
787
|
-
*
|
|
788
|
-
* This is the one channel a port (or a test driving the port) uses to feed the machine its
|
|
789
|
-
* internal protocol. Because emission is explicit — never a side effect of the outbound call
|
|
790
|
-
* the machine made — a single mock works across many tests: the test decides *when* (and
|
|
791
|
-
* whether) to push each internal event.
|
|
792
|
-
*
|
|
793
|
-
* @param eventName - Internal event to post
|
|
794
|
-
* @param payload - Arguments for the event
|
|
795
|
-
* @throws If called before the actor has been bound by a factory
|
|
796
|
-
*/
|
|
797
|
-
send<EventName extends keyof MachineInternal<T>>(eventName: PostedEvent<MachineInternal<T>, EventName>, ...payload: EventPayload<MachineInternal<T>, EventName>): void;
|
|
798
|
-
}
|
|
799
|
-
/**
|
|
800
|
-
* Production port base with standard JavaScript timer and random services.
|
|
801
|
-
*
|
|
802
|
-
* Extend this class for domain ports in production code. It inherits the lazily-bound
|
|
803
|
-
* {@link BasePort.actor | actor}, {@link BasePort.hsm | hsm}, and {@link BasePort.send | send}
|
|
804
|
-
* from {@link BasePort}, and adds {@link Port.setTimeout | setTimeout} /
|
|
805
|
-
* {@link Port.setInterval | setInterval} / {@link Port.clearTimeout | clearTimeout} /
|
|
806
|
-
* {@link Port.clearInterval | clearInterval} plus the {@link RandomService} methods
|
|
807
|
-
* ({@link Port.random | random}, {@link Port.cryptoRandom | cryptoRandom},
|
|
808
|
-
* {@link Port.randomUUID | randomUUID}, {@link Port.getRandomValues | getRandomValues}).
|
|
809
|
-
*
|
|
810
|
-
* {@link State.deferredPost} delegates to {@link Port.setTimeout}. When no custom port is
|
|
811
|
-
* supplied the runtime instantiates a plain `Port` for that purpose.
|
|
812
|
-
*
|
|
813
|
-
* @typeParam T - The machine's root {@link TopState} subclass (e.g. `ConnTop`)
|
|
814
|
-
*
|
|
815
|
-
* @example A minimal domain port
|
|
816
|
-
* ```ts
|
|
817
|
-
* class ConnPortImpl extends ihsm.Port<ConnTop> implements ConnPort {
|
|
818
|
-
* private nextId = 1;
|
|
819
|
-
* connect(host: string): ihsm.ResultWithSubscription<number> {
|
|
820
|
-
* const id = this.nextId++;
|
|
821
|
-
* return { value: id, subscription: { dispose: () => {} } };
|
|
822
|
-
* }
|
|
823
|
-
* disconnect(id: number): void {}
|
|
824
|
-
* }
|
|
825
|
-
* const port = new ConnPortImpl();
|
|
826
|
-
* const conn = ihsm.makeActor(ConnTop, ctx, port); // binds port.actor
|
|
827
|
-
* ```
|
|
828
|
-
*
|
|
829
|
-
* @category Port
|
|
830
|
-
*/
|
|
831
|
-
export declare class Port<T = Any> extends BasePort<T> implements RandomService {
|
|
832
|
-
private _timerSeq;
|
|
833
|
-
private readonly _timeoutHandles;
|
|
834
|
-
private readonly _intervalHandles;
|
|
835
|
-
/**
|
|
836
|
-
* Schedule `callback` after `millis` milliseconds — same argument order as `globalThis.setTimeout`.
|
|
837
|
-
*
|
|
838
|
-
* @returns An opaque handle for {@link Port.clearTimeout}
|
|
839
|
-
*/
|
|
840
|
-
setTimeout(callback: () => void, millis?: number): TimerHandle;
|
|
841
|
-
/** Cancel a pending {@link Port.setTimeout} handle. No-op when `id` is `undefined` or unknown. */
|
|
842
|
-
clearTimeout(id: TimerHandle | undefined): void;
|
|
843
|
-
/**
|
|
844
|
-
* Schedule `callback` every `millis` milliseconds — same argument order as `globalThis.setInterval`.
|
|
845
|
-
*
|
|
846
|
-
* @returns An opaque handle for {@link Port.clearInterval}
|
|
847
|
-
*/
|
|
848
|
-
setInterval(callback: () => void, millis?: number): TimerHandle;
|
|
849
|
-
/** Cancel a pending {@link Port.setInterval} handle. No-op when `id` is `undefined` or unknown. */
|
|
850
|
-
clearInterval(id: TimerHandle | undefined): void;
|
|
851
|
-
/** @inheritdoc RandomService.random */
|
|
852
|
-
random(): number;
|
|
853
|
-
/** @inheritdoc RandomService.cryptoRandom */
|
|
854
|
-
cryptoRandom(): number;
|
|
855
|
-
/** @inheritdoc RandomService.randomUUID */
|
|
856
|
-
randomUUID(): string;
|
|
857
|
-
/** @inheritdoc RandomService.getRandomValues */
|
|
858
|
-
getRandomValues<T extends ArrayBufferView>(array: T): T;
|
|
859
|
-
}
|
|
860
|
-
/**
|
|
861
|
-
* `true` when handler `M` is **service-shaped** — its parameter list begins with a resolve callback
|
|
862
|
-
* and a reject callback (`(resolve, reject, ...payload) => void | Promise<void>`), matching
|
|
863
|
-
* {@link ResolveCallback} / {@link RejectCallback}; `false` for plain **event** handlers whose
|
|
864
|
-
* parameters are data.
|
|
865
|
-
*
|
|
866
|
-
* This is the single discriminator that routes a protocol key to {@link Hsm.call} (services) versus
|
|
867
|
-
* {@link Base.post} (events): {@link ServiceName} / {@link ServiceKeys} accept only keys where this
|
|
868
|
-
* is `true`, while {@link PostedEvent} / {@link EventKeys} reject them (proposal T2).
|
|
869
|
-
*
|
|
870
|
-
* @remarks
|
|
871
|
-
* The check is **structural and heuristic**: a handler counts as a service iff its first two
|
|
872
|
-
* parameters are callables. A plain event that genuinely takes two leading callbacks (rare) would
|
|
873
|
-
* be classified as a service — give such a handler `(): void` / data parameters, and reserve the
|
|
874
|
-
* leading `(resolve, reject)` shape for real services.
|
|
875
|
-
*
|
|
876
|
-
* @category Event handler
|
|
877
|
-
*/
|
|
878
|
-
export type IsServiceMethod<M> = M extends (...args: infer Args) => Promise<void> | void ? (Args extends [resolve: (result: any) => void, reject: (error: any) => void, ...payload: any[]] ? true : false) : false;
|
|
879
|
-
/**
|
|
880
|
-
* Union of protocol keys whose handlers are **services** — invocable with {@link Hsm.call}. Reserved
|
|
881
|
-
* {@link State} method names are excluded; resolves to `string` for the untyped (`undefined`) protocol.
|
|
882
|
-
*
|
|
883
|
-
* @category Event handler
|
|
884
|
-
*/
|
|
885
|
-
export type ServiceKeys<Protocol extends {} | undefined> = Protocol extends undefined ? string : Exclude<{
|
|
886
|
-
[K in keyof Protocol]-?: IsServiceMethod<Protocol[K]> extends true ? K : never;
|
|
887
|
-
}[keyof Protocol], keyof State<any, any>>;
|
|
888
|
-
/**
|
|
889
|
-
* Union of protocol keys whose handlers are **events** — postable with {@link Base.post}. Reserved
|
|
890
|
-
* {@link State} method names and service-shaped keys are excluded; resolves to `string` for the
|
|
891
|
-
* untyped (`undefined`) protocol.
|
|
892
|
-
*
|
|
893
|
-
* @category Event handler
|
|
894
|
-
*/
|
|
895
|
-
export type EventKeys<Protocol extends {} | undefined> = Protocol extends undefined ? string : Exclude<{
|
|
896
|
-
[K in keyof Protocol]-?: IsServiceMethod<Protocol[K]> extends true ? never : K;
|
|
897
|
-
}[keyof Protocol], keyof State<any, any>>;
|
|
898
|
-
/**
|
|
899
|
-
* Tuple of client-supplied arguments to {@link Hsm.call}, excluding injected resolve/reject.
|
|
900
|
-
*
|
|
901
|
-
* @typeParam Protocol - Machine vocabulary interface
|
|
902
|
-
* @typeParam EventName - Service key whose request parameters are extracted
|
|
903
|
-
*
|
|
904
|
-
* @remarks
|
|
905
|
-
* Extracted from everything after `(resolve, reject, ...payload)` on the service method.
|
|
906
|
-
*
|
|
907
|
-
* @category Event handler
|
|
908
|
-
*/
|
|
909
|
-
export type ServiceRequest<Protocol, EventName extends keyof Protocol> = Protocol extends undefined ? any[] : Protocol[EventName] extends (resolve: (result: infer Reply) => void, reject: (error: infer Error) => void, ...payload: infer Payload) => Promise<void> | void ? (Payload extends any[] ? Payload : never) : never;
|
|
910
|
-
/**
|
|
911
|
-
* Success type returned by {@link Hsm.call}, inferred from the service handler's `resolve` callback.
|
|
912
|
-
*
|
|
913
|
-
* @typeParam Protocol - Machine vocabulary interface
|
|
914
|
-
* @typeParam EventName - Service key whose reply type is extracted
|
|
915
|
-
*
|
|
916
|
-
* @remarks
|
|
917
|
-
* For `getBalance(resolve: (n: number) => void, …)`, response is `number`.
|
|
918
|
-
*
|
|
919
|
-
* @category Event handler
|
|
920
|
-
*/
|
|
921
|
-
export type ServiceResponse<Protocol, EventName extends keyof Protocol> = Protocol extends undefined ? any : Protocol[EventName] extends (resolve: (result: infer Reply) => void, reject: (error: infer _Error) => void, ...payload: infer _Payload) => Promise<void> | void ? Reply : never;
|
|
922
|
-
/**
|
|
923
|
-
* Valid first argument to {@link Hsm.call} — protocol keys whose handlers use the service signature
|
|
924
|
-
* `(resolve, reject, ...payload)`. Reserved {@link State} names and **event-shaped** keys become
|
|
925
|
-
* `never`, so `call('open')` on a void event is a compile error (proposal T2).
|
|
926
|
-
*
|
|
927
|
-
* @typeParam Protocol - Machine vocabulary interface
|
|
928
|
-
* @typeParam EventName - Candidate key being constrained
|
|
929
|
-
*
|
|
930
|
-
* @category Event handler
|
|
931
|
-
*/
|
|
932
|
-
export type ServiceName<Protocol, EventName> = Protocol extends undefined ? string : EventName extends keyof State<any, any> ? never : EventName extends keyof Protocol ? (IsServiceMethod<Protocol[EventName]> extends true ? EventName : never) : never;
|
|
933
|
-
/**
|
|
934
|
-
* Lifecycle hooks optionally overridden on state classes.
|
|
935
|
-
*
|
|
936
|
-
* Default implementations on {@link TopState} are empty for entry/exit and rethrow for errors.
|
|
937
|
-
* Only states that **define their own** `onEntry`/`onExit` participate in verbose trace exit lists.
|
|
938
|
-
*
|
|
939
|
-
* @category State machine
|
|
940
|
-
*/
|
|
941
|
-
export interface StateEvents<Context, Protocol extends {} | undefined> {
|
|
942
|
-
/**
|
|
943
|
-
* Invoked when **leaving** this state during an external transition.
|
|
944
|
-
*
|
|
945
|
-
* Runs during the exit phase **after** the triggering handler completes and before the
|
|
946
|
-
* prototype switches away. Async `onExit` is awaited before continuing up the LCA path.
|
|
947
|
-
*
|
|
948
|
-
* @throws {@link TransitionError} when this hook throws — may route to {@link FatalErrorState}
|
|
949
|
-
*
|
|
950
|
-
* @remarks
|
|
951
|
-
* Not called for internal transitions (handler returns without {@link State.transition}).
|
|
952
|
-
* Not called on {@link Hsm.restore}. Self-transitions may skip exit when optimized.
|
|
953
|
-
*/
|
|
954
|
-
onExit(): Promise<void> | void;
|
|
955
|
-
/**
|
|
956
|
-
* Invoked when **entering** this state during initialization or an external transition.
|
|
957
|
-
*
|
|
958
|
-
* Runs during the entry phase **after** the instance prototype points at this state
|
|
959
|
-
* (the runtime adopts the entering state class immediately before invoking the hook).
|
|
960
|
-
* Async `onEntry` is awaited before entering nested initial substates or running deeper
|
|
961
|
-
* `onEntry` hooks.
|
|
962
|
-
*
|
|
963
|
-
* @throws {@link TransitionError} or {@link InitializationError} when this hook throws
|
|
964
|
-
*
|
|
965
|
-
* @remarks
|
|
966
|
-
* During `makeHsm(..., initialize: true)`, `onEntry` runs from root down through each
|
|
967
|
-
* `@InitialState` chain to the initial leaf. Schedule follow-up transitions via {@link Base.post},
|
|
968
|
-
* not {@link State.transition}, from within `onEntry`.
|
|
969
|
-
*/
|
|
970
|
-
onEntry(): Promise<void> | void;
|
|
971
|
-
/**
|
|
972
|
-
* Recovery hook when an event handler throws {@link EventHandlerError}.
|
|
973
|
-
*
|
|
974
|
-
* @typeParam EventName - Correlated event key from `Protocol`
|
|
975
|
-
* @param error - Typed runtime error with {@link RuntimeError.eventName},
|
|
976
|
-
* {@link RuntimeError.eventPayload}, {@link HsmError.context}, and {@link HsmError.cause}
|
|
977
|
-
*
|
|
978
|
-
* @throws Rethrow to propagate; throwing here becomes {@link FatalError}
|
|
979
|
-
*
|
|
980
|
-
* @remarks
|
|
981
|
-
* Default {@link TopState} implementation rethrows. Override to log, transition to a safe
|
|
982
|
-
* state, or swallow. Uncaught errors invoke {@link Properties.dispatchErrorCallback}.
|
|
983
|
-
*/
|
|
984
|
-
onError<EventName extends keyof Protocol>(error: RuntimeError<Context, Protocol, EventName>): Promise<void> | void;
|
|
985
|
-
/**
|
|
986
|
-
* Recovery hook when dispatch would raise {@link UnhandledEventError}.
|
|
987
|
-
*
|
|
988
|
-
* @typeParam EventName - Correlated event key from `Protocol`
|
|
989
|
-
* @param error - Describes the unmatched event and active state
|
|
990
|
-
*
|
|
991
|
-
* @throws Default {@link TopState} implementation rethrows → may enter `onError`
|
|
992
|
-
*
|
|
993
|
-
* @remarks
|
|
994
|
-
* Override to implement catch-all handlers, auditing, or transition to an error state
|
|
995
|
-
* without defining every event on every leaf.
|
|
996
|
-
*/
|
|
997
|
-
onUnhandled<EventName extends keyof Protocol>(error: UnhandledEventError<Context, Protocol, EventName>): Promise<void> | void;
|
|
998
|
-
}
|
|
999
|
-
/**
|
|
1000
|
-
* Abstract root class for every state in the hierarchy.
|
|
1001
|
-
*
|
|
1002
|
-
* States are **never constructed directly** — the runtime binds one instance object whose
|
|
1003
|
-
* prototype moves along the class chain. Subclass `TopState` (or a child state), implement
|
|
1004
|
-
* your `Protocol` methods, and pass the root class to {@link makeHsm}.
|
|
1005
|
-
*
|
|
1006
|
-
* Forwards {@link State} / {@link Properties} APIs and default {@link StateEvents} behavior.
|
|
1007
|
-
*
|
|
1008
|
-
* @category State machine
|
|
1009
|
-
*/
|
|
1010
|
-
export declare abstract class TopState<Context = Any, Protocol extends {} | undefined = undefined, InternalProtocol extends {} = {}, Port = undefined> implements State<Context, Dispatch<Protocol, InternalProtocol>>, StateEvents<Context, Dispatch<Protocol, InternalProtocol>> {
|
|
1011
|
-
/** Domain context (injected by runtime — do not assign in constructors). */
|
|
1012
|
-
readonly ctx: Context;
|
|
1013
|
-
/** Handler view of the machine (`this` inside methods delegates here for core operations). */
|
|
1014
|
-
readonly hsm: State<Context, Dispatch<Protocol, InternalProtocol>>;
|
|
1015
|
-
/**
|
|
1016
|
-
* Phantom type carrier — **never exists at runtime** (`declare`d, never assigned). It makes a
|
|
1017
|
-
* `TopState` subclass the single configuration point for the four machine types, so helpers
|
|
1018
|
-
* like {@link MachineContext} / {@link MachineInternal} / {@link MachinePort} (and
|
|
1019
|
-
* {@link BasePort} / {@link testing!TestPort}) can derive everything from the root state alone.
|
|
1020
|
-
*
|
|
1021
|
-
* @internal
|
|
1022
|
-
*/
|
|
1023
|
-
readonly __ihsm: MachineTypes<Context, Protocol, InternalProtocol, Port>;
|
|
1024
|
-
constructor();
|
|
1025
|
-
/**
|
|
1026
|
-
* Outbound boundary — the `port` instance passed to {@link makeActor} / {@link testing!makeTestActor};
|
|
1027
|
-
* all impure I/O flows through here.
|
|
1028
|
-
*
|
|
1029
|
-
* Typed `undefined` for machines created without a port (the default), so existing
|
|
1030
|
-
* port-less machines are unaffected. At runtime a {@link Port} always backs such
|
|
1031
|
-
* machines — it is what {@link State.deferredPost} uses for its timer service.
|
|
1032
|
-
*/
|
|
1033
|
-
get port(): Port;
|
|
1034
|
-
/** @inheritdoc Properties.eventName */
|
|
1035
|
-
get eventName(): string;
|
|
1036
|
-
/** @inheritdoc Properties.eventPayload */
|
|
1037
|
-
get eventPayload(): any[];
|
|
1038
|
-
/** @inheritdoc Properties.traceHeader */
|
|
1039
|
-
get traceHeader(): string;
|
|
1040
|
-
/** @inheritdoc Properties.topState */
|
|
1041
|
-
get topState(): StateClass<Context, Dispatch<Protocol, InternalProtocol>>;
|
|
1042
|
-
/** @inheritdoc Properties.currentStateName */
|
|
1043
|
-
get currentStateName(): string;
|
|
1044
|
-
/** @inheritdoc Properties.currentState */
|
|
1045
|
-
get currentState(): StateClass<Context, Dispatch<Protocol, InternalProtocol>>;
|
|
1046
|
-
/** @inheritdoc Properties.ctxTypeName */
|
|
1047
|
-
get ctxTypeName(): string;
|
|
1048
|
-
/** @inheritdoc Properties.traceLevel */
|
|
1049
|
-
set traceLevel(value: TraceLevel);
|
|
1050
|
-
/** @inheritdoc Properties.traceLevel */
|
|
1051
|
-
get traceLevel(): TraceLevel;
|
|
1052
|
-
/** @inheritdoc Properties.topStateName */
|
|
1053
|
-
get topStateName(): string;
|
|
1054
|
-
/** @inheritdoc Properties.traceWriter */
|
|
1055
|
-
get traceWriter(): TraceWriter;
|
|
1056
|
-
/** @inheritdoc Properties.traceWriter */
|
|
1057
|
-
set traceWriter(value: TraceWriter);
|
|
1058
|
-
/** @inheritdoc Properties.dispatchErrorCallback */
|
|
1059
|
-
get dispatchErrorCallback(): DispatchErrorCallback<Context, Dispatch<Protocol, InternalProtocol>>;
|
|
1060
|
-
/** @inheritdoc Properties.dispatchErrorCallback */
|
|
1061
|
-
set dispatchErrorCallback(value: DispatchErrorCallback<Context, Dispatch<Protocol, InternalProtocol>>);
|
|
1062
|
-
/** @inheritdoc State.transition */
|
|
1063
|
-
transition(nextState: StateClass<Context, Dispatch<Protocol, InternalProtocol>>): void;
|
|
1064
|
-
/** @inheritdoc State.unhandled */
|
|
1065
|
-
unhandled(): never;
|
|
1066
|
-
/** @inheritdoc State.sleep */
|
|
1067
|
-
sleep(millis: number): Promise<void>;
|
|
1068
|
-
/** @inheritdoc Base.post */
|
|
1069
|
-
post<EventName extends keyof Dispatch<Protocol, InternalProtocol>>(eventName: PostedEvent<Dispatch<Protocol, InternalProtocol>, EventName>, ...eventPayload: EventPayload<Dispatch<Protocol, InternalProtocol>, EventName>): void;
|
|
1070
|
-
/** @inheritdoc State.deferredPost */
|
|
1071
|
-
deferredPost<EventName extends keyof Dispatch<Protocol, InternalProtocol>>(millis: number, eventName: PostedEvent<Dispatch<Protocol, InternalProtocol>, EventName>, ...eventPayload: EventPayload<Dispatch<Protocol, InternalProtocol>, EventName>): void;
|
|
1072
|
-
/** @inheritdoc State.postNow */
|
|
1073
|
-
postNow<EventName extends keyof Dispatch<Protocol, InternalProtocol>>(eventName: PostedEvent<Dispatch<Protocol, InternalProtocol>, EventName>, ...eventPayload: EventPayload<Dispatch<Protocol, InternalProtocol>, EventName>): void;
|
|
1074
|
-
/** @inheritdoc StateEvents.onExit */
|
|
1075
|
-
onExit(): Promise<void> | void;
|
|
1076
|
-
/** @inheritdoc StateEvents.onEntry */
|
|
1077
|
-
onEntry(): Promise<void> | void;
|
|
1078
|
-
/** @inheritdoc StateEvents.onError */
|
|
1079
|
-
onError<EventName extends keyof Dispatch<Protocol, InternalProtocol>>(error: RuntimeError<Context, Dispatch<Protocol, InternalProtocol>, EventName>): Promise<void> | void;
|
|
1080
|
-
/** @inheritdoc StateEvents.onUnhandled */
|
|
1081
|
-
onUnhandled<EventName extends keyof Dispatch<Protocol, InternalProtocol>>(error: UnhandledEventError<Context, Dispatch<Protocol, InternalProtocol>, EventName>): Promise<void> | void;
|
|
1082
|
-
}
|
|
1083
|
-
/**
|
|
1084
|
-
* Base class for all ihsm runtime errors carrying machine context.
|
|
1085
|
-
*
|
|
1086
|
-
* @typeParam Context - Domain context at failure time
|
|
1087
|
-
* @typeParam Protocol - Vocabulary type (for typed subclasses)
|
|
1088
|
-
*
|
|
1089
|
-
* @category Error
|
|
1090
|
-
*/
|
|
1091
|
-
export declare abstract class HsmError<Context, Protocol extends {} | undefined> extends Error {
|
|
1092
|
-
/** Discriminator matching the class name (`EventHandlerError`, etc.). */
|
|
1093
|
-
name: string;
|
|
1094
|
-
/** {@link Properties.topStateName} when the error was constructed. */
|
|
1095
|
-
topStateName: string;
|
|
1096
|
-
/** {@link Properties.currentStateName} when the error was constructed. */
|
|
1097
|
-
stateName: string;
|
|
1098
|
-
/** Snapshot of {@link State.ctx} when the error was constructed. */
|
|
1099
|
-
context: Context;
|
|
1100
|
-
/** Original thrown value when this error wraps a handler or lifecycle failure. */
|
|
1101
|
-
cause?: Error;
|
|
1102
|
-
protected constructor(name: string, hsm: State<Context, Protocol>, message: string, cause?: Error);
|
|
1103
|
-
}
|
|
1104
|
-
/**
|
|
1105
|
-
* Error base for failures during **event dispatch**, with correlated event metadata.
|
|
1106
|
-
*
|
|
1107
|
-
* @typeParam Context - Domain context
|
|
1108
|
-
* @typeParam Protocol - Vocabulary interface
|
|
1109
|
-
* @typeParam EventName - Event or service key being processed
|
|
1110
|
-
*
|
|
1111
|
-
* @category Error
|
|
1112
|
-
*/
|
|
1113
|
-
export declare abstract class RuntimeError<Context, Protocol extends {} | undefined, EventName extends keyof Protocol> extends HsmError<Context, Protocol> {
|
|
1114
|
-
/** Event or service name that was active when the failure occurred. */
|
|
1115
|
-
eventName: PostedEvent<Protocol, EventName>;
|
|
1116
|
-
/** Client-supplied arguments (excluding resolve/reject for services). */
|
|
1117
|
-
eventPayload: EventPayload<Protocol, EventName>;
|
|
1118
|
-
protected constructor(errorName: string, hsm: State<Context, Protocol>, message: string, cause?: Error);
|
|
1119
|
-
}
|
|
1120
|
-
/**
|
|
1121
|
-
* Thrown when {@link StateEvents.onExit} or {@link StateEvents.onEntry} throws during a transition.
|
|
1122
|
-
*
|
|
1123
|
-
* @category Error
|
|
1124
|
-
*/
|
|
1125
|
-
export declare class TransitionError<Context, Protocol extends {} | undefined, EventName extends keyof Protocol> extends RuntimeError<Context, Protocol, EventName> {
|
|
1126
|
-
failedStateName: string;
|
|
1127
|
-
failedCallback: 'onExit' | 'onEntry';
|
|
1128
|
-
fromStateName: string;
|
|
1129
|
-
toStateName: string;
|
|
1130
|
-
/**
|
|
1131
|
-
* @param hsm - Machine view at failure time
|
|
1132
|
-
* @param cause - Error thrown from the lifecycle hook
|
|
1133
|
-
* @param failedStateName - Display name of the state whose hook failed
|
|
1134
|
-
* @param failedCallback - Which hook failed (`onExit` or `onEntry`)
|
|
1135
|
-
* @param fromStateName - Leaf state before the transition
|
|
1136
|
-
* @param toStateName - Requested destination state
|
|
1137
|
-
*/
|
|
1138
|
-
constructor(hsm: State<Context, Protocol>, cause: Error, failedStateName: string, failedCallback: 'onExit' | 'onEntry', fromStateName: string, toStateName: string);
|
|
1139
|
-
}
|
|
1140
|
-
/**
|
|
1141
|
-
* Thrown when an event handler body throws and {@link StateEvents.onError} does not recover.
|
|
1142
|
-
*
|
|
1143
|
-
* @category Error
|
|
1144
|
-
*/
|
|
1145
|
-
export declare class EventHandlerError<Context, Protocol extends {} | undefined, EventName extends keyof Protocol> extends RuntimeError<Context, Protocol, EventName> {
|
|
1146
|
-
/**
|
|
1147
|
-
* @param hsm - Machine view with {@link eventName} set to the failing handler
|
|
1148
|
-
* @param cause - Error thrown from handler code
|
|
1149
|
-
*/
|
|
1150
|
-
constructor(hsm: State<Context, Protocol>, cause: Error);
|
|
1151
|
-
}
|
|
1152
|
-
/**
|
|
1153
|
-
* Thrown when no handler matches the dispatched event and {@link StateEvents.onUnhandled} rethrows.
|
|
1154
|
-
*
|
|
1155
|
-
* @category Error
|
|
1156
|
-
*/
|
|
1157
|
-
export declare class UnhandledEventError<Context, Protocol extends {} | undefined, EventName extends keyof Protocol> extends RuntimeError<Context, Protocol, EventName> {
|
|
1158
|
-
/** @param hsm - Machine view with the unmatched {@link eventName} */
|
|
1159
|
-
constructor(hsm: State<Context, Protocol>);
|
|
1160
|
-
}
|
|
1161
|
-
/**
|
|
1162
|
-
* Thrown at **class definition** time when {@link InitialState} is applied twice to one parent.
|
|
1163
|
-
*
|
|
1164
|
-
* @category Error
|
|
1165
|
-
*/
|
|
1166
|
-
export declare class InitialStateError<Context, Protocol extends {} | undefined> extends Error {
|
|
1167
|
-
/** Display name of the state passed to the duplicate {@link InitialState} call. */
|
|
1168
|
-
targetStateName: string;
|
|
1169
|
-
/** @param targetState - The state class whose parent already has an initial substate */
|
|
1170
|
-
constructor(targetState: StateClass<Context, Protocol>);
|
|
1171
|
-
}
|
|
1172
|
-
/**
|
|
1173
|
-
* Thrown when {@link StateEvents.onError} itself throws, leaving the machine unrecoverable.
|
|
1174
|
-
*
|
|
1175
|
-
* @category Error
|
|
1176
|
-
*/
|
|
1177
|
-
export declare class FatalError<Context, Protocol extends {} | undefined, EventName extends keyof Protocol> extends RuntimeError<Context, Protocol, EventName> {
|
|
1178
|
-
/**
|
|
1179
|
-
* @param hsm - Machine view at failure time
|
|
1180
|
-
* @param cause - Error thrown from `onError`
|
|
1181
|
-
*/
|
|
1182
|
-
constructor(hsm: State<Context, Protocol>, cause: Error);
|
|
1183
|
-
}
|
|
1184
|
-
/**
|
|
1185
|
-
* Thrown when {@link StateEvents.onEntry} fails during the initial `@InitialState` walk at startup.
|
|
1186
|
-
*
|
|
1187
|
-
* @category Error
|
|
1188
|
-
*/
|
|
1189
|
-
export declare class InitializationError<Context, Protocol extends {} | undefined> extends HsmError<Context, Protocol> {
|
|
1190
|
-
failedState: StateClass<Context, Protocol>;
|
|
1191
|
-
/**
|
|
1192
|
-
* @param hsm - Partially initialized machine
|
|
1193
|
-
* @param failedState - State class whose `onEntry` threw
|
|
1194
|
-
* @param cause - Original error from `onEntry`
|
|
1195
|
-
*/
|
|
1196
|
-
constructor(hsm: State<Context, Protocol>, failedState: StateClass<Context, Protocol>, cause: Error);
|
|
1197
|
-
}
|
|
1198
|
-
/**
|
|
1199
|
-
* Terminal sink state class used when the runtime cannot recover from an error.
|
|
1200
|
-
*
|
|
1201
|
-
* Assign or transition here from custom {@link StateEvents.onError} handlers when you need a
|
|
1202
|
-
* well-defined quiescent state. Display name is pre-registered as `'FatalErrorState'`.
|
|
1203
|
-
*
|
|
1204
|
-
* @category State machine
|
|
1205
|
-
*/
|
|
1206
|
-
export declare class FatalErrorState<Context, Protocol extends {} | undefined> extends TopState<Context, Protocol> {
|
|
1207
|
-
}
|
|
1208
|
-
/**
|
|
1209
|
-
* Declares `TargetState` as the **initial substate** of its direct parent composite.
|
|
1210
|
-
*
|
|
1211
|
-
* Apply as a TypeScript decorator or call as a function at class definition time. Exactly **one**
|
|
1212
|
-
* initial child is allowed per parent; a second mark throws {@link InitialStateError}.
|
|
1213
|
-
*
|
|
1214
|
-
* @typeParam Context - Domain context type
|
|
1215
|
-
* @typeParam Protocol - Vocabulary interface
|
|
1216
|
-
* @param TargetState - Child state class whose **parent** is `Object.getPrototypeOf(TargetState.prototype).constructor`
|
|
1217
|
-
*
|
|
1218
|
-
* @throws {@link InitialStateError} when the parent already has an initial substate
|
|
1219
|
-
*
|
|
1220
|
-
* @remarks
|
|
1221
|
-
* During {@link makeHsm} initialization, the runtime descends `@InitialState` chains from the
|
|
1222
|
-
* root until the deepest initial leaf is active, running {@link StateEvents.onEntry} along the path.
|
|
1223
|
-
*
|
|
1224
|
-
* @example
|
|
1225
|
-
* ```ts
|
|
1226
|
-
* class Composite extends TopState {}
|
|
1227
|
-
*
|
|
1228
|
-
* @InitialState
|
|
1229
|
-
* class Idle extends Composite {}
|
|
1230
|
-
* ```
|
|
1231
|
-
*/
|
|
1232
|
-
export declare function InitialState<Context, Protocol extends {} | undefined>(TargetState: StateClass<Context, Protocol>): void;
|
|
1233
|
-
/**
|
|
1234
|
-
* Assigns a stable **display name** to a single state class.
|
|
1235
|
-
*
|
|
1236
|
-
* Minifiers rewrite `Class.name` in production browser bundles; explicit registration keeps
|
|
1237
|
-
* {@link Properties.currentStateName}, trace output, and error messages readable everywhere.
|
|
1238
|
-
*
|
|
1239
|
-
* @typeParam Context - Domain context type
|
|
1240
|
-
* @typeParam Protocol - Vocabulary interface
|
|
1241
|
-
* @param state - State class constructor to tag
|
|
1242
|
-
* @param name - Non-empty display string used in traces and errors (not required to match `Class.name`)
|
|
1243
|
-
*
|
|
1244
|
-
* @remarks
|
|
1245
|
-
* Stored as a non-enumerable own property — never inherited by subclasses from the prototype chain.
|
|
1246
|
-
* {@link registerStateNames} is preferred when every state is a named export from one module.
|
|
1247
|
-
*
|
|
1248
|
-
* @example
|
|
1249
|
-
* ```ts
|
|
1250
|
-
* class Door extends TopState {}
|
|
1251
|
-
* defineStateName(Door, 'Door');
|
|
1252
|
-
* ```
|
|
1253
|
-
*
|
|
1254
|
-
* @category State machine
|
|
1255
|
-
*/
|
|
1256
|
-
export declare function defineStateName<Context, Protocol extends {} | undefined>(state: StateClass<Context, Protocol>, name: string): void;
|
|
1257
|
-
/**
|
|
1258
|
-
* Registers display names for **every** state class in an exports object, using each **export key** as the name.
|
|
1259
|
-
*
|
|
1260
|
-
* @param exports - Module namespace (`import * as machine`) or object literal of state classes.
|
|
1261
|
-
* Non-constructor exports (constants, functions, types) are silently skipped
|
|
1262
|
-
*
|
|
1263
|
-
* @remarks
|
|
1264
|
-
* Export keys survive minification even when class identifiers are mangled — this is the recommended
|
|
1265
|
-
* approach for browser bundles without `keep_classnames`. Call once at module load after all state
|
|
1266
|
-
* classes are defined.
|
|
1267
|
-
*
|
|
1268
|
-
* @example Single module
|
|
1269
|
-
* ```ts
|
|
1270
|
-
* export class DoorTop extends TopState {}
|
|
1271
|
-
* export class Open extends DoorTop {}
|
|
1272
|
-
* export class Closed extends DoorTop {}
|
|
1273
|
-
* registerStateNames({ DoorTop, Open, Closed });
|
|
1274
|
-
* ```
|
|
1275
|
-
*
|
|
1276
|
-
* @example Re-exporting namespace
|
|
1277
|
-
* ```ts
|
|
1278
|
-
* import * as machine from './machine';
|
|
1279
|
-
* registerStateNames(machine);
|
|
1280
|
-
* ```
|
|
1281
|
-
*
|
|
1282
|
-
* @category State machine
|
|
1283
|
-
*/
|
|
1284
|
-
export declare function registerStateNames(exports: Record<string, unknown>): void;
|
|
1285
|
-
/** @internal — shared by the core factories and (via re-export) the `ihsm/testing` factories. */
|
|
1286
|
-
export declare function defaultDispatchErrorCallback<Context, Protocol extends {} | undefined>(hsm: Base<Context, Protocol>, err: Error): void;
|
|
1287
|
-
/** @internal */
|
|
1288
|
-
export declare const defaultTraceWriter: TraceWriter;
|
|
1289
|
-
/** @internal */
|
|
1290
|
-
export declare const defaultInitialize = true;
|
|
1291
|
-
/**
|
|
1292
|
-
* Creates and optionally initializes a hierarchical state machine **actor** bound to `ctx`.
|
|
1293
|
-
*
|
|
1294
|
-
* The returned {@link Hsm} is the single runtime object: external clients call `post` / `call` /
|
|
1295
|
-
* `sync`; the active state is the instance prototype chain updated by {@link State.transition}.
|
|
1296
|
-
*
|
|
1297
|
-
* @typeParam Context - Domain context type (inferred from `ctx` when passed inline)
|
|
1298
|
-
* @typeParam Protocol - Event/service vocabulary (inferred from `topState` when it implements `Protocol`)
|
|
1299
|
-
* @param topState - Root state **class** constructor (must extend {@link TopState})
|
|
1300
|
-
* @param ctx - Mutable domain object shared by all states; stored on the instance as {@link Hsm.ctx}
|
|
1301
|
-
* @param initialize - When `true` (default), enqueue the initial walk: descend `@InitialState`
|
|
1302
|
-
* chains from `topState` and run {@link StateEvents.onEntry} on each entered state until the
|
|
1303
|
-
* initial leaf is active. When `false`, prototype starts at `topState` with **no** entry hooks
|
|
1304
|
-
* @param traceLevel - Initial {@link TraceLevel} (default {@link TraceLevel.DEBUG})
|
|
1305
|
-
* @param traceWriter - {@link TraceWriter} implementation (default: prefixes strings with state name and logs to `console`)
|
|
1306
|
-
* @param dispatchErrorCallback - Last-resort error hook (default: trace + rethrow)
|
|
1307
|
-
* @returns Client handle implementing {@link Hsm} for the same `Context` and `Protocol`
|
|
1308
|
-
*
|
|
1309
|
-
* @remarks
|
|
1310
|
-
* - Await {@link Hsm.sync} after creation when `initialize: true` before asserting initial state
|
|
1311
|
-
* - Zero runtime npm dependencies; safe to embed in browsers when state names are registered
|
|
1312
|
-
* - Transition LCA paths are cached per machine instance for the lifetime of the actor
|
|
1313
|
-
*
|
|
1314
|
-
* @example Minimal door machine
|
|
1315
|
-
* ```ts
|
|
1316
|
-
* const door = makeHsm(DoorTop, { openCount: 0 });
|
|
1317
|
-
* await door.sync();
|
|
1318
|
-
* door.post('open');
|
|
1319
|
-
* await door.sync();
|
|
1320
|
-
* ```
|
|
1321
|
-
*
|
|
1322
|
-
* @example Custom tracing in tests
|
|
1323
|
-
* ```ts
|
|
1324
|
-
* const writer = { write: (_hsm, msg) => logs.push(msg) };
|
|
1325
|
-
* const sm = makeHsm(Top, ctx, true, TraceLevel.VERBOSE_DEBUG, writer);
|
|
1326
|
-
* await sm.sync();
|
|
1327
|
-
* ```
|
|
1328
|
-
*
|
|
1329
|
-
* @category Factory
|
|
1330
|
-
*/
|
|
1331
|
-
export declare function makeHsm<Context, Protocol extends undefined | {}>(topState: StateClass<Context, Protocol>, ctx: Context, initialize?: boolean, traceLevel?: TraceLevel, traceWriter?: TraceWriter, dispatchErrorCallback?: DispatchErrorCallback<Context, Protocol>, port?: PortHandle<Context, Protocol>): Hsm<Context, Protocol>;
|
|
1332
|
-
/**
|
|
1333
|
-
* The constrained root-state argument shared by {@link makeActor} / {@link testing!makeTestActor}.
|
|
1334
|
-
*
|
|
1335
|
-
* Its prototype carries the {@link MachineTypes} marker, so `Context`, `Public`, and `Internal` are
|
|
1336
|
-
* **inferred from the `topState`** at the call site — you never pass those generics explicitly.
|
|
1337
|
-
*
|
|
1338
|
-
* @typeParam Context - Domain context type
|
|
1339
|
-
* @typeParam Public - Public, client-callable protocol
|
|
1340
|
-
* @typeParam Internal - Internal protocol — only postable by the port / handlers
|
|
1341
|
-
*
|
|
1342
|
-
* @category Factory
|
|
1343
|
-
*/
|
|
1344
|
-
export type TopStateArg<Context, Public extends undefined | {}, Internal extends {}> = StateClass<Context, Dispatch<Public, Internal>> & {
|
|
1345
|
-
readonly prototype: {
|
|
1346
|
-
readonly __ihsm: MachineTypes<Context, Public, Internal, unknown>;
|
|
1347
|
-
};
|
|
1348
|
-
};
|
|
1349
|
-
/**
|
|
1350
|
-
* Optional tuning passed as the **last** argument to {@link makeActor} / {@link testing!makeTestActor},
|
|
1351
|
-
* after the three mandatory positional arguments (`topState`, `ctx`, `port`). Every field has a
|
|
1352
|
-
* sensible default; omit the whole object to take them all.
|
|
1353
|
-
*
|
|
1354
|
-
* @typeParam Context - Domain context type
|
|
1355
|
-
* @typeParam Public - Public protocol
|
|
1356
|
-
* @typeParam Internal - Internal protocol
|
|
1357
|
-
*
|
|
1358
|
-
* @category Factory
|
|
1359
|
-
*/
|
|
1360
|
-
export interface ActorOptions<Context, Public extends undefined | {}, Internal extends {} = {}> {
|
|
1361
|
-
/** Run the initial `@InitialState` walk (default `true`). */
|
|
1362
|
-
initialize?: boolean;
|
|
1363
|
-
/** Initial {@link TraceLevel} (default {@link TraceLevel.DEBUG}). */
|
|
1364
|
-
traceLevel?: TraceLevel;
|
|
1365
|
-
/** {@link TraceWriter} implementation (default: prefixes with state name, logs to `console`). */
|
|
1366
|
-
traceWriter?: TraceWriter;
|
|
1367
|
-
/** Last-resort error hook (default: trace + rethrow). */
|
|
1368
|
-
dispatchErrorCallback?: DispatchErrorCallback<Context, Dispatch<Public, Internal>>;
|
|
1369
|
-
}
|
|
1370
|
-
/**
|
|
1371
|
-
* Creates an actor exposing only its **public** protocol, with an optional outbound {@link Port}.
|
|
1372
|
-
*
|
|
1373
|
-
* Like {@link makeHsm} but separates the public, client-callable protocol from an
|
|
1374
|
-
* `InternalProtocol` that only the port may post inward. The returned {@link Actor} surfaces
|
|
1375
|
-
* just the public events; handlers (and the port) may post the merged {@link Dispatch} protocol.
|
|
1376
|
-
*
|
|
1377
|
-
* The trailing `Disjoint` guard is a compile-time gate: if `Public` and `Internal` share an event
|
|
1378
|
-
* name, the call fails to type-check, pointing at the overlapping keys.
|
|
1379
|
-
*
|
|
1380
|
-
* @typeParam Context - Domain context type
|
|
1381
|
-
* @typeParam Public - Public, client-callable protocol
|
|
1382
|
-
* @typeParam Internal - Internal protocol — only postable by the port / handlers
|
|
1383
|
-
* @typeParam P - Concrete {@link Port} type assigned to `this.port`
|
|
1384
|
-
* @param topState - Root state class; `Context` / `Public` / `Internal` are inferred from it (see {@link TopStateArg})
|
|
1385
|
-
* @param ctx - Mutable domain object shared by all states
|
|
1386
|
-
* @param port - Outbound port instance (its `actor` is bound by the factory; use {@link Port} if none)
|
|
1387
|
-
* @param options - Optional tuning: `initialize` / `traceLevel` / `traceWriter` / … (see {@link ActorOptions})
|
|
1388
|
-
* @returns A public-only {@link Actor} handle
|
|
1389
|
-
*
|
|
1390
|
-
* @example
|
|
1391
|
-
* ```ts
|
|
1392
|
-
* const conn = makeActor(ConnTop, new ConnCtx(), port, { traceLevel: TraceLevel.PRODUCTION });
|
|
1393
|
-
* ```
|
|
1394
|
-
*
|
|
1395
|
-
* @category Factory
|
|
4
|
+
* Curated public runtime surface; protocol types live in {@link ./types}.
|
|
1396
5
|
*/
|
|
1397
|
-
export
|
|
6
|
+
export { TraceLevel, asError, quoteUnknown, quoteError, getInitialState, hasInitialState, getTransitionKey, defineStateName, getStateName, Port, RequestingPort, TopState, HsmError, RuntimeError, TransitionError, EventHandlerError, UnhandledEventError, InitialStateError, FatalError, InitializationError, FatalErrorState, InitialState, registerStateNames, ProtocolCollisionError, ReservedNames, buildProtocolIndex, CallTimeoutError, SelfCallDeadlockError, TransitionTableError, planTransitionClasses, executeTransitionRoutine, createTransitionTracer, transitionTraceLines, RuntimeTransitionResolver, isRequestingPort, isServiceCallOptions, splitServiceArgs, serviceCallWithTimeout, defaultTraceWriter, defaultInitialize, defaultDispatchErrorCallback, makeActor, asParentActor, makeChildActor, kHandlerMachine, kParentLink, } from './internal/runtime';
|
|
7
|
+
export type * from './types';
|
|
8
|
+
export type { Any, DispatchErrorCallback, Disposable, EventObserver, MachinePortInput, IPort, Properties, RandomService, TimerService, DomainPortOf, PortServices, ResultWithSubscription, StateClass, StateEvents, TraceWriter, TracedMessage } from './internal/types';
|