@typeonce/effect-machine 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/dist/Machine.d.ts +181 -144
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +3 -1
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/atom.d.ts +13 -2
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js +6 -3
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/configuration.d.ts.map +1 -1
- package/dist/internal/machine/configuration.js +103 -20
- package/dist/internal/machine/configuration.js.map +1 -1
- package/dist/internal/machine/executionPlan.d.ts.map +1 -1
- package/dist/internal/machine/executionPlan.js +6 -0
- package/dist/internal/machine/executionPlan.js.map +1 -1
- package/dist/internal/machine/invocation.js +1 -1
- package/dist/internal/machine/invocation.js.map +1 -1
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +23 -17
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/planner.d.ts +10 -0
- package/dist/internal/machine/planner.d.ts.map +1 -1
- package/dist/internal/machine/planner.js +45 -35
- package/dist/internal/machine/planner.js.map +1 -1
- package/dist/internal/machine/serialization.d.ts.map +1 -1
- package/dist/internal/machine/serialization.js +53 -21
- package/dist/internal/machine/serialization.js.map +1 -1
- package/dist/internal/machine/stateDefinition.d.ts +4 -4
- package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
- package/dist/internal/machine/stateDefinition.js +18 -11
- package/dist/internal/machine/stateDefinition.js.map +1 -1
- package/dist/internal/machine/topology.d.ts +5 -5
- package/dist/internal/machine/topology.d.ts.map +1 -1
- package/dist/internal/machine/topology.js +16 -13
- package/dist/internal/machine/topology.js.map +1 -1
- package/dist/internal/testing/machine/verification.d.ts.map +1 -1
- package/dist/internal/testing/machine/verification.js +10 -3
- package/dist/internal/testing/machine/verification.js.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +34 -2
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +23 -0
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +70 -28
- package/package.json +2 -2
- package/src/Machine.ts +420 -309
- package/src/internal/machine/atom.ts +61 -6
- package/src/internal/machine/configuration.ts +102 -23
- package/src/internal/machine/executionPlan.ts +6 -0
- package/src/internal/machine/invocation.ts +1 -1
- package/src/internal/machine/machine.ts +93 -62
- package/src/internal/machine/planner.ts +49 -38
- package/src/internal/machine/serialization.ts +56 -31
- package/src/internal/machine/stateDefinition.ts +22 -11
- package/src/internal/machine/topology.ts +21 -18
- package/src/internal/testing/machine/verification.ts +13 -3
- package/src/unstable/reactivity/AtomMachine.ts +56 -2
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
configurationFromHistoryRecord,
|
|
19
19
|
getActiveLeafPathFrom,
|
|
20
20
|
getActiveLeafPaths,
|
|
21
|
-
getActiveValue,
|
|
22
21
|
getHistoryRecord,
|
|
23
22
|
getInitialEntryPaths,
|
|
24
23
|
getLeafPath,
|
|
@@ -201,66 +200,76 @@ const completeHistoryConfiguration = (
|
|
|
201
200
|
if (node.initial === undefined) {
|
|
202
201
|
throw new Error(`Machine shallow history expected compound state "${path}" to have an initial child`)
|
|
203
202
|
}
|
|
204
|
-
const
|
|
205
|
-
if (initializer === undefined) {
|
|
206
|
-
throw new Error(`Machine shallow history requires an initial value implementation for state "${path}"`)
|
|
207
|
-
}
|
|
203
|
+
const child = getNode(machine, node.initial)
|
|
208
204
|
const current = {
|
|
209
205
|
active,
|
|
210
206
|
values,
|
|
211
207
|
outputs: configuration.outputs,
|
|
212
208
|
history: configuration.history
|
|
213
209
|
} as ActiveConfiguration
|
|
214
|
-
const initialized = collectStateInitializer(machine, initializer, {
|
|
215
|
-
state: getActiveValue(current, path),
|
|
216
|
-
parent: getParentValue(machine, current, path),
|
|
217
|
-
parents: getParentValues(machine, current, path),
|
|
218
|
-
event
|
|
219
|
-
})
|
|
220
|
-
const child = getNode(machine, node.initial)
|
|
221
210
|
active.add(child.path)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
211
|
+
if (child.schema !== undefined) {
|
|
212
|
+
const initializer = machine.handlers[path]?.initial
|
|
213
|
+
if (initializer === undefined) {
|
|
214
|
+
throw new Error(`Machine shallow history requires an initial value implementation for state "${path}"`)
|
|
215
|
+
}
|
|
216
|
+
const initialized = collectStateInitializer(machine, initializer, {
|
|
217
|
+
state: current.values.get(path),
|
|
218
|
+
parent: getParentValue(machine, current, path),
|
|
219
|
+
parents: getParentValues(machine, current, path),
|
|
220
|
+
event
|
|
221
|
+
})
|
|
222
|
+
values.set(child.path, decodeStateValueSync(machine, child, initialized.value))
|
|
223
|
+
commands.push(...initialized.commands)
|
|
224
|
+
raisedEvents.push(...initialized.raisedEvents)
|
|
225
|
+
emittedEvents.push(...initialized.emittedEvents)
|
|
226
|
+
}
|
|
226
227
|
changed = true
|
|
227
228
|
}
|
|
228
229
|
if (node.type === "parallel") {
|
|
229
230
|
const missing = node.children.filter((childPath) => !active.has(childPath))
|
|
230
231
|
if (missing.length > 0) {
|
|
231
|
-
const
|
|
232
|
-
if (initializer === undefined) {
|
|
233
|
-
throw new Error(`Machine shallow history requires an initial value implementation for state "${path}"`)
|
|
234
|
-
}
|
|
232
|
+
const valuedMissing = missing.filter((childPath) => getNode(machine, childPath).schema !== undefined)
|
|
235
233
|
const current = {
|
|
236
234
|
active,
|
|
237
235
|
values,
|
|
238
236
|
outputs: configuration.outputs,
|
|
239
237
|
history: configuration.history
|
|
240
238
|
} as ActiveConfiguration
|
|
241
|
-
const
|
|
242
|
-
|
|
239
|
+
const initializer = valuedMissing.length === 0 ? undefined : machine.handlers[path]?.initial
|
|
240
|
+
if (valuedMissing.length > 0 && initializer === undefined) {
|
|
241
|
+
throw new Error(`Machine shallow history requires an initial value implementation for state "${path}"`)
|
|
242
|
+
}
|
|
243
|
+
const initialized = initializer === undefined ? undefined : collectStateInitializer(machine, initializer, {
|
|
244
|
+
state: current.values.get(path),
|
|
243
245
|
parent: getParentValue(machine, current, path),
|
|
244
246
|
parents: getParentValues(machine, current, path),
|
|
245
247
|
event
|
|
246
248
|
})
|
|
247
|
-
if (typeof initialized.value !== "object" || initialized.value === null) {
|
|
249
|
+
if (initialized !== undefined && (typeof initialized.value !== "object" || initialized.value === null)) {
|
|
248
250
|
throw new Error(`Machine parallel state initializer for "${path}" must return its region values`)
|
|
249
251
|
}
|
|
250
252
|
for (const childPath of missing) {
|
|
251
253
|
const child = getNode(machine, childPath)
|
|
252
|
-
if (!Object.prototype.hasOwnProperty.call(initialized.value, child.key)) {
|
|
253
|
-
throw new Error(`Machine parallel state initializer for "${path}" must return region "${child.key}"`)
|
|
254
|
-
}
|
|
255
254
|
active.add(child.path)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
255
|
+
if (child.schema !== undefined) {
|
|
256
|
+
if (
|
|
257
|
+
initialized === undefined ||
|
|
258
|
+
!Object.prototype.hasOwnProperty.call(initialized.value as object, child.key)
|
|
259
|
+
) {
|
|
260
|
+
throw new Error(`Machine parallel state initializer for "${path}" must return region "${child.key}"`)
|
|
261
|
+
}
|
|
262
|
+
values.set(
|
|
263
|
+
child.path,
|
|
264
|
+
decodeStateValueSync(machine, child, (initialized.value as Record<string, unknown>)[child.key])
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (initialized !== undefined) {
|
|
269
|
+
commands.push(...initialized.commands)
|
|
270
|
+
raisedEvents.push(...initialized.raisedEvents)
|
|
271
|
+
emittedEvents.push(...initialized.emittedEvents)
|
|
260
272
|
}
|
|
261
|
-
commands.push(...initialized.commands)
|
|
262
|
-
raisedEvents.push(...initialized.raisedEvents)
|
|
263
|
-
emittedEvents.push(...initialized.emittedEvents)
|
|
264
273
|
changed = true
|
|
265
274
|
}
|
|
266
275
|
}
|
|
@@ -495,7 +504,7 @@ const makeStateActionContext = <
|
|
|
495
504
|
path: string,
|
|
496
505
|
event: Machine.LifecycleEvent<Events>
|
|
497
506
|
): Machine.StateActionContext<States, Events, Emits, StateId> => ({
|
|
498
|
-
state:
|
|
507
|
+
state: configuration.values.get(path) as Machine.StateByIdentifier<States, StateId>,
|
|
499
508
|
parent: getParentValue(machine, configuration, path) as Machine.ParentStateValue<States, StateId>,
|
|
500
509
|
parents: getParentValues(machine, configuration, path) as Machine.ParentStateValues<States, StateId>,
|
|
501
510
|
event
|
|
@@ -514,7 +523,7 @@ const makeTransitionContext = <
|
|
|
514
523
|
event: Machine.EventByTag<Events, EventTag>,
|
|
515
524
|
snapshot: Machine.Snapshot<States>
|
|
516
525
|
): Machine.HandlerContext<States, Events, Emits, StateId, EventTag, any, any> => ({
|
|
517
|
-
state:
|
|
526
|
+
state: configuration.values.get(path) as Machine.StateByIdentifier<States, StateId>,
|
|
518
527
|
parent: getParentValue(machine, configuration, path) as Machine.ParentStateValue<States, StateId>,
|
|
519
528
|
parents: getParentValues(machine, configuration, path) as Machine.ParentStateValues<States, StateId>,
|
|
520
529
|
event,
|
|
@@ -535,7 +544,7 @@ const makeDoneContext = <
|
|
|
535
544
|
output: unknown,
|
|
536
545
|
snapshot: Machine.Snapshot<States>
|
|
537
546
|
): Machine.DoneContext<States, Events, Emits, StateId> => ({
|
|
538
|
-
state:
|
|
547
|
+
state: configuration.values.get(path) as Machine.StateByIdentifier<States, StateId>,
|
|
539
548
|
parent: getParentValue(machine, configuration, path) as Machine.ParentStateValue<States, StateId>,
|
|
540
549
|
parents: getParentValues(machine, configuration, path) as Machine.ParentStateValues<States, StateId>,
|
|
541
550
|
event,
|
|
@@ -629,7 +638,7 @@ const selectAlwaysTransitions = <
|
|
|
629
638
|
Machine.AlwaysContext<States, Events, Emits, Machine.StateIdentifier<States>>
|
|
630
639
|
>,
|
|
631
640
|
context: {
|
|
632
|
-
state:
|
|
641
|
+
state: configuration.values.get(path) as Machine.StateByIdentifier<
|
|
633
642
|
States,
|
|
634
643
|
Machine.StateIdentifier<States>
|
|
635
644
|
>,
|
|
@@ -919,7 +928,9 @@ const choicesFromTarget = (
|
|
|
919
928
|
return
|
|
920
929
|
}
|
|
921
930
|
if (typeof current !== "object" || current === null || !("path" in current) || !("value" in current)) return
|
|
922
|
-
|
|
931
|
+
if (current.value !== undefined) {
|
|
932
|
+
values[String(current.path)] = current.value
|
|
933
|
+
}
|
|
923
934
|
if ("state" in current) visit(current.state)
|
|
924
935
|
if ("states" in current && typeof current.states === "object" && current.states !== null) {
|
|
925
936
|
for (const state of Object.values(current.states)) visit(state)
|
|
@@ -31,7 +31,7 @@ const EncodedSnapshotSchema = Schema.Struct({
|
|
|
31
31
|
_tag: Schema.Literal("MachineSnapshot"),
|
|
32
32
|
active: Schema.Array(Schema.Struct({
|
|
33
33
|
path: Schema.String,
|
|
34
|
-
value: Schema.Unknown
|
|
34
|
+
value: Schema.optional(Schema.Unknown)
|
|
35
35
|
})),
|
|
36
36
|
completed: Schema.optional(Schema.Array(Schema.Struct({
|
|
37
37
|
path: Schema.String,
|
|
@@ -220,13 +220,17 @@ export const encodeSnapshot = (
|
|
|
220
220
|
const path of Array.from(configuration.active).sort((left, right) => compareDocumentOrder(machine, left, right))
|
|
221
221
|
) {
|
|
222
222
|
const node = getNode(machine, path)
|
|
223
|
-
|
|
224
|
-
path
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
if (node.schema === undefined) {
|
|
224
|
+
active.push({ path })
|
|
225
|
+
} else {
|
|
226
|
+
active.push({
|
|
227
|
+
path,
|
|
228
|
+
value: yield* encodeBoundary(machine, getStateNodeSchema(node), getActiveValue(configuration, path), {
|
|
229
|
+
boundary: "state",
|
|
230
|
+
state: path
|
|
231
|
+
})
|
|
228
232
|
})
|
|
229
|
-
}
|
|
233
|
+
}
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
const completed: Array<Machine.EncodedSnapshotCompletion> = []
|
|
@@ -290,7 +294,6 @@ export const encodeSnapshot = (
|
|
|
290
294
|
const stateNode = machine.stateNodes.byPath.get(path)
|
|
291
295
|
if (
|
|
292
296
|
stateNode === undefined || stateNode.type === "history" || stateNode.type === "choice" ||
|
|
293
|
-
!record.values.has(path) ||
|
|
294
297
|
!(isPathInSubtree(path, record.parent) || getPathToRoot(machine, record.parent).includes(path))
|
|
295
298
|
) {
|
|
296
299
|
return yield* Effect.fail(
|
|
@@ -302,14 +305,23 @@ export const encodeSnapshot = (
|
|
|
302
305
|
})
|
|
303
306
|
)
|
|
304
307
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
308
|
+
if (stateNode.schema === undefined) {
|
|
309
|
+
if (record.values.has(path)) {
|
|
310
|
+
throw new Error(`Machine history record contains a value for structural state "${path}"`)
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
if (!record.values.has(path)) {
|
|
314
|
+
throw new Error(`Machine history record omits value for "${path}"`)
|
|
315
|
+
}
|
|
316
|
+
encodedValues[path] = yield* encodeBoundary(
|
|
317
|
+
machine,
|
|
318
|
+
getStateNodeSchema(stateNode),
|
|
319
|
+
record.values.get(path),
|
|
320
|
+
{ boundary: "history", state: path }
|
|
321
|
+
)
|
|
322
|
+
}
|
|
311
323
|
}
|
|
312
|
-
if (
|
|
324
|
+
if (Object.keys(encodedValues).length !== record.values.size) {
|
|
313
325
|
return yield* Effect.fail(
|
|
314
326
|
new MachineSchemaEncodeError({
|
|
315
327
|
machineId: machine.id,
|
|
@@ -356,13 +368,19 @@ export const decodeSnapshot = (
|
|
|
356
368
|
}
|
|
357
369
|
const node = getNode(machine, entry.path)
|
|
358
370
|
active.add(entry.path)
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
371
|
+
const hasValue = Object.prototype.hasOwnProperty.call(entry, "value")
|
|
372
|
+
if (node.schema === undefined) {
|
|
373
|
+
if (hasValue) throw new Error(`Machine encoded snapshot contains a value for structural state "${entry.path}"`)
|
|
374
|
+
} else {
|
|
375
|
+
if (!hasValue) throw new Error(`Machine encoded snapshot omits value for state "${entry.path}"`)
|
|
376
|
+
values.set(
|
|
377
|
+
entry.path,
|
|
378
|
+
yield* decodeEncodedBoundary(machine, getStateNodeSchema(node), entry.value, {
|
|
379
|
+
boundary: "state",
|
|
380
|
+
state: entry.path
|
|
381
|
+
})
|
|
382
|
+
)
|
|
383
|
+
}
|
|
366
384
|
}
|
|
367
385
|
|
|
368
386
|
const history = new Map<string, HistoryRecord>()
|
|
@@ -397,7 +415,6 @@ export const decodeSnapshot = (
|
|
|
397
415
|
const stateNode = machine.stateNodes.byPath.get(path)
|
|
398
416
|
if (
|
|
399
417
|
stateNode === undefined || stateNode.type === "history" || stateNode.type === "choice" ||
|
|
400
|
-
!Object.prototype.hasOwnProperty.call(encodedRecord.values, path) ||
|
|
401
418
|
!(isPathInSubtree(path, historyNode.parent) || getPathToRoot(machine, historyNode.parent).includes(path))
|
|
402
419
|
) {
|
|
403
420
|
return yield* Effect.fail(
|
|
@@ -410,15 +427,23 @@ export const decodeSnapshot = (
|
|
|
410
427
|
)
|
|
411
428
|
}
|
|
412
429
|
rememberedActive.add(path)
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
430
|
+
const hasValue = Object.prototype.hasOwnProperty.call(encodedRecord.values, path)
|
|
431
|
+
if (stateNode.schema === undefined) {
|
|
432
|
+
if (hasValue) {
|
|
433
|
+
throw new Error(`Machine encoded history contains a value for structural state "${path}"`)
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
if (!hasValue) throw new Error(`Machine encoded history omits value for state "${path}"`)
|
|
437
|
+
rememberedValues.set(
|
|
438
|
+
path,
|
|
439
|
+
yield* decodeEncodedBoundary(machine, getStateNodeSchema(stateNode), encodedRecord.values[path], {
|
|
440
|
+
boundary: "history",
|
|
441
|
+
state: path
|
|
442
|
+
})
|
|
443
|
+
)
|
|
444
|
+
}
|
|
420
445
|
}
|
|
421
|
-
if (Object.keys(encodedRecord.values).length !==
|
|
446
|
+
if (Object.keys(encodedRecord.values).length !== rememberedValues.size) {
|
|
422
447
|
return yield* Effect.fail(
|
|
423
448
|
new MachineSchemaDecodeError({
|
|
424
449
|
machineId: machine.id,
|
|
@@ -2,10 +2,10 @@ import * as Schema from "effect/Schema"
|
|
|
2
2
|
import * as SchemaAST from "effect/SchemaAST"
|
|
3
3
|
|
|
4
4
|
export const StateNodePropertyPolicy = {
|
|
5
|
-
atomic: ["schema", "type"],
|
|
6
|
-
final: ["schema", "type", "output"],
|
|
7
|
-
compound: ["schema", "type", "initial", "states"],
|
|
8
|
-
parallel: ["schema", "type", "output", "states"],
|
|
5
|
+
atomic: ["schema", "type", "annotations"],
|
|
6
|
+
final: ["schema", "type", "output", "annotations"],
|
|
7
|
+
compound: ["schema", "type", "initial", "states", "annotations"],
|
|
8
|
+
parallel: ["schema", "type", "output", "states", "annotations"],
|
|
9
9
|
history: ["type", "history", "annotations"],
|
|
10
10
|
choice: ["type", "annotations"]
|
|
11
11
|
} as const
|
|
@@ -152,18 +152,22 @@ const assertAllowedProperties = (
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
const
|
|
155
|
+
const validateSchemaLessAnnotations = (
|
|
156
156
|
boundary: StateDefinitionBoundary,
|
|
157
157
|
path: string,
|
|
158
158
|
annotations: unknown
|
|
159
159
|
): void => {
|
|
160
|
-
assertPlainRecord(boundary, `${path}.annotations`, annotations, "
|
|
160
|
+
assertPlainRecord(boundary, `${path}.annotations`, annotations, "schema-less state annotations")
|
|
161
161
|
for (const property of Reflect.ownKeys(annotations)) {
|
|
162
162
|
if (!(PseudoStateAnnotationProperties as ReadonlyArray<PropertyKey>).includes(property)) {
|
|
163
|
-
fail(
|
|
163
|
+
fail(
|
|
164
|
+
boundary,
|
|
165
|
+
`${path}.annotations`,
|
|
166
|
+
`schema-less state annotations cannot declare property "${String(property)}"`
|
|
167
|
+
)
|
|
164
168
|
}
|
|
165
169
|
if (typeof annotations[property] !== "string") {
|
|
166
|
-
fail(boundary, `${path}.annotations.${String(property)}`, "
|
|
170
|
+
fail(boundary, `${path}.annotations.${String(property)}`, "schema-less state annotation values must be strings")
|
|
167
171
|
}
|
|
168
172
|
}
|
|
169
173
|
}
|
|
@@ -211,7 +215,7 @@ const validateStateTree = (
|
|
|
211
215
|
fail(boundary, path, `${kind} states must be declared below an active parent state`)
|
|
212
216
|
}
|
|
213
217
|
if (hasOwn(node, "annotations")) {
|
|
214
|
-
|
|
218
|
+
validateSchemaLessAnnotations(boundary, path, node.annotations)
|
|
215
219
|
}
|
|
216
220
|
if (kind === "history" && hasOwn(node, "history") && node.history !== "shallow" && node.history !== "deep") {
|
|
217
221
|
fail(boundary, `${path}.history`, "history must be \"shallow\" or \"deep\"")
|
|
@@ -219,8 +223,15 @@ const validateStateTree = (
|
|
|
219
223
|
continue
|
|
220
224
|
}
|
|
221
225
|
|
|
222
|
-
|
|
223
|
-
|
|
226
|
+
const valued = hasOwn(node, "schema")
|
|
227
|
+
if (valued && !isTaggedSchema(node.schema)) {
|
|
228
|
+
fail(boundary, `${path}.schema`, "state schemas must decode to an object with a required PropertyKey _tag")
|
|
229
|
+
}
|
|
230
|
+
if (valued && hasOwn(node, "annotations")) {
|
|
231
|
+
fail(boundary, `${path}.annotations`, "schema-backed states must declare annotations on their schema")
|
|
232
|
+
}
|
|
233
|
+
if (!valued && hasOwn(node, "annotations")) {
|
|
234
|
+
validateSchemaLessAnnotations(boundary, path, node.annotations)
|
|
224
235
|
}
|
|
225
236
|
if (kind === "atomic" && hasOwn(node, "type") && node.type !== "active") {
|
|
226
237
|
fail(boundary, `${path}.type`, "atomic state type must be \"active\"")
|
|
@@ -70,7 +70,7 @@ interface NormalizedStateNodeDefinitionBase {
|
|
|
70
70
|
type NormalizedStateNodeDefinition =
|
|
71
71
|
| (NormalizedStateNodeDefinitionBase & {
|
|
72
72
|
readonly type: "atomic"
|
|
73
|
-
readonly schema: Machine.TaggedSchema
|
|
73
|
+
readonly schema: Machine.TaggedSchema | undefined
|
|
74
74
|
readonly output: undefined
|
|
75
75
|
readonly history: undefined
|
|
76
76
|
readonly initial: undefined
|
|
@@ -78,7 +78,7 @@ type NormalizedStateNodeDefinition =
|
|
|
78
78
|
})
|
|
79
79
|
| (NormalizedStateNodeDefinitionBase & {
|
|
80
80
|
readonly type: "compound"
|
|
81
|
-
readonly schema: Machine.TaggedSchema
|
|
81
|
+
readonly schema: Machine.TaggedSchema | undefined
|
|
82
82
|
readonly output: undefined
|
|
83
83
|
readonly history: undefined
|
|
84
84
|
readonly initial: string
|
|
@@ -86,7 +86,7 @@ type NormalizedStateNodeDefinition =
|
|
|
86
86
|
})
|
|
87
87
|
| (NormalizedStateNodeDefinitionBase & {
|
|
88
88
|
readonly type: "parallel"
|
|
89
|
-
readonly schema: Machine.TaggedSchema
|
|
89
|
+
readonly schema: Machine.TaggedSchema | undefined
|
|
90
90
|
readonly output: Schema.Top | undefined
|
|
91
91
|
readonly history: undefined
|
|
92
92
|
readonly initial: undefined
|
|
@@ -94,7 +94,7 @@ type NormalizedStateNodeDefinition =
|
|
|
94
94
|
})
|
|
95
95
|
| (NormalizedStateNodeDefinitionBase & {
|
|
96
96
|
readonly type: "final"
|
|
97
|
-
readonly schema: Machine.TaggedSchema
|
|
97
|
+
readonly schema: Machine.TaggedSchema | undefined
|
|
98
98
|
readonly output: Schema.Top | undefined
|
|
99
99
|
readonly history: undefined
|
|
100
100
|
readonly initial: undefined
|
|
@@ -154,9 +154,10 @@ export const getStateNodeDefinition = (
|
|
|
154
154
|
states: undefined
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
const schema = hasProperty(definition, "schema") && Schema.isSchema(definition.schema)
|
|
158
|
+
? definition.schema as Machine.TaggedSchema
|
|
159
|
+
: undefined
|
|
160
|
+
const annotations = schema === undefined ? definition.annotations : Schema.resolveAnnotations(schema)
|
|
160
161
|
if (definition.type === "parallel" && !hasProperty(definition, "states")) {
|
|
161
162
|
throw new Error(`Machine.make expected parallel state "${path}" to declare child regions`)
|
|
162
163
|
}
|
|
@@ -167,9 +168,9 @@ export const getStateNodeDefinition = (
|
|
|
167
168
|
}
|
|
168
169
|
if (definition.type === "parallel") {
|
|
169
170
|
return {
|
|
170
|
-
schema
|
|
171
|
+
schema,
|
|
171
172
|
output: Schema.isSchema(definition.output) ? definition.output : undefined,
|
|
172
|
-
annotations
|
|
173
|
+
annotations,
|
|
173
174
|
type: "parallel",
|
|
174
175
|
history: undefined,
|
|
175
176
|
initial: undefined,
|
|
@@ -180,9 +181,9 @@ export const getStateNodeDefinition = (
|
|
|
180
181
|
throw new Error(`Machine.make expected compound state "${path}" to declare an initial child`)
|
|
181
182
|
}
|
|
182
183
|
return {
|
|
183
|
-
schema
|
|
184
|
+
schema,
|
|
184
185
|
output: undefined,
|
|
185
|
-
annotations
|
|
186
|
+
annotations,
|
|
186
187
|
type: "compound",
|
|
187
188
|
history: undefined,
|
|
188
189
|
initial: definition.initial,
|
|
@@ -192,18 +193,18 @@ export const getStateNodeDefinition = (
|
|
|
192
193
|
const output = Schema.isSchema(definition.output) ? definition.output : undefined
|
|
193
194
|
return definition.type === "final"
|
|
194
195
|
? {
|
|
195
|
-
schema
|
|
196
|
+
schema,
|
|
196
197
|
output,
|
|
197
|
-
annotations
|
|
198
|
+
annotations,
|
|
198
199
|
type: "final",
|
|
199
200
|
history: undefined,
|
|
200
201
|
initial: undefined,
|
|
201
202
|
states: undefined
|
|
202
203
|
}
|
|
203
204
|
: {
|
|
204
|
-
schema
|
|
205
|
+
schema,
|
|
205
206
|
output: undefined,
|
|
206
|
-
annotations
|
|
207
|
+
annotations,
|
|
207
208
|
type: "atomic",
|
|
208
209
|
history: undefined,
|
|
209
210
|
initial: undefined,
|
|
@@ -405,7 +406,7 @@ export const makeTarget = <
|
|
|
405
406
|
readonly snapshot?: Machine.SnapshotByIdentifier<States, StateId>
|
|
406
407
|
readonly values?: Partial<
|
|
407
408
|
{
|
|
408
|
-
readonly [AncestorStateId in Machine.
|
|
409
|
+
readonly [AncestorStateId in Machine.ValuedStateIdentifier<States>]: Machine.StateByIdentifier<
|
|
409
410
|
States,
|
|
410
411
|
AncestorStateId
|
|
411
412
|
>
|
|
@@ -445,7 +446,9 @@ export const getSnapshotByPath = (
|
|
|
445
446
|
return Option.none()
|
|
446
447
|
}
|
|
447
448
|
if (parents !== undefined) {
|
|
448
|
-
|
|
449
|
+
if (snapshot.value !== undefined) {
|
|
450
|
+
parents[snapshot.path] = snapshot.value
|
|
451
|
+
}
|
|
449
452
|
}
|
|
450
453
|
if (hasProperty(snapshot, "state") && isSnapshot(snapshot.state)) {
|
|
451
454
|
return getSnapshotByPath(snapshot.state, path, parents)
|
|
@@ -473,7 +476,7 @@ export const getNode = (machine: Machine.Any, path: string): Machine.StateNode =
|
|
|
473
476
|
|
|
474
477
|
export const getStateNodeSchema = (node: Machine.StateNode): Machine.TaggedSchema => {
|
|
475
478
|
if (node.schema === undefined) {
|
|
476
|
-
throw new Error(`Machine
|
|
479
|
+
throw new Error(`Machine state "${node.path}" has no value schema`)
|
|
477
480
|
}
|
|
478
481
|
return node.schema
|
|
479
482
|
}
|
|
@@ -1114,7 +1114,9 @@ export const verify = <M extends AnyMachine>(
|
|
|
1114
1114
|
}
|
|
1115
1115
|
return
|
|
1116
1116
|
}
|
|
1117
|
-
if (reportConfiguration &&
|
|
1117
|
+
if (reportConfiguration && node.schema === undefined && current.value !== undefined) {
|
|
1118
|
+
add("configuration.schema", location, `${label} structural state "${path}" contains a value`, path)
|
|
1119
|
+
} else if (reportConfiguration && node.schema !== undefined && !schemaMatches(node.schema, current.value)) {
|
|
1118
1120
|
add("configuration.schema", location, `${label} value for "${path}" does not match its schema`, path)
|
|
1119
1121
|
}
|
|
1120
1122
|
|
|
@@ -1284,9 +1286,17 @@ export const verify = <M extends AnyMachine>(
|
|
|
1284
1286
|
path
|
|
1285
1287
|
)
|
|
1286
1288
|
}
|
|
1287
|
-
|
|
1289
|
+
const hasValue = hasOwn(entry.values, path)
|
|
1290
|
+
if (node.schema === undefined && hasValue) {
|
|
1291
|
+
add(
|
|
1292
|
+
"history.value",
|
|
1293
|
+
location,
|
|
1294
|
+
`${label} history record "${historyPath}" values structural state "${path}"`,
|
|
1295
|
+
path
|
|
1296
|
+
)
|
|
1297
|
+
} else if (node.schema !== undefined && !hasValue) {
|
|
1288
1298
|
add("history.value", location, `${label} history record "${historyPath}" omits value for "${path}"`, path)
|
|
1289
|
-
} else if (!schemaMatches(node.schema, entry.values[path])) {
|
|
1299
|
+
} else if (node.schema !== undefined && !schemaMatches(node.schema, entry.values[path])) {
|
|
1290
1300
|
add(
|
|
1291
1301
|
"history.value",
|
|
1292
1302
|
location,
|
|
@@ -291,10 +291,21 @@ type SnapshotIdentifier<State> = SnapshotNode<State> extends infer Node ?
|
|
|
291
291
|
Node extends { readonly path: infer Path extends string } ? Path : never
|
|
292
292
|
: never
|
|
293
293
|
|
|
294
|
+
type ValuedSnapshotIdentifier<State> = SnapshotNode<State> extends infer Node ?
|
|
295
|
+
Node extends { readonly path: infer Path extends string; readonly value: infer Value } ?
|
|
296
|
+
[Value] extends [undefined] ? never
|
|
297
|
+
: Path
|
|
298
|
+
: never
|
|
299
|
+
: never
|
|
300
|
+
|
|
294
301
|
type SnapshotValueByIdentifier<State, Path extends SnapshotIdentifier<State>> = SnapshotNode<State> extends infer Node ?
|
|
295
302
|
Node extends { readonly path: Path; readonly value: infer Value } ? Value : never
|
|
296
303
|
: never
|
|
297
304
|
|
|
305
|
+
type SnapshotByIdentifier<State, Path extends SnapshotIdentifier<State>> = SnapshotNode<State> extends infer Node ?
|
|
306
|
+
Node extends { readonly path: Path } ? Node : never
|
|
307
|
+
: never
|
|
308
|
+
|
|
298
309
|
type ChildState<Child extends Machine.ChildMachine.Any> = RefState<Machine.ChildMachine.Ref<Child>>
|
|
299
310
|
|
|
300
311
|
/**
|
|
@@ -334,11 +345,32 @@ export const select: <
|
|
|
334
345
|
Error,
|
|
335
346
|
Output,
|
|
336
347
|
StartError,
|
|
337
|
-
const Path extends
|
|
348
|
+
const Path extends ValuedSnapshotIdentifier<State>
|
|
338
349
|
>(self: MachineAtom<State, Event, Error, Output, StartError>, path: Path) => Atom.Atom<
|
|
339
350
|
AsyncResult.AsyncResult<Option.Option<SnapshotValueByIdentifier<State, Path>>, StartError | Error>
|
|
340
351
|
> = internal.select
|
|
341
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Selects the typed logical snapshot for an active state path.
|
|
355
|
+
*
|
|
356
|
+
* Unlike {@link select}, the selected value retains its child snapshot
|
|
357
|
+
* topology. The derived atom suppresses structurally equal updates. Keep the
|
|
358
|
+
* returned atom stable when constructing it inside a component.
|
|
359
|
+
*
|
|
360
|
+
* @category combinators
|
|
361
|
+
* @since 0.7.0
|
|
362
|
+
*/
|
|
363
|
+
export const selectSnapshot: <
|
|
364
|
+
State extends Machine.Machine.AtomicSnapshot<string, unknown>,
|
|
365
|
+
Event,
|
|
366
|
+
Error,
|
|
367
|
+
Output,
|
|
368
|
+
StartError,
|
|
369
|
+
const Path extends SnapshotIdentifier<State>
|
|
370
|
+
>(self: MachineAtom<State, Event, Error, Output, StartError>, path: Path) => Atom.Atom<
|
|
371
|
+
AsyncResult.AsyncResult<Option.Option<SnapshotByIdentifier<State, Path>>, StartError | Error>
|
|
372
|
+
> = internal.selectSnapshot
|
|
373
|
+
|
|
342
374
|
/**
|
|
343
375
|
* Selects the typed value for an active state path in an invoked child.
|
|
344
376
|
*
|
|
@@ -359,7 +391,7 @@ export const select: <
|
|
|
359
391
|
export const selectChild: <
|
|
360
392
|
Child extends Machine.ChildMachine.Any,
|
|
361
393
|
StartError,
|
|
362
|
-
const Path extends
|
|
394
|
+
const Path extends ValuedSnapshotIdentifier<ChildState<Child>>
|
|
363
395
|
>(self: ChildMachineAtom<Child, StartError>, path: Path) => Atom.Atom<
|
|
364
396
|
AsyncResult.AsyncResult<
|
|
365
397
|
Option.Option<SnapshotValueByIdentifier<ChildState<Child>, Path>>,
|
|
@@ -367,6 +399,28 @@ export const selectChild: <
|
|
|
367
399
|
>
|
|
368
400
|
> = internal.selectChild
|
|
369
401
|
|
|
402
|
+
/**
|
|
403
|
+
* Selects the typed logical snapshot for an active state path in an invoked
|
|
404
|
+
* child.
|
|
405
|
+
*
|
|
406
|
+
* An inactive child or state path produces `Option.none()`. Unlike
|
|
407
|
+
* {@link selectChild}, the selected value retains its child snapshot topology.
|
|
408
|
+
* The derived atom suppresses structurally equal updates.
|
|
409
|
+
*
|
|
410
|
+
* @category combinators
|
|
411
|
+
* @since 0.7.0
|
|
412
|
+
*/
|
|
413
|
+
export const selectSnapshotChild: <
|
|
414
|
+
Child extends Machine.ChildMachine.Any,
|
|
415
|
+
StartError,
|
|
416
|
+
const Path extends SnapshotIdentifier<ChildState<Child>>
|
|
417
|
+
>(self: ChildMachineAtom<Child, StartError>, path: Path) => Atom.Atom<
|
|
418
|
+
AsyncResult.AsyncResult<
|
|
419
|
+
Option.Option<SnapshotByIdentifier<ChildState<Child>, Path>>,
|
|
420
|
+
StartError | RefError<Machine.ChildMachine.Ref<Child>>
|
|
421
|
+
>
|
|
422
|
+
> = internal.selectSnapshotChild
|
|
423
|
+
|
|
370
424
|
/**
|
|
371
425
|
* Returns whether a state path is active.
|
|
372
426
|
*
|