xstate 6.0.0-beta.1 → 6.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/{createActor-551369be.development.esm.js → createActor-0250284a.development.esm.js} +244 -41
  2. package/dist/{createActor-cbe3abac.esm.js → createActor-20a97166.esm.js} +243 -40
  3. package/dist/{createActor-a49ae0ce.development.cjs.js → createActor-c029d29f.development.cjs.js} +245 -40
  4. package/dist/{createActor-25900f76.cjs.js → createActor-c91c9ffe.cjs.js} +244 -39
  5. package/dist/declarations/src/createActor.d.ts +3 -1
  6. package/dist/declarations/src/createMachine.d.ts +6 -3
  7. package/dist/declarations/src/durable/index.d.ts +43 -8
  8. package/dist/declarations/src/effectDescriptor.d.ts +2 -2
  9. package/dist/declarations/src/machineVersion.types.d.ts +2 -2
  10. package/dist/declarations/src/setup.d.ts +2 -2
  11. package/dist/declarations/src/systemTransition.d.ts +1 -1
  12. package/dist/declarations/src/types.d.ts +4 -2
  13. package/dist/declarations/src/types.v6.d.ts +37 -2
  14. package/dist/{effectDescriptor-f73da4ec.development.cjs.js → effectDescriptor-3fff5c51.development.cjs.js} +5 -5
  15. package/dist/{effectDescriptor-2fa27c54.cjs.js → effectDescriptor-47f73e7e.cjs.js} +5 -5
  16. package/dist/{effectDescriptor-321a8ce9.development.esm.js → effectDescriptor-5ac7dccb.development.esm.js} +5 -5
  17. package/dist/{effectDescriptor-d8b0f749.esm.js → effectDescriptor-e93df910.esm.js} +5 -5
  18. package/dist/{machineSchemas-88d01821.cjs.js → legacySnapshot-8d595687.cjs.js} +117 -1
  19. package/dist/{machineSchemas-45c21bc2.esm.js → legacySnapshot-9e06feb4.esm.js} +116 -2
  20. package/dist/{machineSchemas-b5736804.development.esm.js → legacySnapshot-ae6594a7.development.esm.js} +117 -3
  21. package/dist/{machineSchemas-6d96bef4.development.cjs.js → legacySnapshot-ebf0b2b8.development.cjs.js} +118 -2
  22. package/dist/xstate-actors.cjs.js +1 -1
  23. package/dist/xstate-actors.development.cjs.js +1 -1
  24. package/dist/xstate-actors.development.esm.js +2 -2
  25. package/dist/xstate-actors.esm.js +2 -2
  26. package/dist/xstate-actors.umd.min.js +1 -1
  27. package/dist/xstate-actors.umd.min.js.map +1 -1
  28. package/dist/xstate-durable.cjs.js +120 -10
  29. package/dist/xstate-durable.development.cjs.js +120 -10
  30. package/dist/xstate-durable.development.esm.js +120 -10
  31. package/dist/xstate-durable.esm.js +120 -10
  32. package/dist/xstate-durable.umd.min.js +1 -1
  33. package/dist/xstate-durable.umd.min.js.map +1 -1
  34. package/dist/xstate-fsm.cjs.js +4 -4
  35. package/dist/xstate-fsm.development.cjs.js +4 -4
  36. package/dist/xstate-fsm.development.esm.js +4 -4
  37. package/dist/xstate-fsm.esm.js +4 -4
  38. package/dist/xstate-fsm.umd.min.js +1 -1
  39. package/dist/xstate-fsm.umd.min.js.map +1 -1
  40. package/dist/xstate-graph.cjs.js +1 -1
  41. package/dist/xstate-graph.development.cjs.js +1 -1
  42. package/dist/xstate-graph.development.esm.js +2 -2
  43. package/dist/xstate-graph.esm.js +2 -2
  44. package/dist/xstate-graph.umd.min.js +1 -1
  45. package/dist/xstate-graph.umd.min.js.map +1 -1
  46. package/dist/xstate-persist.cjs.js +23 -12
  47. package/dist/xstate-persist.development.cjs.js +23 -12
  48. package/dist/xstate-persist.development.esm.js +17 -6
  49. package/dist/xstate-persist.esm.js +17 -6
  50. package/dist/xstate-persist.umd.min.js +1 -1
  51. package/dist/xstate-persist.umd.min.js.map +1 -1
  52. package/dist/xstate-validation.cjs.js +1 -1
  53. package/dist/xstate-validation.development.cjs.js +1 -1
  54. package/dist/xstate-validation.development.esm.js +1 -1
  55. package/dist/xstate-validation.esm.js +1 -1
  56. package/dist/xstate.cjs.js +63 -51
  57. package/dist/xstate.development.cjs.js +63 -51
  58. package/dist/xstate.development.esm.js +53 -41
  59. package/dist/xstate.esm.js +53 -41
  60. package/dist/xstate.umd.min.js +1 -1
  61. package/dist/xstate.umd.min.js.map +1 -1
  62. package/docs/api-stability.json +267 -249
  63. package/docs/backend-workflows.md +3 -3
  64. package/docs/durable-execution.md +72 -48
  65. package/docs/errors.md +2 -2
  66. package/docs/examples.md +34 -34
  67. package/docs/fsm.md +2 -1
  68. package/docs/guards.md +1 -1
  69. package/docs/index.md +1 -1
  70. package/docs/invoke.md +34 -2
  71. package/docs/persistence.md +16 -10
  72. package/docs/serialization.md +1 -1
  73. package/docs/setup-and-provide.md +3 -3
  74. package/docs/spawn.md +1 -1
  75. package/docs/system-transitions.md +6 -6
  76. package/docs/tutorial.md +5 -5
  77. package/docs/typescript.md +9 -0
  78. package/docs/xstate-v5-to-v6.md +17 -17
  79. package/package.json +1 -1
@@ -3,9 +3,9 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var dist_xstatePersist = require('./xstate-persist.cjs.js');
6
- var createActor = require('./createActor-25900f76.cjs.js');
7
- var effectDescriptor = require('./effectDescriptor-2fa27c54.cjs.js');
8
- require('./machineSchemas-88d01821.cjs.js');
6
+ var createActor = require('./createActor-c91c9ffe.cjs.js');
7
+ var effectDescriptor = require('./effectDescriptor-47f73e7e.cjs.js');
8
+ require('./legacySnapshot-8d595687.cjs.js');
9
9
  require('./validation-4561b43c.cjs.js');
10
10
 
11
11
  /** @experimental */
@@ -20,6 +20,36 @@ require('./validation-4561b43c.cjs.js');
20
20
  * @experimental
21
21
  */
22
22
 
23
+ /**
24
+ * A root-addressed event that the execution received but has not processed,
25
+ * stored in a checkpoint. `source` is the sender's logical address.
26
+ *
27
+ * @experimental
28
+ */
29
+
30
+ /**
31
+ * Everything needed to resume an execution: the persisted snapshot, the next
32
+ * transition index and the events received but not yet processed. Persist
33
+ * the whole object atomically and pass it to `restore()`.
34
+ *
35
+ * @experimental
36
+ */
37
+
38
+ // Lifecycle events that address a child by `actorId`. Snapshot updates are
39
+ // not stored: a restored child reports its current snapshot when it starts.
40
+ const storedLifecycleEvents = new Set(['xstate.done.actor', 'xstate.error.actor', 'xstate.timeout.actor']);
41
+ function isCheckpoint(value) {
42
+ return !!value && typeof value === 'object' && 'inbox' in value && 'nextTransitionIndex' in value && 'snapshot' in value;
43
+ }
44
+ function isInboxEntry(item) {
45
+ if (!item || typeof item !== 'object') return false;
46
+ const {
47
+ event,
48
+ source
49
+ } = item;
50
+ return !!event && typeof event.type === 'string' && (source === undefined || !!source && typeof source['@actor'] === 'string' && (source.incarnation === undefined || typeof source.incarnation === 'string'));
51
+ }
52
+
23
53
  /** @experimental */
24
54
 
25
55
  /**
@@ -149,6 +179,15 @@ function createDurable(logic, adapter, options) {
149
179
  // loop to take them through `waitForEvent()`.
150
180
  const pendingRootEvents = [];
151
181
 
182
+ // For a stored lifecycle event: the child it addresses in `snapshot`, or
183
+ // `null` when that child is gone. `undefined` for any other event.
184
+ const lifecycleChild = (snapshot, event) => {
185
+ if (!createActor.isMachineSnapshot(snapshot) || !storedLifecycleEvents.has(event.type) || typeof event.actorId !== 'string') {
186
+ return undefined;
187
+ }
188
+ return snapshot.children[event.actorId] ?? null;
189
+ };
190
+
152
191
  // Every inter-actor edge is an async handoff to the runtime, and every
153
192
  // handoff queues behind the last one: hosts whose step or activity model
154
193
  // forbids concurrent entries depend on that, and so does deterministic
@@ -289,6 +328,16 @@ function createDurable(logic, adapter, options) {
289
328
  }
290
329
  const inspect = options?.inspect;
291
330
 
331
+ // Runs a transition as this execution: views it derives belong to this
332
+ // execution, and its systems report to the execution's inspector.
333
+ const owner = {
334
+ runtime: wrappedSystemRuntime,
335
+ inspect
336
+ };
337
+ function inExecution(fn) {
338
+ return createActor.withExecutionOwner(owner, () => createActor.withSystemInspector(inspect, fn));
339
+ }
340
+
292
341
  // Attaches the execution's inspector to a system that does not have it
293
342
  // yet. Systems constructed inside a transition get it at construction (the
294
343
  // ambient inspector); this covers systems materialized outside that window
@@ -319,10 +368,12 @@ function createDurable(logic, adapter, options) {
319
368
  const visit = actor => {
320
369
  if (!systems.has(actor.system)) {
321
370
  systems.add(actor.system);
322
- if (wrappedSystemRuntime) {
323
- actor.system.runtime = wrappedSystemRuntime;
371
+ if (createActor.adoptSystem(actor.system, owner)) {
372
+ if (wrappedSystemRuntime) {
373
+ actor.system.runtime = wrappedSystemRuntime;
374
+ }
375
+ wireInspection(actor.system);
324
376
  }
325
- wireInspection(actor.system);
326
377
  }
327
378
  if (recursive) {
328
379
  const children = actor.getSnapshot().children;
@@ -354,14 +405,55 @@ function createDurable(logic, adapter, options) {
354
405
  get nextTransitionIndex() {
355
406
  return nextTransitionIndex;
356
407
  },
357
- restore(persistedSnapshot) {
408
+ checkpoint(snapshot, persistOptions) {
409
+ const inbox = [];
410
+ for (const {
411
+ event,
412
+ source
413
+ } of pendingRootEvents) {
414
+ const child = lifecycleChild(snapshot, event);
415
+ if (event.type === 'xstate.snapshot.actor' || child !== undefined && (!child || event.sessionId !== undefined && child.sessionId !== undefined && child.sessionId !== event.sessionId)) {
416
+ continue;
417
+ }
418
+ inbox.push({
419
+ event,
420
+ source: source && {
421
+ '@actor': source.address,
422
+ ...(createActor.isRemoteActorRef(source) && source.sessionId !== undefined ? {
423
+ incarnation: source.sessionId
424
+ } : {})
425
+ }
426
+ });
427
+ }
428
+ return {
429
+ snapshot: logic.getPersistedSnapshot?.(snapshot, persistOptions) ?? snapshot,
430
+ nextTransitionIndex,
431
+ inbox
432
+ };
433
+ },
434
+ restore(checkpointOrSnapshot) {
435
+ const checkpoint = isCheckpoint(checkpointOrSnapshot) ? checkpointOrSnapshot : undefined;
358
436
  if (restored || nextTransitionIndex !== startingTransitionIndex || currentBatch) {
359
437
  throw new Error('restore() requires a fresh durable execution');
360
438
  }
439
+ if (checkpoint) {
440
+ if (!Array.isArray(checkpoint.inbox) || !checkpoint.inbox.every(isInboxEntry)) {
441
+ throw new TypeError('The checkpoint inbox is invalid.');
442
+ }
443
+ if (adapter.transitionIndex !== undefined && adapter.transitionIndex !== checkpoint.nextTransitionIndex) {
444
+ throw new RangeError(`The adapter's transitionIndex (${adapter.transitionIndex}) does not match the checkpoint's nextTransitionIndex (${checkpoint.nextTransitionIndex}).`);
445
+ }
446
+ if (!Number.isSafeInteger(checkpoint.nextTransitionIndex) || checkpoint.nextTransitionIndex < 0) {
447
+ throw new RangeError('nextTransitionIndex must be a non-negative safe integer');
448
+ }
449
+ nextTransitionIndex = checkpoint.nextTransitionIndex;
450
+ lastTransitionIndex = nextTransitionIndex === 0 ? undefined : nextTransitionIndex - 1;
451
+ }
452
+ const persistedSnapshot = checkpoint ? checkpoint.snapshot : checkpointOrSnapshot;
361
453
  const {
362
454
  snapshot,
363
455
  scope
364
- } = createActor.withSystemInspector(inspect, () => createActor.withExecutionIdentity(executionIdentity, () => {
456
+ } = inExecution(() => createActor.withExecutionIdentity(executionIdentity, () => {
365
457
  const scope = createActor.createInertActorScope(logic);
366
458
  const snapshot = dist_xstatePersist.restoreLogicSnapshot(logic, persistedSnapshot, scope);
367
459
  createActor.setInertActorScopeSnapshot(scope, snapshot, false);
@@ -370,6 +462,24 @@ function createDurable(logic, adapter, options) {
370
462
  scope
371
463
  };
372
464
  }));
465
+ for (const {
466
+ event,
467
+ source
468
+ } of checkpoint?.inbox ?? []) {
469
+ const child = lifecycleChild(snapshot, event);
470
+ if (child === null) {
471
+ continue;
472
+ }
473
+ pendingRootEvents.push({
474
+ // A restored child has a new session; its pending lifecycle events
475
+ // follow it.
476
+ event: child && child.sessionId !== undefined && 'sessionId' in event ? {
477
+ ...event,
478
+ sessionId: child.sessionId
479
+ } : event,
480
+ source: source && execution.getActorRef(snapshot, source['@actor'])
481
+ });
482
+ }
373
483
  const effects = [];
374
484
  if (snapshot.status === 'active') {
375
485
  const {
@@ -399,11 +509,11 @@ function createDurable(logic, adapter, options) {
399
509
  if (restored) {
400
510
  throw new Error('initialTransition() cannot initialize a restored execution');
401
511
  }
402
- const [snapshot, effects] = createActor.withSystemInspector(inspect, () => createActor.withExecutionIdentity(executionIdentity, () => effectDescriptor.initialTransition(logic, ...args)));
512
+ const [snapshot, effects] = inExecution(() => createActor.withExecutionIdentity(executionIdentity, () => effectDescriptor.initialTransition(logic, ...args)));
403
513
  return [installSystemRuntime(snapshot), tagEffects(effects)];
404
514
  },
405
515
  transition(snapshot, event) {
406
- const [nextSnapshot, effects] = createActor.withSystemInspector(inspect, () => createActor.withExecutionIdentity(executionIdentity, () => effectDescriptor.transition(logic, snapshot, event)));
516
+ const [nextSnapshot, effects] = inExecution(() => createActor.withExecutionIdentity(executionIdentity, () => effectDescriptor.transition(logic, snapshot, event)));
407
517
  return [installSystemRuntime(nextSnapshot), tagEffects(effects)];
408
518
  },
409
519
  getActorRef(snapshot, address) {
@@ -3,9 +3,9 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var dist_xstatePersist = require('./xstate-persist.development.cjs.js');
6
- var createActor = require('./createActor-a49ae0ce.development.cjs.js');
7
- var effectDescriptor = require('./effectDescriptor-f73da4ec.development.cjs.js');
8
- require('./machineSchemas-6d96bef4.development.cjs.js');
6
+ var createActor = require('./createActor-c029d29f.development.cjs.js');
7
+ var effectDescriptor = require('./effectDescriptor-3fff5c51.development.cjs.js');
8
+ require('./legacySnapshot-ebf0b2b8.development.cjs.js');
9
9
  require('./validation-f2714a35.development.cjs.js');
10
10
 
11
11
  /** @experimental */
@@ -20,6 +20,36 @@ require('./validation-f2714a35.development.cjs.js');
20
20
  * @experimental
21
21
  */
22
22
 
23
+ /**
24
+ * A root-addressed event that the execution received but has not processed,
25
+ * stored in a checkpoint. `source` is the sender's logical address.
26
+ *
27
+ * @experimental
28
+ */
29
+
30
+ /**
31
+ * Everything needed to resume an execution: the persisted snapshot, the next
32
+ * transition index and the events received but not yet processed. Persist
33
+ * the whole object atomically and pass it to `restore()`.
34
+ *
35
+ * @experimental
36
+ */
37
+
38
+ // Lifecycle events that address a child by `actorId`. Snapshot updates are
39
+ // not stored: a restored child reports its current snapshot when it starts.
40
+ const storedLifecycleEvents = new Set(['xstate.done.actor', 'xstate.error.actor', 'xstate.timeout.actor']);
41
+ function isCheckpoint(value) {
42
+ return !!value && typeof value === 'object' && 'inbox' in value && 'nextTransitionIndex' in value && 'snapshot' in value;
43
+ }
44
+ function isInboxEntry(item) {
45
+ if (!item || typeof item !== 'object') return false;
46
+ const {
47
+ event,
48
+ source
49
+ } = item;
50
+ return !!event && typeof event.type === 'string' && (source === undefined || !!source && typeof source['@actor'] === 'string' && (source.incarnation === undefined || typeof source.incarnation === 'string'));
51
+ }
52
+
23
53
  /** @experimental */
24
54
 
25
55
  /**
@@ -149,6 +179,15 @@ function createDurable(logic, adapter, options) {
149
179
  // loop to take them through `waitForEvent()`.
150
180
  const pendingRootEvents = [];
151
181
 
182
+ // For a stored lifecycle event: the child it addresses in `snapshot`, or
183
+ // `null` when that child is gone. `undefined` for any other event.
184
+ const lifecycleChild = (snapshot, event) => {
185
+ if (!createActor.isMachineSnapshot(snapshot) || !storedLifecycleEvents.has(event.type) || typeof event.actorId !== 'string') {
186
+ return undefined;
187
+ }
188
+ return snapshot.children[event.actorId] ?? null;
189
+ };
190
+
152
191
  // Every inter-actor edge is an async handoff to the runtime, and every
153
192
  // handoff queues behind the last one: hosts whose step or activity model
154
193
  // forbids concurrent entries depend on that, and so does deterministic
@@ -289,6 +328,16 @@ function createDurable(logic, adapter, options) {
289
328
  }
290
329
  const inspect = options?.inspect;
291
330
 
331
+ // Runs a transition as this execution: views it derives belong to this
332
+ // execution, and its systems report to the execution's inspector.
333
+ const owner = {
334
+ runtime: wrappedSystemRuntime,
335
+ inspect
336
+ };
337
+ function inExecution(fn) {
338
+ return createActor.withExecutionOwner(owner, () => createActor.withSystemInspector(inspect, fn));
339
+ }
340
+
292
341
  // Attaches the execution's inspector to a system that does not have it
293
342
  // yet. Systems constructed inside a transition get it at construction (the
294
343
  // ambient inspector); this covers systems materialized outside that window
@@ -319,10 +368,12 @@ function createDurable(logic, adapter, options) {
319
368
  const visit = actor => {
320
369
  if (!systems.has(actor.system)) {
321
370
  systems.add(actor.system);
322
- if (wrappedSystemRuntime) {
323
- actor.system.runtime = wrappedSystemRuntime;
371
+ if (createActor.adoptSystem(actor.system, owner)) {
372
+ if (wrappedSystemRuntime) {
373
+ actor.system.runtime = wrappedSystemRuntime;
374
+ }
375
+ wireInspection(actor.system);
324
376
  }
325
- wireInspection(actor.system);
326
377
  }
327
378
  if (recursive) {
328
379
  const children = actor.getSnapshot().children;
@@ -354,14 +405,55 @@ function createDurable(logic, adapter, options) {
354
405
  get nextTransitionIndex() {
355
406
  return nextTransitionIndex;
356
407
  },
357
- restore(persistedSnapshot) {
408
+ checkpoint(snapshot, persistOptions) {
409
+ const inbox = [];
410
+ for (const {
411
+ event,
412
+ source
413
+ } of pendingRootEvents) {
414
+ const child = lifecycleChild(snapshot, event);
415
+ if (event.type === 'xstate.snapshot.actor' || child !== undefined && (!child || event.sessionId !== undefined && child.sessionId !== undefined && child.sessionId !== event.sessionId)) {
416
+ continue;
417
+ }
418
+ inbox.push({
419
+ event,
420
+ source: source && {
421
+ '@actor': source.address,
422
+ ...(createActor.isRemoteActorRef(source) && source.sessionId !== undefined ? {
423
+ incarnation: source.sessionId
424
+ } : {})
425
+ }
426
+ });
427
+ }
428
+ return {
429
+ snapshot: logic.getPersistedSnapshot?.(snapshot, persistOptions) ?? snapshot,
430
+ nextTransitionIndex,
431
+ inbox
432
+ };
433
+ },
434
+ restore(checkpointOrSnapshot) {
435
+ const checkpoint = isCheckpoint(checkpointOrSnapshot) ? checkpointOrSnapshot : undefined;
358
436
  if (restored || nextTransitionIndex !== startingTransitionIndex || currentBatch) {
359
437
  throw new Error('restore() requires a fresh durable execution');
360
438
  }
439
+ if (checkpoint) {
440
+ if (!Array.isArray(checkpoint.inbox) || !checkpoint.inbox.every(isInboxEntry)) {
441
+ throw new TypeError('The checkpoint inbox is invalid.');
442
+ }
443
+ if (adapter.transitionIndex !== undefined && adapter.transitionIndex !== checkpoint.nextTransitionIndex) {
444
+ throw new RangeError(`The adapter's transitionIndex (${adapter.transitionIndex}) does not match the checkpoint's nextTransitionIndex (${checkpoint.nextTransitionIndex}).`);
445
+ }
446
+ if (!Number.isSafeInteger(checkpoint.nextTransitionIndex) || checkpoint.nextTransitionIndex < 0) {
447
+ throw new RangeError('nextTransitionIndex must be a non-negative safe integer');
448
+ }
449
+ nextTransitionIndex = checkpoint.nextTransitionIndex;
450
+ lastTransitionIndex = nextTransitionIndex === 0 ? undefined : nextTransitionIndex - 1;
451
+ }
452
+ const persistedSnapshot = checkpoint ? checkpoint.snapshot : checkpointOrSnapshot;
361
453
  const {
362
454
  snapshot,
363
455
  scope
364
- } = createActor.withSystemInspector(inspect, () => createActor.withExecutionIdentity(executionIdentity, () => {
456
+ } = inExecution(() => createActor.withExecutionIdentity(executionIdentity, () => {
365
457
  const scope = createActor.createInertActorScope(logic);
366
458
  const snapshot = dist_xstatePersist.restoreLogicSnapshot(logic, persistedSnapshot, scope);
367
459
  createActor.setInertActorScopeSnapshot(scope, snapshot, false);
@@ -370,6 +462,24 @@ function createDurable(logic, adapter, options) {
370
462
  scope
371
463
  };
372
464
  }));
465
+ for (const {
466
+ event,
467
+ source
468
+ } of checkpoint?.inbox ?? []) {
469
+ const child = lifecycleChild(snapshot, event);
470
+ if (child === null) {
471
+ continue;
472
+ }
473
+ pendingRootEvents.push({
474
+ // A restored child has a new session; its pending lifecycle events
475
+ // follow it.
476
+ event: child && child.sessionId !== undefined && 'sessionId' in event ? {
477
+ ...event,
478
+ sessionId: child.sessionId
479
+ } : event,
480
+ source: source && execution.getActorRef(snapshot, source['@actor'])
481
+ });
482
+ }
373
483
  const effects = [];
374
484
  if (snapshot.status === 'active') {
375
485
  const {
@@ -399,11 +509,11 @@ function createDurable(logic, adapter, options) {
399
509
  if (restored) {
400
510
  throw new Error('initialTransition() cannot initialize a restored execution');
401
511
  }
402
- const [snapshot, effects] = createActor.withSystemInspector(inspect, () => createActor.withExecutionIdentity(executionIdentity, () => effectDescriptor.initialTransition(logic, ...args)));
512
+ const [snapshot, effects] = inExecution(() => createActor.withExecutionIdentity(executionIdentity, () => effectDescriptor.initialTransition(logic, ...args)));
403
513
  return [installSystemRuntime(snapshot), tagEffects(effects)];
404
514
  },
405
515
  transition(snapshot, event) {
406
- const [nextSnapshot, effects] = createActor.withSystemInspector(inspect, () => createActor.withExecutionIdentity(executionIdentity, () => effectDescriptor.transition(logic, snapshot, event)));
516
+ const [nextSnapshot, effects] = inExecution(() => createActor.withExecutionIdentity(executionIdentity, () => effectDescriptor.transition(logic, snapshot, event)));
407
517
  return [installSystemRuntime(nextSnapshot), tagEffects(effects)];
408
518
  },
409
519
  getActorRef(snapshot, address) {
@@ -1,7 +1,7 @@
1
1
  import { restoreLogicSnapshot } from './xstate-persist.development.esm.js';
2
- import { j as encodeAddressSegment, aL as getRootActorId, aM as RUNTIME_OPERATIONS, aN as withSystemInspector, aI as isRemoteActorRef, aB as createStartEffect, aO as createTimerEffect, aP as getSnapshotActorRef, aQ as withExecutionIdentity, ah as createInertActorScope, ai as setInertActorScopeSnapshot, ak as attachSnapshotActorRef, y as matchesTimerKey, aR as getTimerStart, aS as recordTimerStart, K as deliverEvent } from './createActor-551369be.development.esm.js';
3
- import { a as initialTransition, t as transition, d as describeExecutableEffect } from './effectDescriptor-321a8ce9.development.esm.js';
4
- import './machineSchemas-b5736804.development.esm.js';
2
+ import { j as encodeAddressSegment, aL as getRootActorId, aM as RUNTIME_OPERATIONS, aI as isRemoteActorRef, aB as createStartEffect, aN as createTimerEffect, aO as getSnapshotActorRef, z as isMachineSnapshot, aP as withExecutionOwner, y as matchesTimerKey, aQ as getTimerStart, aR as recordTimerStart, K as deliverEvent, aS as withSystemInspector, aT as adoptSystem, aU as withExecutionIdentity, ah as createInertActorScope, ai as setInertActorScopeSnapshot, ak as attachSnapshotActorRef } from './createActor-0250284a.development.esm.js';
3
+ import { d as describeExecutableEffect, a as initialTransition, t as transition } from './effectDescriptor-5ac7dccb.development.esm.js';
4
+ import './legacySnapshot-ae6594a7.development.esm.js';
5
5
  import './validation-e69470ad.development.esm.js';
6
6
 
7
7
  /** @experimental */
@@ -16,6 +16,36 @@ import './validation-e69470ad.development.esm.js';
16
16
  * @experimental
17
17
  */
18
18
 
19
+ /**
20
+ * A root-addressed event that the execution received but has not processed,
21
+ * stored in a checkpoint. `source` is the sender's logical address.
22
+ *
23
+ * @experimental
24
+ */
25
+
26
+ /**
27
+ * Everything needed to resume an execution: the persisted snapshot, the next
28
+ * transition index and the events received but not yet processed. Persist
29
+ * the whole object atomically and pass it to `restore()`.
30
+ *
31
+ * @experimental
32
+ */
33
+
34
+ // Lifecycle events that address a child by `actorId`. Snapshot updates are
35
+ // not stored: a restored child reports its current snapshot when it starts.
36
+ const storedLifecycleEvents = new Set(['xstate.done.actor', 'xstate.error.actor', 'xstate.timeout.actor']);
37
+ function isCheckpoint(value) {
38
+ return !!value && typeof value === 'object' && 'inbox' in value && 'nextTransitionIndex' in value && 'snapshot' in value;
39
+ }
40
+ function isInboxEntry(item) {
41
+ if (!item || typeof item !== 'object') return false;
42
+ const {
43
+ event,
44
+ source
45
+ } = item;
46
+ return !!event && typeof event.type === 'string' && (source === undefined || !!source && typeof source['@actor'] === 'string' && (source.incarnation === undefined || typeof source.incarnation === 'string'));
47
+ }
48
+
19
49
  /** @experimental */
20
50
 
21
51
  /**
@@ -145,6 +175,15 @@ function createDurable(logic, adapter, options) {
145
175
  // loop to take them through `waitForEvent()`.
146
176
  const pendingRootEvents = [];
147
177
 
178
+ // For a stored lifecycle event: the child it addresses in `snapshot`, or
179
+ // `null` when that child is gone. `undefined` for any other event.
180
+ const lifecycleChild = (snapshot, event) => {
181
+ if (!isMachineSnapshot(snapshot) || !storedLifecycleEvents.has(event.type) || typeof event.actorId !== 'string') {
182
+ return undefined;
183
+ }
184
+ return snapshot.children[event.actorId] ?? null;
185
+ };
186
+
148
187
  // Every inter-actor edge is an async handoff to the runtime, and every
149
188
  // handoff queues behind the last one: hosts whose step or activity model
150
189
  // forbids concurrent entries depend on that, and so does deterministic
@@ -285,6 +324,16 @@ function createDurable(logic, adapter, options) {
285
324
  }
286
325
  const inspect = options?.inspect;
287
326
 
327
+ // Runs a transition as this execution: views it derives belong to this
328
+ // execution, and its systems report to the execution's inspector.
329
+ const owner = {
330
+ runtime: wrappedSystemRuntime,
331
+ inspect
332
+ };
333
+ function inExecution(fn) {
334
+ return withExecutionOwner(owner, () => withSystemInspector(inspect, fn));
335
+ }
336
+
288
337
  // Attaches the execution's inspector to a system that does not have it
289
338
  // yet. Systems constructed inside a transition get it at construction (the
290
339
  // ambient inspector); this covers systems materialized outside that window
@@ -315,10 +364,12 @@ function createDurable(logic, adapter, options) {
315
364
  const visit = actor => {
316
365
  if (!systems.has(actor.system)) {
317
366
  systems.add(actor.system);
318
- if (wrappedSystemRuntime) {
319
- actor.system.runtime = wrappedSystemRuntime;
367
+ if (adoptSystem(actor.system, owner)) {
368
+ if (wrappedSystemRuntime) {
369
+ actor.system.runtime = wrappedSystemRuntime;
370
+ }
371
+ wireInspection(actor.system);
320
372
  }
321
- wireInspection(actor.system);
322
373
  }
323
374
  if (recursive) {
324
375
  const children = actor.getSnapshot().children;
@@ -350,14 +401,55 @@ function createDurable(logic, adapter, options) {
350
401
  get nextTransitionIndex() {
351
402
  return nextTransitionIndex;
352
403
  },
353
- restore(persistedSnapshot) {
404
+ checkpoint(snapshot, persistOptions) {
405
+ const inbox = [];
406
+ for (const {
407
+ event,
408
+ source
409
+ } of pendingRootEvents) {
410
+ const child = lifecycleChild(snapshot, event);
411
+ if (event.type === 'xstate.snapshot.actor' || child !== undefined && (!child || event.sessionId !== undefined && child.sessionId !== undefined && child.sessionId !== event.sessionId)) {
412
+ continue;
413
+ }
414
+ inbox.push({
415
+ event,
416
+ source: source && {
417
+ '@actor': source.address,
418
+ ...(isRemoteActorRef(source) && source.sessionId !== undefined ? {
419
+ incarnation: source.sessionId
420
+ } : {})
421
+ }
422
+ });
423
+ }
424
+ return {
425
+ snapshot: logic.getPersistedSnapshot?.(snapshot, persistOptions) ?? snapshot,
426
+ nextTransitionIndex,
427
+ inbox
428
+ };
429
+ },
430
+ restore(checkpointOrSnapshot) {
431
+ const checkpoint = isCheckpoint(checkpointOrSnapshot) ? checkpointOrSnapshot : undefined;
354
432
  if (restored || nextTransitionIndex !== startingTransitionIndex || currentBatch) {
355
433
  throw new Error('restore() requires a fresh durable execution');
356
434
  }
435
+ if (checkpoint) {
436
+ if (!Array.isArray(checkpoint.inbox) || !checkpoint.inbox.every(isInboxEntry)) {
437
+ throw new TypeError('The checkpoint inbox is invalid.');
438
+ }
439
+ if (adapter.transitionIndex !== undefined && adapter.transitionIndex !== checkpoint.nextTransitionIndex) {
440
+ throw new RangeError(`The adapter's transitionIndex (${adapter.transitionIndex}) does not match the checkpoint's nextTransitionIndex (${checkpoint.nextTransitionIndex}).`);
441
+ }
442
+ if (!Number.isSafeInteger(checkpoint.nextTransitionIndex) || checkpoint.nextTransitionIndex < 0) {
443
+ throw new RangeError('nextTransitionIndex must be a non-negative safe integer');
444
+ }
445
+ nextTransitionIndex = checkpoint.nextTransitionIndex;
446
+ lastTransitionIndex = nextTransitionIndex === 0 ? undefined : nextTransitionIndex - 1;
447
+ }
448
+ const persistedSnapshot = checkpoint ? checkpoint.snapshot : checkpointOrSnapshot;
357
449
  const {
358
450
  snapshot,
359
451
  scope
360
- } = withSystemInspector(inspect, () => withExecutionIdentity(executionIdentity, () => {
452
+ } = inExecution(() => withExecutionIdentity(executionIdentity, () => {
361
453
  const scope = createInertActorScope(logic);
362
454
  const snapshot = restoreLogicSnapshot(logic, persistedSnapshot, scope);
363
455
  setInertActorScopeSnapshot(scope, snapshot, false);
@@ -366,6 +458,24 @@ function createDurable(logic, adapter, options) {
366
458
  scope
367
459
  };
368
460
  }));
461
+ for (const {
462
+ event,
463
+ source
464
+ } of checkpoint?.inbox ?? []) {
465
+ const child = lifecycleChild(snapshot, event);
466
+ if (child === null) {
467
+ continue;
468
+ }
469
+ pendingRootEvents.push({
470
+ // A restored child has a new session; its pending lifecycle events
471
+ // follow it.
472
+ event: child && child.sessionId !== undefined && 'sessionId' in event ? {
473
+ ...event,
474
+ sessionId: child.sessionId
475
+ } : event,
476
+ source: source && execution.getActorRef(snapshot, source['@actor'])
477
+ });
478
+ }
369
479
  const effects = [];
370
480
  if (snapshot.status === 'active') {
371
481
  const {
@@ -395,11 +505,11 @@ function createDurable(logic, adapter, options) {
395
505
  if (restored) {
396
506
  throw new Error('initialTransition() cannot initialize a restored execution');
397
507
  }
398
- const [snapshot, effects] = withSystemInspector(inspect, () => withExecutionIdentity(executionIdentity, () => initialTransition(logic, ...args)));
508
+ const [snapshot, effects] = inExecution(() => withExecutionIdentity(executionIdentity, () => initialTransition(logic, ...args)));
399
509
  return [installSystemRuntime(snapshot), tagEffects(effects)];
400
510
  },
401
511
  transition(snapshot, event) {
402
- const [nextSnapshot, effects] = withSystemInspector(inspect, () => withExecutionIdentity(executionIdentity, () => transition(logic, snapshot, event)));
512
+ const [nextSnapshot, effects] = inExecution(() => withExecutionIdentity(executionIdentity, () => transition(logic, snapshot, event)));
403
513
  return [installSystemRuntime(nextSnapshot), tagEffects(effects)];
404
514
  },
405
515
  getActorRef(snapshot, address) {