@defold-typescript/types 0.47.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hsm/debug.d.ts +6 -3
- package/hsm/index.d.ts +409 -157
- package/package.json +1 -1
package/hsm/debug.d.ts
CHANGED
|
@@ -14,8 +14,11 @@ export interface MachineInspector {
|
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Prints one line when it is called, then one line for each move `instance` makes, each starting
|
|
17
|
-
* with `hsm` and the label.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* with `hsm` and the label. A move's line names the state whose rule made it and that rule's key.
|
|
18
|
+
* An event no active state takes prints an `unhandled` line with its `type` and the active
|
|
19
|
+
* leaves, once per type until the next move. Call it once after `start`. A label another
|
|
20
|
+
* `inspect` call already used gets an ordinal, such as `enemy#2`. In a release build it registers
|
|
21
|
+
* nothing and returns an inspector whose `draw` does nothing, so the calls can stay in shipped
|
|
22
|
+
* code.
|
|
20
23
|
*/
|
|
21
24
|
export declare function inspect<Ctx, E extends EventObject, P extends string>(instance: MachineInstance<Ctx, E, P>, label: string): MachineInspector;
|
package/hsm/index.d.ts
CHANGED
|
@@ -16,18 +16,26 @@ export type MoveCause = "event" | "after" | "update" | "stop" | "reload" | "alwa
|
|
|
16
16
|
/**
|
|
17
17
|
* A listener given to `onMove`. It gets the leaf left, the leaf entered (`undefined` after
|
|
18
18
|
* `stop()`), the cause, and the event, which is `undefined` unless the cause is `event`. It runs
|
|
19
|
-
* after the move's `enter` hooks.
|
|
19
|
+
* after the move's `enter` hooks. The event is read-only: the listener cannot assign to its
|
|
20
|
+
* fields.
|
|
20
21
|
* @noSelf
|
|
21
22
|
*/
|
|
22
|
-
export type MoveListener<E extends EventObject, P extends string = string> = (from: P, to: P | undefined, cause: MoveCause, event: E | undefined) => void;
|
|
23
|
+
export type MoveListener<E extends EventObject, P extends string = string> = (from: P, to: P | undefined, cause: MoveCause, event: Readonly<E> | undefined) => void;
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
25
|
+
* A listener given to `onUnhandled`. It gets the event no rule of an active state took. The
|
|
26
|
+
* event is read-only: the listener cannot assign to its fields.
|
|
26
27
|
* @noSelf
|
|
27
28
|
*/
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
export type UnhandledListener<E extends EventObject> = (event: Readonly<E>) => void;
|
|
30
|
+
/**
|
|
31
|
+
* What a `when` sees of the running machine: which states are active, and nothing that changes
|
|
32
|
+
* it. `P` is the union of the paths it takes. The `machine` a `when` receives takes the machine's
|
|
33
|
+
* state paths, and also any path below a state that holds only callbacks with unannotated
|
|
34
|
+
* parameters and `after` or `every` lists with one in them, or whose child states all do. `Tag`
|
|
35
|
+
* is the union of the machine's tag names.
|
|
36
|
+
* @noSelf
|
|
37
|
+
*/
|
|
38
|
+
export interface MachineView<P extends string = string, Tag extends string = never> {
|
|
31
39
|
/**
|
|
32
40
|
* The full path of the deepest active state, or `undefined` once stopped. In a parallel state
|
|
33
41
|
* it is the first region's leaf.
|
|
@@ -39,16 +47,45 @@ export interface MachineInstance<Ctx, E extends EventObject, P extends string =
|
|
|
39
47
|
*/
|
|
40
48
|
readonly leaves: readonly P[];
|
|
41
49
|
/** Whether the state at the full path `path` is active. Ancestors of the current state count. */
|
|
42
|
-
readonly matches:
|
|
50
|
+
readonly matches: {
|
|
51
|
+
matches(this: void, path: P): boolean;
|
|
52
|
+
}["matches"];
|
|
53
|
+
/**
|
|
54
|
+
* Whether an active state carries `tag`. A parent's tag holds while any of its children is
|
|
55
|
+
* active, and a tag in any region of a parallel state counts. The answer is `false` for a name
|
|
56
|
+
* no state carries, and once stopped.
|
|
57
|
+
*/
|
|
58
|
+
readonly hasTag: (tag: Tag) => boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* One running machine, returned by `start`. `P` is the union of the paths it takes: the
|
|
62
|
+
* machine's state paths on a started instance. The `machine` a hook, a `run` or a `task`
|
|
63
|
+
* receives takes those paths, and also any path below a state that holds only callbacks with
|
|
64
|
+
* unannotated parameters and `after` or `every` lists with one in them, or whose child states all
|
|
65
|
+
* do. `Tag` is the union of the machine's tag names.
|
|
66
|
+
* @noSelf
|
|
67
|
+
*/
|
|
68
|
+
export interface MachineInstance<Ctx, E extends EventObject, P extends string = string, Tag extends string = never> extends MachineView<P, Tag> {
|
|
69
|
+
/** The context passed to `start`. */
|
|
70
|
+
readonly ctx: Ctx;
|
|
71
|
+
/**
|
|
72
|
+
* Whether a rule of an active state would take `event` if it were sent at that moment. The
|
|
73
|
+
* answer is for that one event, payload included: a rule's `when` reads it. A rule with no
|
|
74
|
+
* `to` counts. No hook, `run` or listener runs and nothing is queued, and once the machine is
|
|
75
|
+
* stopped the answer is `false`.
|
|
76
|
+
*/
|
|
77
|
+
readonly can: (event: E) => boolean;
|
|
43
78
|
/**
|
|
44
79
|
* Handles `event`, or queues it when called during a step. An event no active state handles is
|
|
45
|
-
* dropped
|
|
80
|
+
* dropped: it runs the active states' `unhandled` hooks and is then reported to the
|
|
81
|
+
* `onUnhandled` listeners. Once the machine is stopped the call does nothing.
|
|
46
82
|
*/
|
|
47
83
|
readonly send: (event: E) => void;
|
|
48
84
|
/**
|
|
49
|
-
* Advances the machine by `dt` seconds: runs the `update` hooks, then
|
|
50
|
-
*
|
|
51
|
-
* is
|
|
85
|
+
* Advances the machine by `dt` seconds: runs the `update` hooks, then, in every region no hook
|
|
86
|
+
* moved, each state's `every` entries and after them its `after` timers. A machine with no
|
|
87
|
+
* parallel state is one region. A machine whose `update` is not called is frozen in time, and
|
|
88
|
+
* once the machine is stopped the call does nothing.
|
|
52
89
|
*/
|
|
53
90
|
readonly update: (dt: number) => void;
|
|
54
91
|
/**
|
|
@@ -63,12 +100,28 @@ export interface MachineInstance<Ctx, E extends EventObject, P extends string =
|
|
|
63
100
|
* function removes that one.
|
|
64
101
|
*/
|
|
65
102
|
readonly onMove: (listener: MoveListener<E, P>) => () => void;
|
|
103
|
+
/**
|
|
104
|
+
* Adds a listener called with each event the machine processes that no rule of an active state
|
|
105
|
+
* takes. It runs inside the step, after the active states' `unhandled` hooks and before the
|
|
106
|
+
* next queued event. A rule with no `to` takes its event. Each call adds a listener, and the
|
|
107
|
+
* returned function removes that one.
|
|
108
|
+
*/
|
|
109
|
+
readonly onUnhandled: (listener: UnhandledListener<E>) => () => void;
|
|
66
110
|
}
|
|
67
111
|
/**
|
|
68
112
|
* A function a rule's `run` calls during the move, after the `exit` hooks and before the `enter`
|
|
69
|
-
* hooks. Its `event` is the variant the `on` key names
|
|
113
|
+
* hooks. Its `event` is the variant the `on` key names, and is read-only: a `run` changes `ctx`
|
|
114
|
+
* and cannot assign to a field of the event. `Hook` is the union of the paths its `machine`
|
|
115
|
+
* takes: the machine's paths for a `run` written in the config, and any `string` when left out.
|
|
70
116
|
*/
|
|
71
|
-
export type MoveAction<Ctx, E extends EventObject, V extends E = E> = (ctx: Ctx, event: V
|
|
117
|
+
export type MoveAction<Ctx, E extends EventObject, V extends E = E, Tag extends string = never, Hook extends string = string> = (ctx: Ctx, event: Readonly<V>, machine: MachineInstance<Ctx, E, Hook, Tag>) => void;
|
|
118
|
+
/**
|
|
119
|
+
* A function a `run` calls where there is no event: under `after`, `always` and `every`. `Hook`
|
|
120
|
+
* is the union of the paths its `machine` takes: the machine's paths for a `run` written in the
|
|
121
|
+
* config, and any `string` when left out.
|
|
122
|
+
*/
|
|
123
|
+
export type RunAction<Ctx, E extends EventObject, Tag extends string = never, Hook extends string = string> = (ctx: Ctx, machine: MachineInstance<Ctx, E, Hook, Tag>) => void;
|
|
124
|
+
type RuleRun<Ctx, E extends EventObject, Tag extends string = never, Hook extends string = string> = [E] extends [never] ? never : RunAction<Ctx, E, Tag, Hook> | readonly RunAction<Ctx, E, Tag, Hook>[];
|
|
72
125
|
interface RuleList<Rule> {
|
|
73
126
|
readonly 0: Rule;
|
|
74
127
|
readonly [index: number]: Rule;
|
|
@@ -79,7 +132,7 @@ interface RuleList<Rule> {
|
|
|
79
132
|
* no `to` runs its `run` and nothing else: no state exits or enters.
|
|
80
133
|
* @noSelf
|
|
81
134
|
*/
|
|
82
|
-
export interface TransitionConfig<Ctx, E extends EventObject, V extends E = E, To extends string = string> {
|
|
135
|
+
export interface TransitionConfig<Ctx, E extends EventObject, V extends E = E, To extends string = string, Tag extends string = never, Hook extends string = string> {
|
|
83
136
|
/**
|
|
84
137
|
* The full path of the state to move to. A `to` naming the state the rule is on exits and
|
|
85
138
|
* re-enters it, restarting its timers and `task`; a `to` below it keeps it active.
|
|
@@ -88,15 +141,18 @@ export interface TransitionConfig<Ctx, E extends EventObject, V extends E = E, T
|
|
|
88
141
|
/**
|
|
89
142
|
* The rule is taken only when this returns `true`. It must have no side effects: it is also
|
|
90
143
|
* checked when the move does not happen, in guarded lists and on every `always` recheck.
|
|
144
|
+
* `ctx` and `event` are read-only here, one level deep: a write belongs in `run`.
|
|
145
|
+
* `machine` reads the active states; inside a parallel state it shows the regions that already
|
|
146
|
+
* took the event.
|
|
91
147
|
*/
|
|
92
|
-
readonly when?: (ctx: Ctx
|
|
148
|
+
readonly when?: (ctx: Readonly<Ctx>, event: Readonly<V>, machine: MachineView<Hook, Tag>) => boolean;
|
|
93
149
|
/**
|
|
94
150
|
* Runs during the move. The event is accepted when its key matches and `when` passes; then
|
|
95
151
|
* `exit` runs on each state left, deepest first; then `run`; then `enter` on each state
|
|
96
152
|
* entered, outermost first. Shared ancestors neither exit nor enter. A rule with no `to` runs
|
|
97
153
|
* only `run`.
|
|
98
154
|
*/
|
|
99
|
-
readonly run?: MoveAction<Ctx, E, V> | readonly MoveAction<Ctx, E, V>[];
|
|
155
|
+
readonly run?: MoveAction<Ctx, E, V, Tag, Hook> | readonly MoveAction<Ctx, E, V, Tag, Hook>[];
|
|
100
156
|
/** Renamed to `to`. */
|
|
101
157
|
readonly target?: never;
|
|
102
158
|
/** Renamed to `when`. */
|
|
@@ -110,20 +166,27 @@ export interface TransitionConfig<Ctx, E extends EventObject, V extends E = E, T
|
|
|
110
166
|
* What an `on` key maps to: a target path, a `TransitionConfig`, or a non-empty list of them
|
|
111
167
|
* tried in order. The first rule with no `when`, or whose `when` returns `true`, is taken.
|
|
112
168
|
*/
|
|
113
|
-
export type TransitionSpec<Ctx, E extends EventObject, V extends E = E, To extends string = string> = To | TransitionConfig<Ctx, E, V, To> | RuleList<TransitionConfig<Ctx, E, V, To>>;
|
|
169
|
+
export type TransitionSpec<Ctx, E extends EventObject, V extends E = E, To extends string = string, Tag extends string = never, Hook extends string = string> = To | TransitionConfig<Ctx, E, V, To, Tag, Hook> | RuleList<TransitionConfig<Ctx, E, V, To, Tag, Hook>>;
|
|
114
170
|
/**
|
|
115
|
-
* The object form of an `always` rule: a `to` and an optional `
|
|
116
|
-
* no `
|
|
171
|
+
* The object form of an `always` rule: a `to`, an optional `when` and an optional `run`. It gets
|
|
172
|
+
* no event. `E` is the machine's event type; a rule typed without it has no `run`. `Hook` is
|
|
173
|
+
* the union of the paths the `machine` of its `when` and `run` takes: any `string` when left out.
|
|
117
174
|
* @noSelf
|
|
118
175
|
*/
|
|
119
|
-
export interface AlwaysConfig<Ctx, To extends string = string> {
|
|
176
|
+
export interface AlwaysConfig<Ctx, To extends string = string, E extends EventObject = never, Tag extends string = never, Hook extends string = string> {
|
|
120
177
|
/**
|
|
121
178
|
* The full path of the state to move to. An `always` rule without one throws when the machine
|
|
122
179
|
* is defined.
|
|
123
180
|
*/
|
|
124
181
|
readonly to: To;
|
|
125
|
-
/**
|
|
126
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Checked on every recheck, so it must have no side effects. `ctx` is read-only here, one
|
|
184
|
+
* level deep: a write belongs in `run`. `machine` reads the active states; inside a parallel
|
|
185
|
+
* state it shows the regions that already moved.
|
|
186
|
+
*/
|
|
187
|
+
readonly when?: (ctx: Readonly<Ctx>, machine: MachineView<Hook, Tag>) => boolean;
|
|
188
|
+
/** Runs between the `exit` and `enter` hooks of the move, only when the rule is taken. */
|
|
189
|
+
readonly run?: RuleRun<Ctx, E, Tag, Hook>;
|
|
127
190
|
/** Renamed to `to`. */
|
|
128
191
|
readonly target?: never;
|
|
129
192
|
/** Renamed to `when`. */
|
|
@@ -133,46 +196,149 @@ export interface AlwaysConfig<Ctx, To extends string = string> {
|
|
|
133
196
|
* What `always` takes: a target path, an `AlwaysConfig`, or a non-empty list of them. The first
|
|
134
197
|
* whose `when` is missing or passes is taken.
|
|
135
198
|
*/
|
|
136
|
-
export type AlwaysSpec<Ctx, To extends string = string> = To | AlwaysConfig<Ctx, To> | RuleList<AlwaysConfig<Ctx, To>>;
|
|
199
|
+
export type AlwaysSpec<Ctx, To extends string = string, E extends EventObject = never, Tag extends string = never, Hook extends string = string> = To | AlwaysConfig<Ctx, To, E, Tag, Hook> | AlwaysConfig<Ctx, To, never, Tag, Hook> | RuleList<AlwaysConfig<Ctx, To, E, Tag, Hook> | AlwaysConfig<Ctx, To, never, Tag, Hook>>;
|
|
200
|
+
/**
|
|
201
|
+
* The object form of a rule on an `after` timer: a `to`, a `when` and a `run`, each optional. A
|
|
202
|
+
* rule with neither a `to` nor a `run` throws when the machine is defined. `E` is the machine's
|
|
203
|
+
* event type; a rule typed without it has no `run`. `Hook` is the union of the paths the
|
|
204
|
+
* `machine` of its `when` and `run` takes: any `string` when left out.
|
|
205
|
+
* @noSelf
|
|
206
|
+
*/
|
|
207
|
+
export interface AfterRule<Ctx, To extends string = string, E extends EventObject = never, Tag extends string = never, Hook extends string = string> {
|
|
208
|
+
/**
|
|
209
|
+
* The full path of the state to move to when the timer fires. A rule without one runs its
|
|
210
|
+
* `run` and nothing else: no state exits or enters, and it is not a move.
|
|
211
|
+
*/
|
|
212
|
+
readonly to?: To;
|
|
213
|
+
/**
|
|
214
|
+
* The timer fires only when this returns `true`. It is first checked once the delay has
|
|
215
|
+
* passed, then on every `update` call until it passes or the state is left, so it must have no
|
|
216
|
+
* side effects. `ctx` is read-only here, one level deep: a write belongs in `run`. `machine`
|
|
217
|
+
* reads the active states.
|
|
218
|
+
*/
|
|
219
|
+
readonly when?: (ctx: Readonly<Ctx>, machine: MachineView<Hook, Tag>) => boolean;
|
|
220
|
+
/**
|
|
221
|
+
* Runs when the timer fires: between the `exit` and `enter` hooks of the move, or on its own
|
|
222
|
+
* for a rule with no `to`.
|
|
223
|
+
*/
|
|
224
|
+
readonly run?: RuleRun<Ctx, E, Tag, Hook>;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* One entry of the list form of `after`: a `delay` beside the fields of an `AfterRule`.
|
|
228
|
+
* @noSelf
|
|
229
|
+
*/
|
|
230
|
+
export interface AfterConfig<Ctx, To extends string = string, E extends EventObject = never, Tag extends string = never, Hook extends string = string> extends AfterRule<Ctx, To, E, Tag, Hook> {
|
|
231
|
+
/**
|
|
232
|
+
* The delay in seconds of game time: a number, or a function of the context that returns one.
|
|
233
|
+
* A function is read once each time the state is entered, after its `enter` hook, and again on
|
|
234
|
+
* a hot reload, so the delay is fixed for that stay. The function only reads the context: it
|
|
235
|
+
* cannot assign to a field of `ctx`. A result that is not a non-negative number throws, naming
|
|
236
|
+
* the state.
|
|
237
|
+
*/
|
|
238
|
+
readonly delay: number | ((ctx: Readonly<Ctx>) => number);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* One entry of the list form of `every`: a `delay` and the `run` it repeats.
|
|
242
|
+
* @noSelf
|
|
243
|
+
*/
|
|
244
|
+
export interface EveryConfig<Ctx, E extends EventObject, Tag extends string = never, Hook extends string = string> {
|
|
245
|
+
/**
|
|
246
|
+
* The seconds of game time between two runs: a number, or a function of the context that
|
|
247
|
+
* returns one. A function is read once each time the state is entered, after its `enter` hook,
|
|
248
|
+
* and again on a hot reload, so the delay is fixed for that stay. The function only reads the
|
|
249
|
+
* context: it cannot assign to a field of `ctx`. A result that is not a positive number
|
|
250
|
+
* throws, naming the state.
|
|
251
|
+
*/
|
|
252
|
+
readonly delay: number | ((ctx: Readonly<Ctx>) => number);
|
|
253
|
+
/** Runs each time the delay passes: one function, or a list run in order. */
|
|
254
|
+
readonly run: RunAction<Ctx, E, Tag, Hook> | readonly RunAction<Ctx, E, Tag, Hook>[];
|
|
255
|
+
/** Not taken here: an `after` timer leaves the state. */
|
|
256
|
+
readonly to?: never;
|
|
257
|
+
/** Not taken here: a `run` tests a condition itself. */
|
|
258
|
+
readonly when?: never;
|
|
259
|
+
}
|
|
137
260
|
/**
|
|
138
261
|
* A state's rules by event `type`. Only the machine's event types are keys, and each rule's
|
|
139
262
|
* `when` and `run` see the event variant its key names.
|
|
140
263
|
*/
|
|
141
|
-
export type OnConfig<Ctx, E extends EventObject, To extends string = string> = {
|
|
264
|
+
export type OnConfig<Ctx, E extends EventObject, To extends string = string, Tag extends string = never, Hook extends string = string> = {
|
|
142
265
|
readonly [K in E["type"]]?: TransitionSpec<Ctx, E, Extract<E, {
|
|
143
266
|
type: K;
|
|
144
|
-
}>, To>;
|
|
267
|
+
}>, To, Tag, Hook>;
|
|
145
268
|
};
|
|
146
269
|
/**
|
|
147
270
|
* Runs on every way in (`enter`) or out (`exit`) of the state: a move, `start` and `stop`. A hot
|
|
148
271
|
* reload enters the states it adds, but drops a removed state without its `exit`. It gets no
|
|
149
|
-
* event; event data reaches `enter` through `ctx`, set in `run`.
|
|
272
|
+
* event; event data reaches `enter` through `ctx`, set in `run`. `Hook` is the union of the
|
|
273
|
+
* paths its `machine` takes: the machine's paths for a hook written in the config, and any
|
|
274
|
+
* `string` when left out.
|
|
150
275
|
*/
|
|
151
|
-
export type StateHook<Ctx, E extends EventObject> = (ctx: Ctx, machine: MachineInstance<Ctx, E>) => void;
|
|
276
|
+
export type StateHook<Ctx, E extends EventObject, Tag extends string = never, Hook extends string = string> = (ctx: Ctx, machine: MachineInstance<Ctx, E, Hook, Tag>) => void;
|
|
152
277
|
/**
|
|
153
278
|
* Runs on every `update(dt)` while the state is active, deepest state first. It returns a full
|
|
154
279
|
* path to move there, or `undefined` to pass. The first hook to return a path wins: the hooks
|
|
155
|
-
* above it and the `after` timers do not run on that call, and the move has no
|
|
280
|
+
* above it and the `after` timers of its region do not run on that call, and the move has no
|
|
281
|
+
* `run`. Under a parallel state the other regions still run their hooks and timers, unless the
|
|
282
|
+
* move left the parallel state or entered it again. `Hook` is the union of the paths its
|
|
283
|
+
* `machine` takes: the machine's paths for a hook written in the config, and any `string` when
|
|
284
|
+
* left out.
|
|
285
|
+
*/
|
|
286
|
+
export type UpdateHook<Ctx, E extends EventObject, To extends string = string, Tag extends string = never, Hook extends string = string> = (ctx: Ctx, dt: number, machine: MachineInstance<Ctx, E, Hook, Tag>) => To | undefined;
|
|
287
|
+
/**
|
|
288
|
+
* Runs when the machine drops an event while the state is active: for the events `onUnhandled`
|
|
289
|
+
* reports, and no others. Every active state that has the hook runs it, deepest first, in the
|
|
290
|
+
* order `update` hooks run. `event` is the machine's whole event union, and is read-only: the
|
|
291
|
+
* hook cannot assign to its fields. The hook moves nothing: a `send` from it queues, and a
|
|
292
|
+
* `stop()` ends the step once every hook and listener has run for the event. `Hook` is the
|
|
293
|
+
* union of the paths its `machine` takes: the machine's paths for a hook written in the config,
|
|
294
|
+
* and any `string` when left out.
|
|
156
295
|
*/
|
|
157
|
-
export type
|
|
296
|
+
export type UnhandledHook<Ctx, E extends EventObject, Tag extends string = never, Hook extends string = string> = (ctx: Ctx, event: Readonly<E>, machine: MachineInstance<Ctx, E, Hook, Tag>) => void;
|
|
158
297
|
/**
|
|
159
298
|
* Starts work that lives as long as the state. `finish` sends one event and is ignored after the
|
|
160
|
-
* first call or once the state is left. The returned cleanup runs on every way out.
|
|
299
|
+
* first call or once the state is left. The returned cleanup runs on every way out. A task that
|
|
300
|
+
* starts several callbacks counts them with `waitGroup`. `Hook` is the union of the paths its
|
|
301
|
+
* `machine` takes: the machine's paths for a task written in the config, and any `string` when
|
|
302
|
+
* left out.
|
|
303
|
+
* @noSelf
|
|
304
|
+
*/
|
|
305
|
+
export type TaskStart<Ctx, E extends EventObject, Tag extends string = never, Hook extends string = string> = (ctx: Ctx, finish: (event: E) => void, machine: MachineInstance<Ctx, E, Hook, Tag>) => (() => void) | void;
|
|
306
|
+
/**
|
|
307
|
+
* Counts several pieces of engine work a state starts and waits for, returned by `waitGroup()`.
|
|
308
|
+
* It knows nothing about a machine: built inside a `task`, it is new for each entry of the
|
|
309
|
+
* state.
|
|
161
310
|
* @noSelf
|
|
162
311
|
*/
|
|
163
|
-
export
|
|
312
|
+
export interface WaitGroup {
|
|
313
|
+
/**
|
|
314
|
+
* Records one pending piece of work and returns the function that reports it finished. That
|
|
315
|
+
* function counts once, and a later call does nothing. It ignores its arguments, so an engine
|
|
316
|
+
* function takes it as its callback, as in `http.request(url, "GET", group.add())`.
|
|
317
|
+
*/
|
|
318
|
+
readonly add: () => () => void;
|
|
319
|
+
/**
|
|
320
|
+
* Calls `callback` once when no added work is pending: at once when none is pending at the
|
|
321
|
+
* call, and otherwise inside the report that finishes the last pending piece. Each call
|
|
322
|
+
* registers one callback, and several run in the order registered. A `wait` made before the
|
|
323
|
+
* first `add` runs at once, so it goes after them.
|
|
324
|
+
*/
|
|
325
|
+
readonly wait: (callback: () => void) => void;
|
|
326
|
+
}
|
|
164
327
|
/**
|
|
165
328
|
* A machine definition, returned by the second `defineMachine` call. Its `start` creates the
|
|
166
329
|
* instances that run.
|
|
167
330
|
* @noSelf
|
|
168
331
|
*/
|
|
169
|
-
export interface Machine<Ctx, E extends EventObject, P extends string = string> {
|
|
332
|
+
export interface Machine<Ctx, E extends EventObject, P extends string = string, Tag extends string = never> {
|
|
170
333
|
/**
|
|
171
|
-
* Creates an instance with `ctx` as its context
|
|
172
|
-
*
|
|
173
|
-
*
|
|
334
|
+
* Creates an instance with `ctx` as its context. Without `at` it enters the root's `initial`
|
|
335
|
+
* chain. With `at`, a list of full state paths such as a copy of `leaves`, it enters toward
|
|
336
|
+
* each path: a level or region no path names goes through `initial`, a path the machine lacks
|
|
337
|
+
* falls back to its closest parent, and of two paths that cannot be active together the first
|
|
338
|
+
* wins. Timer progress and `restoreDepth` memory are not part of the list. Each call returns a
|
|
339
|
+
* separate instance, which has already handled the events its `enter` hooks sent.
|
|
174
340
|
*/
|
|
175
|
-
readonly start: (ctx: Ctx) => MachineInstance<Ctx, E, P>;
|
|
341
|
+
readonly start: (ctx: Ctx, at?: readonly P[]) => MachineInstance<Ctx, E, P, Tag>;
|
|
176
342
|
}
|
|
177
343
|
type PathDepth = [never, 0, 1, 2, 3];
|
|
178
344
|
type NextCount = [never, 2, 3, 4, 5, 6];
|
|
@@ -180,10 +346,6 @@ type TreePathsBelow<T, D extends number> = {
|
|
|
180
346
|
[K in keyof T & string]: K | `${K}/${D extends 1 ? keyof T[K] extends never ? never : string : TreePathsBelow<T[K], PathDepth[D]>}`;
|
|
181
347
|
}[keyof T & string];
|
|
182
348
|
type TreePath<T> = `/${TreePathsBelow<T, 4>}`;
|
|
183
|
-
type SlotPathsBelow<T, D extends number> = unknown extends T ? string : {
|
|
184
|
-
[K in keyof T & string]: K | `${K}/${D extends 1 ? string : SlotPathsBelow<T[K], PathDepth[D]>}`;
|
|
185
|
-
}[keyof T & string];
|
|
186
|
-
type SlotPath<T> = `/${SlotPathsBelow<T, 4>}`;
|
|
187
349
|
type TreeOf<C> = C extends {
|
|
188
350
|
readonly states: infer Children;
|
|
189
351
|
} ? {
|
|
@@ -197,56 +359,89 @@ export type StatePath<C> = TreePath<TreeOf<C>>;
|
|
|
197
359
|
type TreeDepth<T, D extends number> = unknown extends T ? number : [D] extends [never] ? number : 1 | ({
|
|
198
360
|
[K in keyof T]: unknown extends T[K] ? never : TreeDepth<T[K], PathDepth[D]>;
|
|
199
361
|
}[keyof T] extends infer N ? N extends number ? number extends N ? number : NextCount[N] : never : never);
|
|
200
|
-
type AnyChild<Ctx, E extends EventObject, T, To extends string> = unknown extends T ? {
|
|
201
|
-
readonly [name: string]: StateNode<Ctx, E, unknown, string, To>;
|
|
362
|
+
type AnyChild<Ctx, E extends EventObject, T, To extends string, Tag extends string = never, Hook extends string = string> = unknown extends T ? {
|
|
363
|
+
readonly [name: string]: StateNode<Ctx, E, unknown, string, To, Tag, Hook>;
|
|
202
364
|
} : unknown;
|
|
203
365
|
/** @noSelf */
|
|
204
|
-
interface StateBase<Ctx, E extends EventObject, T, Self extends string, To extends string> {
|
|
366
|
+
interface StateBase<Ctx, E extends EventObject, T, Self extends string, To extends string, Tag extends string = never, Hook extends string = string> {
|
|
205
367
|
/** Child states, by name. A name is non-empty and contains no `/`. */
|
|
206
368
|
readonly states?: {
|
|
207
|
-
readonly [K in keyof T]: StateNode<Ctx, E, T[K], `${Self}/${K & string}`, To>;
|
|
208
|
-
} & NoInfer<AnyChild<Ctx, E, T, To>>;
|
|
369
|
+
readonly [K in keyof T]: StateNode<Ctx, E, T[K], `${Self}/${K & string}`, To, Tag, Hook>;
|
|
370
|
+
} & NoInfer<AnyChild<Ctx, E, T, To, Tag, Hook>>;
|
|
371
|
+
/**
|
|
372
|
+
* Names from the machine's third type argument that hold while the state is active, read with
|
|
373
|
+
* `hasTag`.
|
|
374
|
+
*/
|
|
375
|
+
readonly tags?: readonly Tag[];
|
|
209
376
|
/**
|
|
210
377
|
* Rules by event `type`, each a target path, a rule object or a list of them. An event starts
|
|
211
378
|
* at the deepest active state and moves up to the parent while no rule takes it.
|
|
212
379
|
*/
|
|
213
|
-
readonly on?: NoInfer<OnConfig<Ctx, E, To>>;
|
|
380
|
+
readonly on?: NoInfer<OnConfig<Ctx, E, To, Tag, Hook>>;
|
|
214
381
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
382
|
+
* Timers, in one of two forms: targets or `AfterRule` objects by delay in seconds of game
|
|
383
|
+
* time, such as `{ 2: "/idle" }`, or a list of `AfterConfig` entries, such as
|
|
384
|
+
* `[{ delay: (ctx) => ctx.patrolFor, to: "/rest" }]`, whose `delay` is a number or a function
|
|
385
|
+
* of the context. A delay function is read once each time the state is entered, after its
|
|
386
|
+
* `enter` hook, and again on a hot reload. The delay is counted by `update(dt)` while the state
|
|
387
|
+
* is active, from zero on each entry, and a timer fires at most once per stay. An `update`
|
|
388
|
+
* call tries the due timers in delay order: one whose `when` fails stays pending, one with no
|
|
389
|
+
* `to` runs its `run` and the call goes on, and the first that moves ends the timers of its
|
|
390
|
+
* region for that call. None is tried in a region an `update` hook moved on that call. Under a
|
|
391
|
+
* parallel state each region follows both rules on its own, and the parallel state's own
|
|
392
|
+
* timers are tried only when no region moved. A state's `every` entries run before its timers.
|
|
218
393
|
*/
|
|
219
394
|
readonly after?: NoInfer<{
|
|
220
|
-
readonly [seconds: number]: To
|
|
221
|
-
}>;
|
|
395
|
+
readonly [seconds: number]: To | AfterRule<Ctx, To, E, Tag, Hook> | AfterRule<Ctx, To, never, Tag, Hook>;
|
|
396
|
+
} | readonly (AfterConfig<Ctx, To, E, Tag, Hook> | AfterConfig<Ctx, To, never, Tag, Hook>)[]>;
|
|
222
397
|
/**
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
398
|
+
* Code that repeats while the state is active, in one of two forms: a `run` by delay in
|
|
399
|
+
* seconds of game time, such as `{ 0.5: (ctx) => burn(ctx) }`, or a list of `EveryConfig`
|
|
400
|
+
* entries, such as `[{ delay: (ctx) => ctx.fireEvery, run: fire }]`, whose `delay` is a number
|
|
401
|
+
* or a function of the context, read as an `after` delay is. A `run` is one function or a list
|
|
402
|
+
* run in order. An entry first runs one delay after the state is entered, then each time
|
|
403
|
+
* another delay has passed on the state's clock, which `update(dt)` advances while the state
|
|
404
|
+
* is active, from zero on each entry. It runs at most once per `update` call: a call that
|
|
405
|
+
* covers several delays runs it once, and its later runs stay on multiples of the delay. A run
|
|
406
|
+
* is not a move: no `update` hook is skipped, `always` is not rechecked and `onMove` is not
|
|
407
|
+
* called. A state's entries run where its `after` timers are tried and before them, a list in
|
|
408
|
+
* list order and the other form shortest delay first; on a call in which an `update` hook or a
|
|
409
|
+
* timer of a deeper state moved, they wait for the next call. A delay that is not a positive
|
|
410
|
+
* number throws, naming the state.
|
|
226
411
|
*/
|
|
227
|
-
readonly
|
|
412
|
+
readonly every?: NoInfer<{
|
|
413
|
+
readonly [seconds: number]: RunAction<Ctx, E, Tag, Hook> | readonly RunAction<Ctx, E, Tag, Hook>[];
|
|
414
|
+
} | readonly EveryConfig<Ctx, E, Tag, Hook>[]>;
|
|
415
|
+
/**
|
|
416
|
+
* Rules taken right after any move that leaves this state active; the first whose `when` is
|
|
417
|
+
* missing or passes wins, and its `run` runs during the move. It is checked after a move only:
|
|
418
|
+
* not after a rule with no `to`, and not on an `update` call that moves nothing.
|
|
419
|
+
*/
|
|
420
|
+
readonly always?: AlwaysSpec<Ctx, To, E, Tag, Hook>;
|
|
228
421
|
/**
|
|
229
422
|
* Runs each time the state is entered, outermost state first: on a move, at `start`, and when
|
|
230
423
|
* a hot reload adds the state. It gets no event.
|
|
231
424
|
*/
|
|
232
|
-
readonly enter?: StateHook<Ctx, E>;
|
|
425
|
+
readonly enter?: StateHook<Ctx, E, Tag, Hook>;
|
|
233
426
|
/**
|
|
234
427
|
* Runs each time the state is left, deepest state first, including on `stop()`. A state a hot
|
|
235
428
|
* reload removes is dropped without it.
|
|
236
429
|
*/
|
|
237
|
-
readonly exit?: StateHook<Ctx, E>;
|
|
430
|
+
readonly exit?: StateHook<Ctx, E, Tag, Hook>;
|
|
238
431
|
/**
|
|
239
432
|
* Runs on every `update(dt)` while the state is active. Return one of the machine's full paths
|
|
240
433
|
* to move there, or `undefined` to stay.
|
|
241
434
|
*/
|
|
242
|
-
readonly update?: NoInfer<UpdateHook<Ctx, E, To>>;
|
|
435
|
+
readonly update?: NoInfer<UpdateHook<Ctx, E, To, Tag, Hook>>;
|
|
243
436
|
/** Started on each entry; see `TaskStart`. */
|
|
244
|
-
readonly task?: TaskStart<Ctx, E>;
|
|
437
|
+
readonly task?: TaskStart<Ctx, E, Tag, Hook>;
|
|
438
|
+
/** Runs when the machine drops an event while the state is active; see `UnhandledHook`. */
|
|
439
|
+
readonly unhandled?: UnhandledHook<Ctx, E, Tag, Hook>;
|
|
245
440
|
/** Renamed to `task`. */
|
|
246
441
|
readonly invoke?: never;
|
|
247
442
|
}
|
|
248
443
|
/** @noSelf */
|
|
249
|
-
interface CompoundState<Ctx, E extends EventObject, T, Self extends string, To extends string> extends StateBase<Ctx, E, T, Self, To> {
|
|
444
|
+
interface CompoundState<Ctx, E extends EventObject, T, Self extends string, To extends string, Tag extends string = never, Hook extends string = string> extends StateBase<Ctx, E, T, Self, To, Tag, Hook> {
|
|
250
445
|
/**
|
|
251
446
|
* `"parallel"` keeps every child active at once, each as a region with its own active state.
|
|
252
447
|
* Not allowed on the root.
|
|
@@ -266,7 +461,7 @@ interface CompoundState<Ctx, E extends EventObject, T, Self extends string, To e
|
|
|
266
461
|
readonly restoreDepth?: NoInfer<TreeDepth<T, 4> | "all">;
|
|
267
462
|
}
|
|
268
463
|
/** @noSelf */
|
|
269
|
-
interface ParallelState<Ctx, E extends EventObject, T, Self extends string, To extends string> extends StateBase<Ctx, E, T, Self, To> {
|
|
464
|
+
interface ParallelState<Ctx, E extends EventObject, T, Self extends string, To extends string, Tag extends string = never, Hook extends string = string> extends StateBase<Ctx, E, T, Self, To, Tag, Hook> {
|
|
270
465
|
/**
|
|
271
466
|
* `"parallel"` keeps every child active at once, each as a region with its own active state.
|
|
272
467
|
* Not allowed on the root.
|
|
@@ -275,45 +470,110 @@ interface ParallelState<Ctx, E extends EventObject, T, Self extends string, To e
|
|
|
275
470
|
readonly initial?: never;
|
|
276
471
|
readonly restoreDepth?: never;
|
|
277
472
|
}
|
|
278
|
-
type StateNode<Ctx, E extends EventObject, T, Self extends string, To extends string> = CompoundState<Ctx, E, T, Self, To> | ParallelState<Ctx, E, T, Self, To>;
|
|
279
|
-
type
|
|
473
|
+
type StateNode<Ctx, E extends EventObject, T, Self extends string, To extends string, Tag extends string = never, Hook extends string = string> = CompoundState<Ctx, E, T, Self, To, Tag, Hook> | ParallelState<Ctx, E, T, Self, To, Tag, Hook>;
|
|
474
|
+
type StateKey = keyof CompoundState<unknown, EventObject, unknown, string, string> | keyof ParallelState<unknown, EventObject, unknown, string, string>;
|
|
475
|
+
type NodeLevel<NT> = {
|
|
476
|
+
readonly [K in keyof NT]: NodeKeys<NT[K]> & InferOnly<NT[K]>;
|
|
477
|
+
};
|
|
478
|
+
type NodeKeys<N> = {
|
|
479
|
+
readonly [P in keyof N]: NodeSlot<P, N[P], keyof N>;
|
|
480
|
+
};
|
|
481
|
+
type NodeSlot<P, V, Keys> = P extends "states" ? NodeLevel<V> : P extends "initial" | "restoreDepth" ? "states" extends Keys ? unknown : never : P extends StateKey ? unknown : never;
|
|
482
|
+
type InferOnly<X> = X extends unknown ? unknown : X;
|
|
483
|
+
type StatesOf<N> = N extends {
|
|
484
|
+
readonly states: infer C;
|
|
485
|
+
} ? C : unknown;
|
|
486
|
+
type HasStates<N> = "states" extends keyof N ? true : false;
|
|
487
|
+
type NodePathsBelow<L, D extends number> = unknown extends L ? string : {
|
|
488
|
+
[K in keyof L & string]: K | (unknown extends L[K] ? `${K}/${string}` : HasStates<L[K]> extends true ? `${K}/${D extends 1 ? string : NodePathsBelow<StatesOf<L[K]>, PathDepth[D]>}` : never);
|
|
489
|
+
}[keyof L & string];
|
|
490
|
+
type NodePath<NT> = `/${NodePathsBelow<NT, 4>}`;
|
|
491
|
+
type RootState<Ctx, E extends EventObject, T, Tag extends string = never, NT = unknown, HT = unknown> = CompoundState<Ctx, E, T, "", NoInfer<NodePath<NT>>, Tag, NoInfer<NodePath<HT>>> & {
|
|
280
492
|
readonly initial: string;
|
|
281
493
|
readonly states: object;
|
|
494
|
+
} & {
|
|
495
|
+
readonly states: NodeLevel<NT>;
|
|
496
|
+
} & {
|
|
497
|
+
readonly states: NodeLevel<HT>;
|
|
282
498
|
};
|
|
283
499
|
/**
|
|
284
500
|
* The config of one state. Every field is optional. A state with child `states` is a compound
|
|
285
501
|
* state and needs an `initial`, unless its `type` is `"parallel"`; a state without children is a
|
|
286
|
-
* leaf.
|
|
502
|
+
* leaf. `Tag` is the union of the machine's tag names. The `machine` a callback of a config
|
|
503
|
+
* typed this way receives takes any `string` path.
|
|
287
504
|
*/
|
|
288
|
-
export type StateConfig<Ctx, E extends EventObject> = StateNode<Ctx, E, unknown, string, string>;
|
|
505
|
+
export type StateConfig<Ctx, E extends EventObject, Tag extends string = never> = StateNode<Ctx, E, unknown, string, string, Tag>;
|
|
289
506
|
/**
|
|
290
507
|
* The root config passed to `defineMachine`. It takes the fields of a state and requires
|
|
291
|
-
* `initial` and `states`. The root has no name and cannot be parallel.
|
|
508
|
+
* `initial` and `states`. The root has no name and cannot be parallel. `Tag` is the union of the
|
|
509
|
+
* machine's tag names. The `machine` a callback of a config typed this way receives takes any
|
|
510
|
+
* `string` path.
|
|
292
511
|
*/
|
|
293
|
-
export type MachineConfig<Ctx, E extends EventObject> = CompoundState<Ctx, E, unknown, string, string> & {
|
|
512
|
+
export type MachineConfig<Ctx, E extends EventObject, Tag extends string = never> = CompoundState<Ctx, E, unknown, string, string, Tag> & {
|
|
294
513
|
readonly initial: string;
|
|
295
514
|
readonly states: {
|
|
296
|
-
readonly [name: string]: StateConfig<Ctx, E>;
|
|
515
|
+
readonly [name: string]: StateConfig<Ctx, E, Tag>;
|
|
297
516
|
};
|
|
298
517
|
};
|
|
518
|
+
type WithPrivate<Ctx, Priv> = [Priv] extends [never] ? Ctx : Ctx & Priv;
|
|
299
519
|
/**
|
|
300
520
|
* Defines a machine in two calls: `defineMachine<Ctx, E>()` fixes the context and event types and
|
|
301
521
|
* takes an optional hot-reload key, and the second call takes the config. The config is checked
|
|
302
522
|
* when that call runs, and a mistake throws, naming the state path. With a key, a later
|
|
303
523
|
* definition under it puts its states into the machine already built, so every running instance
|
|
304
524
|
* sees the edit; without one, each call builds a separate machine.
|
|
525
|
+
*
|
|
526
|
+
* A third type argument, as in `defineMachine<Ctx, E, Tag>()`, is a union of tag names: a state's
|
|
527
|
+
* `tags` lists some of them, and `hasTag` asks whether an active state carries one. A machine
|
|
528
|
+
* defined without it has no tags.
|
|
529
|
+
*
|
|
530
|
+
* The root config may hold `privateCtx`, for a machine that keeps fields of its own, such as a
|
|
531
|
+
* counter, a heat level or a list it builds up. Every hook, `when`, `run`, `task` and `update`
|
|
532
|
+
* then sees those fields beside `Ctx`, while `start` takes only `Ctx` and the instance it returns
|
|
533
|
+
* shows only `Ctx` in its `ctx`. A machine without fields of its own leaves `privateCtx` out.
|
|
534
|
+
*/
|
|
535
|
+
export declare function defineMachine<Ctx, E extends EventObject, Tag extends string = never>(key?: string): <T, Priv extends object = never, NT = unknown, HT = unknown>(config: RootState<WithPrivate<Ctx, Priv>, E, T, Tag, NT, HT> & PrivateCtxConfig<Ctx, Priv>) => Machine<Ctx, E, TreePath<T>, Tag>;
|
|
536
|
+
/**
|
|
537
|
+
* Makes a group that counts the engine work a `task` started and tells when all of it has
|
|
538
|
+
* finished. Each `add()` returns the callback to pass to one engine call, and `wait` runs its
|
|
539
|
+
* callback once every one of them has reported.
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```ts
|
|
543
|
+
* loading: {
|
|
544
|
+
* task: (ctx, finish) => {
|
|
545
|
+
* const group = waitGroup();
|
|
546
|
+
* http.request(ctx.url, "GET", group.add());
|
|
547
|
+
* go.animate(
|
|
548
|
+
* ctx.curtain,
|
|
549
|
+
* "tint.w",
|
|
550
|
+
* go.PLAYBACK_ONCE_FORWARD,
|
|
551
|
+
* 0,
|
|
552
|
+
* go.EASING_LINEAR,
|
|
553
|
+
* 0.5,
|
|
554
|
+
* 0,
|
|
555
|
+
* group.add(),
|
|
556
|
+
* );
|
|
557
|
+
* group.wait(() => finish({ type: "READY" }));
|
|
558
|
+
* },
|
|
559
|
+
* on: { READY: "/playing" },
|
|
560
|
+
* },
|
|
561
|
+
* ```
|
|
305
562
|
*/
|
|
306
|
-
export declare function
|
|
563
|
+
export declare function waitGroup(): WaitGroup;
|
|
307
564
|
/**
|
|
308
|
-
* The key a `
|
|
565
|
+
* The optional key a `defineMachine` root config takes beside `initial` and `states`.
|
|
309
566
|
* @noSelf
|
|
310
567
|
*/
|
|
311
568
|
export interface PrivateCtxConfig<Ctx, Priv> {
|
|
312
569
|
/**
|
|
313
570
|
* Builds the fields only the machine uses. It runs once per `start`, before the root's `enter`
|
|
314
571
|
* hook, so every instance gets fresh fields of its own; a value written straight into the
|
|
315
|
-
* config would be one array or table shared by every instance. It
|
|
316
|
-
* a private field from a field of `Ctx
|
|
572
|
+
* config would be one array or table shared by every instance. It receives the object passed
|
|
573
|
+
* to `start`, typed `Ctx`, so a private field can start from a field of `Ctx`:
|
|
574
|
+
* `privateCtx: (ctx: TurretOptions) => ({ heat: ctx.startHeat })`. Give `ctx` its type; without
|
|
575
|
+
* one TypeScript can type a hook before it knows the private fields, and that hook reports the
|
|
576
|
+
* field as missing.
|
|
317
577
|
*
|
|
318
578
|
* The fields are added to the object passed to `start`, which stays the instance's `ctx`, so
|
|
319
579
|
* restarting with `start(old.ctx)` keeps the public fields and builds the private ones fresh.
|
|
@@ -323,92 +583,84 @@ export interface PrivateCtxConfig<Ctx, Priv> {
|
|
|
323
583
|
*
|
|
324
584
|
* The fields are hidden from callers by type only. At run time `ctx` is one table, and hooks,
|
|
325
585
|
* `hsm-view` and a debugger see every field.
|
|
586
|
+
*
|
|
587
|
+
* There are two ways to type the fields.
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* **Inferred.** For a few fields, write them inline and let TypeScript infer their types. Add
|
|
591
|
+
* `as` where the inferred type is wrong: an empty array is `never[]` until it reads
|
|
592
|
+
* `[] as Hash[]`, and a field holding one of several strings is any `string` until it reads
|
|
593
|
+
* `"cool" as "cool" | "hot"`.
|
|
594
|
+
*
|
|
595
|
+
* ```ts
|
|
596
|
+
* type TurretEvent = { type: "SPOTTED" } | { type: "LOST" };
|
|
597
|
+
*
|
|
598
|
+
* export const turret = defineMachine<{ readonly fireRate: number }, TurretEvent>("turret")({
|
|
599
|
+
* privateCtx: () => ({ heat: 0, targets: [] as Hash[] }),
|
|
600
|
+
* initial: "/idle",
|
|
601
|
+
* states: {
|
|
602
|
+
* idle: { on: { SPOTTED: "/firing" } },
|
|
603
|
+
* firing: {
|
|
604
|
+
* enter: (ctx) => {
|
|
605
|
+
* ctx.heat += 1 / ctx.fireRate;
|
|
606
|
+
* },
|
|
607
|
+
* on: { LOST: "/idle" },
|
|
608
|
+
* },
|
|
609
|
+
* },
|
|
610
|
+
* });
|
|
611
|
+
*
|
|
612
|
+
* const gun = turret.start({ fireRate: 2 }); // gun.ctx shows fireRate only
|
|
613
|
+
* ```
|
|
614
|
+
*
|
|
615
|
+
* @example
|
|
616
|
+
* **A named type.** For more fields, or when a helper outside the config needs the whole
|
|
617
|
+
* context, declare the fields as an interface and make it the factory's return type. The
|
|
618
|
+
* fields need no `as`, a misspelled or unknown field is a compile error, and the whole context
|
|
619
|
+
* is `TurretOptions & TurretPrivate`. `satisfies TurretPrivate` does not do this: it checks the
|
|
620
|
+
* object but keeps its narrow types, so an empty array stays `never[]`.
|
|
621
|
+
*
|
|
622
|
+
* ```ts
|
|
623
|
+
* interface TurretOptions {
|
|
624
|
+
* readonly fireRate: number;
|
|
625
|
+
* }
|
|
626
|
+
*
|
|
627
|
+
* interface TurretPrivate {
|
|
628
|
+
* heat: number;
|
|
629
|
+
* mode: "cool" | "hot";
|
|
630
|
+
* }
|
|
631
|
+
*
|
|
632
|
+
* type TurretEvent = { type: "SPOTTED" } | { type: "LOST" };
|
|
633
|
+
*
|
|
634
|
+
* const overheated = (ctx: TurretOptions & TurretPrivate) => ctx.heat > ctx.fireRate;
|
|
635
|
+
*
|
|
636
|
+
* export const turret = defineMachine<TurretOptions, TurretEvent>("turret")({
|
|
637
|
+
* privateCtx: (): TurretPrivate => ({ heat: 0, mode: "cool" }),
|
|
638
|
+
* initial: "/idle",
|
|
639
|
+
* states: {
|
|
640
|
+
* idle: { on: { SPOTTED: "/firing" } },
|
|
641
|
+
* firing: {
|
|
642
|
+
* enter: (ctx) => {
|
|
643
|
+
* ctx.heat += 1 / ctx.fireRate;
|
|
644
|
+
* },
|
|
645
|
+
* always: { to: "/cooling", when: overheated },
|
|
646
|
+
* on: { LOST: "/idle" },
|
|
647
|
+
* },
|
|
648
|
+
* cooling: {
|
|
649
|
+
* enter: (ctx) => {
|
|
650
|
+
* ctx.mode = "hot";
|
|
651
|
+
* },
|
|
652
|
+
* exit: (ctx) => {
|
|
653
|
+
* ctx.heat = 0;
|
|
654
|
+
* ctx.mode = "cool";
|
|
655
|
+
* },
|
|
656
|
+
* after: { 2: "/idle" },
|
|
657
|
+
* },
|
|
658
|
+
* },
|
|
659
|
+
* });
|
|
660
|
+
* ```
|
|
326
661
|
*/
|
|
327
|
-
readonly privateCtx:
|
|
662
|
+
readonly privateCtx?: (ctx: Ctx) => Priv & {
|
|
328
663
|
readonly [K in keyof Ctx & keyof Priv]: never;
|
|
329
664
|
};
|
|
330
665
|
}
|
|
331
|
-
/**
|
|
332
|
-
* Like `defineMachine`, for a machine that keeps fields of its own, such as a counter, a heat
|
|
333
|
-
* level or a list it builds up. The config's `privateCtx` returns those fields. Every hook,
|
|
334
|
-
* `when`, `run`, `task` and `update` sees them beside `Ctx`, while `start` takes only `Ctx` and
|
|
335
|
-
* the instance it returns shows only `Ctx` in its `ctx`. A machine without fields of its own
|
|
336
|
-
* uses `defineMachine`.
|
|
337
|
-
*
|
|
338
|
-
* There are two ways to type the fields.
|
|
339
|
-
*
|
|
340
|
-
* @example
|
|
341
|
-
* **Inferred.** For a few fields, write them inline and let TypeScript infer their types. Add
|
|
342
|
-
* `as` where the inferred type is wrong: an empty array is `never[]` until it reads
|
|
343
|
-
* `[] as Hash[]`, and a field holding one of several strings is any `string` until it reads
|
|
344
|
-
* `"cool" as "cool" | "hot"`.
|
|
345
|
-
*
|
|
346
|
-
* ```ts
|
|
347
|
-
* type TurretEvent = { type: "SPOTTED" } | { type: "LOST" };
|
|
348
|
-
*
|
|
349
|
-
* export const turret = definePrivateMachine<{ readonly fireRate: number }, TurretEvent>("turret")({
|
|
350
|
-
* privateCtx: () => ({ heat: 0, targets: [] as Hash[] }),
|
|
351
|
-
* initial: "/idle",
|
|
352
|
-
* states: {
|
|
353
|
-
* idle: { on: { SPOTTED: "/firing" } },
|
|
354
|
-
* firing: {
|
|
355
|
-
* enter: (ctx) => {
|
|
356
|
-
* ctx.heat += 1 / ctx.fireRate;
|
|
357
|
-
* },
|
|
358
|
-
* on: { LOST: "/idle" },
|
|
359
|
-
* },
|
|
360
|
-
* },
|
|
361
|
-
* });
|
|
362
|
-
*
|
|
363
|
-
* const gun = turret.start({ fireRate: 2 }); // gun.ctx shows fireRate only
|
|
364
|
-
* ```
|
|
365
|
-
*
|
|
366
|
-
* @example
|
|
367
|
-
* **A named type.** For more fields, or when a helper outside the config needs the whole
|
|
368
|
-
* context, declare the fields as an interface and make it the factory's return type. The fields
|
|
369
|
-
* need no `as`, a misspelled or unknown field is a compile error, and the whole context is
|
|
370
|
-
* `TurretOptions & TurretPrivate`. `satisfies TurretPrivate` does not do this: it checks the
|
|
371
|
-
* object but keeps its narrow types, so an empty array stays `never[]`.
|
|
372
|
-
*
|
|
373
|
-
* ```ts
|
|
374
|
-
* interface TurretOptions {
|
|
375
|
-
* readonly fireRate: number;
|
|
376
|
-
* }
|
|
377
|
-
*
|
|
378
|
-
* interface TurretPrivate {
|
|
379
|
-
* heat: number;
|
|
380
|
-
* mode: "cool" | "hot";
|
|
381
|
-
* }
|
|
382
|
-
*
|
|
383
|
-
* type TurretEvent = { type: "SPOTTED" } | { type: "LOST" };
|
|
384
|
-
*
|
|
385
|
-
* const overheated = (ctx: TurretOptions & TurretPrivate) => ctx.heat > ctx.fireRate;
|
|
386
|
-
*
|
|
387
|
-
* export const turret = definePrivateMachine<TurretOptions, TurretEvent>("turret")({
|
|
388
|
-
* privateCtx: (): TurretPrivate => ({ heat: 0, mode: "cool" }),
|
|
389
|
-
* initial: "/idle",
|
|
390
|
-
* states: {
|
|
391
|
-
* idle: { on: { SPOTTED: "/firing" } },
|
|
392
|
-
* firing: {
|
|
393
|
-
* enter: (ctx) => {
|
|
394
|
-
* ctx.heat += 1 / ctx.fireRate;
|
|
395
|
-
* },
|
|
396
|
-
* always: { to: "/cooling", when: overheated },
|
|
397
|
-
* on: { LOST: "/idle" },
|
|
398
|
-
* },
|
|
399
|
-
* cooling: {
|
|
400
|
-
* enter: (ctx) => {
|
|
401
|
-
* ctx.mode = "hot";
|
|
402
|
-
* },
|
|
403
|
-
* exit: (ctx) => {
|
|
404
|
-
* ctx.heat = 0;
|
|
405
|
-
* ctx.mode = "cool";
|
|
406
|
-
* },
|
|
407
|
-
* after: { 2: "/idle" },
|
|
408
|
-
* },
|
|
409
|
-
* },
|
|
410
|
-
* });
|
|
411
|
-
* ```
|
|
412
|
-
*/
|
|
413
|
-
export declare const definePrivateMachine: <Ctx, E extends EventObject>(key?: string) => <T, Priv extends object>(config: RootState<Ctx & Priv, E, T> & PrivateCtxConfig<Ctx, Priv>) => Machine<Ctx, E, TreePath<T>>;
|
|
414
666
|
export {};
|