xstate 6.0.0-alpha.35 → 6.0.0-alpha.36

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.
@@ -7,7 +7,7 @@ export { createMachineFromConfig } from "./createMachineFromConfig.js";
7
7
  export type { ActionJSON, GuardJSON, InvokeJSON, MachineJSON, StateNodeJSON, TransitionJSON } from "./createMachineFromConfig.js";
8
8
  export { machineConfigToJSON, serializeMachine, type CodeExpression } from "./serialize.js";
9
9
  export { mapState } from "./mapState.js";
10
- export { machineVersions, type MigrateSnapshotOptions, type MachineVersionsOptions, type ParsedPersistedSnapshot, type PersistedMachineIdentity, type PersistedSnapshotDataFrom, type PersistedSnapshotSource, type SnapshotMigrationHandlers, type PersistedSnapshotFrom } from "./machineVersions.js";
10
+ export { machineVersions, type AdaptEventsOptions, type EventAdapterHandlers, type EventHistorySource, type MigrateSnapshotOptions, type MachineVersionsOptions, type ParsedPersistedSnapshot, type PersistedMachineIdentity, type PersistedSnapshotDataFrom, type PersistedSnapshotSource, type SnapshotMigrationHandlers, type PersistedSnapshotFrom } from "./machineVersions.js";
11
11
  export { types, isTypeSchema, type StandardSchemaV1, type TypeSchema } from "./schema.types.js";
12
12
  export type { ActorValidationBoundary, ActorValidationEventOrigin, ActorValidationRequest, ActorLogicValidator } from "./validation.types.js";
13
13
  export { createSystem, setup } from "./setup.js";
@@ -1,5 +1,5 @@
1
1
  import type { AnyMachineSchemas } from "./types.v6.js";
2
- import type { AnyStateMachine, ContextFrom, PersistedSnapshotFor, Snapshot } from "./types.js";
2
+ import type { AnyStateMachine, ContextFrom, EventFrom, PersistedSnapshotFor, Snapshot } from "./types.js";
3
3
  export type PersistedMachineIdentity<TMachine extends AnyStateMachine = AnyStateMachine> = {
4
4
  id: TMachine['id'];
5
5
  version: NonNullable<TMachine['version']>;
@@ -44,12 +44,27 @@ export type MigrateSnapshotOptions<TMachines extends readonly VersionedStateMach
44
44
  to: TTargetVersion;
45
45
  migrations: SnapshotMigrationHandlers<TMachines, MachineForVersion<TMachines, TTargetVersion>>;
46
46
  };
47
- /** Creates parsers backed by retained versions of one machine. */
47
+ export type EventHistorySource = {
48
+ id?: string;
49
+ version?: string;
50
+ };
51
+ export type EventAdapterHandlers<TMachines extends readonly VersionedStateMachine[], TTarget extends VersionedStateMachine> = {
52
+ [TVersion in Exclude<MachineVersion<TMachines>, TTarget['version']>]?: (events: readonly EventFrom<MachineForVersion<TMachines, TVersion>>[]) => MaybePromise<readonly EventFrom<TTarget>[]>;
53
+ } & {
54
+ '*'?: (events: readonly unknown[], source: EventHistorySource) => MaybePromise<readonly EventFrom<TTarget>[]>;
55
+ };
56
+ export type AdaptEventsOptions<TMachines extends readonly VersionedStateMachine[], TTargetVersion extends MachineVersion<TMachines>> = {
57
+ from: EventHistorySource;
58
+ to: TTargetVersion;
59
+ adapters: EventAdapterHandlers<TMachines, MachineForVersion<TMachines, TTargetVersion>>;
60
+ };
61
+ /** Creates migration and adaptation utilities backed by retained versions of one machine. */
48
62
  export declare function machineVersions<const TMachines extends readonly [
49
63
  VersionedStateMachine,
50
64
  ...VersionedStateMachine[]
51
65
  ]>(machines: TMachines, options?: MachineVersionsOptions<TMachines>): {
52
66
  parseSnapshot: (raw: unknown) => Promise<ParsedPersistedSnapshot<TMachines>>;
67
+ adaptEvents<TTargetVersion extends MachineVersion<TMachines>>(events: readonly unknown[], adaptationOptions: AdaptEventsOptions<TMachines, TTargetVersion>): Promise<EventFrom<MachineForVersion<TMachines, TTargetVersion>>[]>;
53
68
  migrateSnapshot<TTargetVersion extends MachineVersion<TMachines>>(raw: unknown, migrationOptions: MigrateSnapshotOptions<TMachines, TTargetVersion>): Promise<PersistedSnapshotFrom<MachineForVersion<TMachines, TTargetVersion>>>;
54
69
  };
55
70
  export {};
@@ -2004,8 +2004,36 @@ async function finalizeSnapshot(snapshot, target) {
2004
2004
  version: target.version
2005
2005
  };
2006
2006
  }
2007
+ async function validateEvents(events, machine) {
2008
+ return Promise.all(events.map(async (event, index) => {
2009
+ if (!isObject(event) || typeof event.type !== 'string') {
2010
+ throw new Error(`Invalid event at index ${index}.`);
2011
+ }
2012
+ const eventSchemas = machine.schemas?.events;
2013
+ const isFrameworkEvent = event.type.startsWith('xstate.') || event.type.startsWith('@xstate.');
2014
+ const schema = eventSchemas && Object.hasOwn(eventSchemas, event.type) ? eventSchemas[event.type] : undefined;
2015
+ if (eventSchemas && !schema && !isFrameworkEvent) {
2016
+ throw new Error(`Unknown event '${event.type}' for machine '${machine.id}' version '${machine.version}'.`);
2017
+ }
2018
+ if (!schema) {
2019
+ return event;
2020
+ }
2021
+ const {
2022
+ type,
2023
+ ...payload
2024
+ } = event;
2025
+ const validatedPayload = await validate(schema, payload, `event '${type}' at index ${index}`);
2026
+ if (!isObject(validatedPayload)) {
2027
+ throw new Error(`Invalid event '${type}' at index ${index}.`);
2028
+ }
2029
+ return {
2030
+ ...validatedPayload,
2031
+ type
2032
+ };
2033
+ }));
2034
+ }
2007
2035
 
2008
- /** Creates parsers backed by retained versions of one machine. */
2036
+ /** Creates migration and adaptation utilities backed by retained versions of one machine. */
2009
2037
  function machineVersions(machines, options) {
2010
2038
  const byIdentity = new Map();
2011
2039
  const machineId = machines[0].id;
@@ -2076,6 +2104,47 @@ function machineVersions(machines, options) {
2076
2104
  };
2077
2105
  return {
2078
2106
  parseSnapshot,
2107
+ async adaptEvents(events, adaptationOptions) {
2108
+ const target = byIdentity.get(`${machineId}\0${adaptationOptions.to}`);
2109
+ if (!target) {
2110
+ throw new Error(`Target version '${adaptationOptions.to}' is not retained for machine '${machineId}'.`);
2111
+ }
2112
+ const sourceId = adaptationOptions.from.id ?? machineId;
2113
+ const source = byIdentity.get(`${sourceId}\0${adaptationOptions.from.version}`);
2114
+ let sourceError;
2115
+ let sourceValidationFailed = false;
2116
+ if (source) {
2117
+ let sourceEvents;
2118
+ try {
2119
+ sourceEvents = await validateEvents(events, source);
2120
+ } catch (error) {
2121
+ sourceError = error;
2122
+ sourceValidationFailed = true;
2123
+ }
2124
+ if (sourceEvents) {
2125
+ if (source === target) {
2126
+ return sourceEvents;
2127
+ }
2128
+ const adapter = adaptationOptions.adapters[source.version];
2129
+ if (adapter) {
2130
+ return validateEvents(await adapter(sourceEvents), target);
2131
+ }
2132
+ }
2133
+ }
2134
+ const wildcardAdapter = adaptationOptions.adapters['*'];
2135
+ if (wildcardAdapter) {
2136
+ return validateEvents(await wildcardAdapter(events, adaptationOptions.from), target);
2137
+ }
2138
+ if (source) {
2139
+ if (sourceValidationFailed) {
2140
+ throw sourceError instanceof Error ? sourceError : new Error(`Unable to validate event history for machine '${source.id}' version '${source.version}'.`, {
2141
+ cause: sourceError
2142
+ });
2143
+ }
2144
+ throw new Error(`No event adapter from version '${source.version}' to '${target.version}' for machine '${target.id}'.`);
2145
+ }
2146
+ throw new Error(`Unknown event history source '${sourceId}' version '${adaptationOptions.from.version}'.`);
2147
+ },
2079
2148
  async migrateSnapshot(raw, migrationOptions) {
2080
2149
  const target = byIdentity.get(`${machineId}\0${migrationOptions.to}`);
2081
2150
  if (!target) {
@@ -2004,8 +2004,36 @@ async function finalizeSnapshot(snapshot, target) {
2004
2004
  version: target.version
2005
2005
  };
2006
2006
  }
2007
+ async function validateEvents(events, machine) {
2008
+ return Promise.all(events.map(async (event, index) => {
2009
+ if (!isObject(event) || typeof event.type !== 'string') {
2010
+ throw new Error(`Invalid event at index ${index}.`);
2011
+ }
2012
+ const eventSchemas = machine.schemas?.events;
2013
+ const isFrameworkEvent = event.type.startsWith('xstate.') || event.type.startsWith('@xstate.');
2014
+ const schema = eventSchemas && Object.hasOwn(eventSchemas, event.type) ? eventSchemas[event.type] : undefined;
2015
+ if (eventSchemas && !schema && !isFrameworkEvent) {
2016
+ throw new Error(`Unknown event '${event.type}' for machine '${machine.id}' version '${machine.version}'.`);
2017
+ }
2018
+ if (!schema) {
2019
+ return event;
2020
+ }
2021
+ const {
2022
+ type,
2023
+ ...payload
2024
+ } = event;
2025
+ const validatedPayload = await validate(schema, payload, `event '${type}' at index ${index}`);
2026
+ if (!isObject(validatedPayload)) {
2027
+ throw new Error(`Invalid event '${type}' at index ${index}.`);
2028
+ }
2029
+ return {
2030
+ ...validatedPayload,
2031
+ type
2032
+ };
2033
+ }));
2034
+ }
2007
2035
 
2008
- /** Creates parsers backed by retained versions of one machine. */
2036
+ /** Creates migration and adaptation utilities backed by retained versions of one machine. */
2009
2037
  function machineVersions(machines, options) {
2010
2038
  const byIdentity = new Map();
2011
2039
  const machineId = machines[0].id;
@@ -2076,6 +2104,47 @@ function machineVersions(machines, options) {
2076
2104
  };
2077
2105
  return {
2078
2106
  parseSnapshot,
2107
+ async adaptEvents(events, adaptationOptions) {
2108
+ const target = byIdentity.get(`${machineId}\0${adaptationOptions.to}`);
2109
+ if (!target) {
2110
+ throw new Error(`Target version '${adaptationOptions.to}' is not retained for machine '${machineId}'.`);
2111
+ }
2112
+ const sourceId = adaptationOptions.from.id ?? machineId;
2113
+ const source = byIdentity.get(`${sourceId}\0${adaptationOptions.from.version}`);
2114
+ let sourceError;
2115
+ let sourceValidationFailed = false;
2116
+ if (source) {
2117
+ let sourceEvents;
2118
+ try {
2119
+ sourceEvents = await validateEvents(events, source);
2120
+ } catch (error) {
2121
+ sourceError = error;
2122
+ sourceValidationFailed = true;
2123
+ }
2124
+ if (sourceEvents) {
2125
+ if (source === target) {
2126
+ return sourceEvents;
2127
+ }
2128
+ const adapter = adaptationOptions.adapters[source.version];
2129
+ if (adapter) {
2130
+ return validateEvents(await adapter(sourceEvents), target);
2131
+ }
2132
+ }
2133
+ }
2134
+ const wildcardAdapter = adaptationOptions.adapters['*'];
2135
+ if (wildcardAdapter) {
2136
+ return validateEvents(await wildcardAdapter(events, adaptationOptions.from), target);
2137
+ }
2138
+ if (source) {
2139
+ if (sourceValidationFailed) {
2140
+ throw sourceError instanceof Error ? sourceError : new Error(`Unable to validate event history for machine '${source.id}' version '${source.version}'.`, {
2141
+ cause: sourceError
2142
+ });
2143
+ }
2144
+ throw new Error(`No event adapter from version '${source.version}' to '${target.version}' for machine '${target.id}'.`);
2145
+ }
2146
+ throw new Error(`Unknown event history source '${sourceId}' version '${adaptationOptions.from.version}'.`);
2147
+ },
2079
2148
  async migrateSnapshot(raw, migrationOptions) {
2080
2149
  const target = byIdentity.get(`${machineId}\0${migrationOptions.to}`);
2081
2150
  if (!target) {
@@ -2002,8 +2002,36 @@ async function finalizeSnapshot(snapshot, target) {
2002
2002
  version: target.version
2003
2003
  };
2004
2004
  }
2005
+ async function validateEvents(events, machine) {
2006
+ return Promise.all(events.map(async (event, index) => {
2007
+ if (!isObject(event) || typeof event.type !== 'string') {
2008
+ throw new Error(`Invalid event at index ${index}.`);
2009
+ }
2010
+ const eventSchemas = machine.schemas?.events;
2011
+ const isFrameworkEvent = event.type.startsWith('xstate.') || event.type.startsWith('@xstate.');
2012
+ const schema = eventSchemas && Object.hasOwn(eventSchemas, event.type) ? eventSchemas[event.type] : undefined;
2013
+ if (eventSchemas && !schema && !isFrameworkEvent) {
2014
+ throw new Error(`Unknown event '${event.type}' for machine '${machine.id}' version '${machine.version}'.`);
2015
+ }
2016
+ if (!schema) {
2017
+ return event;
2018
+ }
2019
+ const {
2020
+ type,
2021
+ ...payload
2022
+ } = event;
2023
+ const validatedPayload = await validate(schema, payload, `event '${type}' at index ${index}`);
2024
+ if (!isObject(validatedPayload)) {
2025
+ throw new Error(`Invalid event '${type}' at index ${index}.`);
2026
+ }
2027
+ return {
2028
+ ...validatedPayload,
2029
+ type
2030
+ };
2031
+ }));
2032
+ }
2005
2033
 
2006
- /** Creates parsers backed by retained versions of one machine. */
2034
+ /** Creates migration and adaptation utilities backed by retained versions of one machine. */
2007
2035
  function machineVersions(machines, options) {
2008
2036
  const byIdentity = new Map();
2009
2037
  const machineId = machines[0].id;
@@ -2074,6 +2102,47 @@ function machineVersions(machines, options) {
2074
2102
  };
2075
2103
  return {
2076
2104
  parseSnapshot,
2105
+ async adaptEvents(events, adaptationOptions) {
2106
+ const target = byIdentity.get(`${machineId}\0${adaptationOptions.to}`);
2107
+ if (!target) {
2108
+ throw new Error(`Target version '${adaptationOptions.to}' is not retained for machine '${machineId}'.`);
2109
+ }
2110
+ const sourceId = adaptationOptions.from.id ?? machineId;
2111
+ const source = byIdentity.get(`${sourceId}\0${adaptationOptions.from.version}`);
2112
+ let sourceError;
2113
+ let sourceValidationFailed = false;
2114
+ if (source) {
2115
+ let sourceEvents;
2116
+ try {
2117
+ sourceEvents = await validateEvents(events, source);
2118
+ } catch (error) {
2119
+ sourceError = error;
2120
+ sourceValidationFailed = true;
2121
+ }
2122
+ if (sourceEvents) {
2123
+ if (source === target) {
2124
+ return sourceEvents;
2125
+ }
2126
+ const adapter = adaptationOptions.adapters[source.version];
2127
+ if (adapter) {
2128
+ return validateEvents(await adapter(sourceEvents), target);
2129
+ }
2130
+ }
2131
+ }
2132
+ const wildcardAdapter = adaptationOptions.adapters['*'];
2133
+ if (wildcardAdapter) {
2134
+ return validateEvents(await wildcardAdapter(events, adaptationOptions.from), target);
2135
+ }
2136
+ if (source) {
2137
+ if (sourceValidationFailed) {
2138
+ throw sourceError instanceof Error ? sourceError : new Error(`Unable to validate event history for machine '${source.id}' version '${source.version}'.`, {
2139
+ cause: sourceError
2140
+ });
2141
+ }
2142
+ throw new Error(`No event adapter from version '${source.version}' to '${target.version}' for machine '${target.id}'.`);
2143
+ }
2144
+ throw new Error(`Unknown event history source '${sourceId}' version '${adaptationOptions.from.version}'.`);
2145
+ },
2077
2146
  async migrateSnapshot(raw, migrationOptions) {
2078
2147
  const target = byIdentity.get(`${machineId}\0${migrationOptions.to}`);
2079
2148
  if (!target) {
@@ -2002,8 +2002,36 @@ async function finalizeSnapshot(snapshot, target) {
2002
2002
  version: target.version
2003
2003
  };
2004
2004
  }
2005
+ async function validateEvents(events, machine) {
2006
+ return Promise.all(events.map(async (event, index) => {
2007
+ if (!isObject(event) || typeof event.type !== 'string') {
2008
+ throw new Error(`Invalid event at index ${index}.`);
2009
+ }
2010
+ const eventSchemas = machine.schemas?.events;
2011
+ const isFrameworkEvent = event.type.startsWith('xstate.') || event.type.startsWith('@xstate.');
2012
+ const schema = eventSchemas && Object.hasOwn(eventSchemas, event.type) ? eventSchemas[event.type] : undefined;
2013
+ if (eventSchemas && !schema && !isFrameworkEvent) {
2014
+ throw new Error(`Unknown event '${event.type}' for machine '${machine.id}' version '${machine.version}'.`);
2015
+ }
2016
+ if (!schema) {
2017
+ return event;
2018
+ }
2019
+ const {
2020
+ type,
2021
+ ...payload
2022
+ } = event;
2023
+ const validatedPayload = await validate(schema, payload, `event '${type}' at index ${index}`);
2024
+ if (!isObject(validatedPayload)) {
2025
+ throw new Error(`Invalid event '${type}' at index ${index}.`);
2026
+ }
2027
+ return {
2028
+ ...validatedPayload,
2029
+ type
2030
+ };
2031
+ }));
2032
+ }
2005
2033
 
2006
- /** Creates parsers backed by retained versions of one machine. */
2034
+ /** Creates migration and adaptation utilities backed by retained versions of one machine. */
2007
2035
  function machineVersions(machines, options) {
2008
2036
  const byIdentity = new Map();
2009
2037
  const machineId = machines[0].id;
@@ -2074,6 +2102,47 @@ function machineVersions(machines, options) {
2074
2102
  };
2075
2103
  return {
2076
2104
  parseSnapshot,
2105
+ async adaptEvents(events, adaptationOptions) {
2106
+ const target = byIdentity.get(`${machineId}\0${adaptationOptions.to}`);
2107
+ if (!target) {
2108
+ throw new Error(`Target version '${adaptationOptions.to}' is not retained for machine '${machineId}'.`);
2109
+ }
2110
+ const sourceId = adaptationOptions.from.id ?? machineId;
2111
+ const source = byIdentity.get(`${sourceId}\0${adaptationOptions.from.version}`);
2112
+ let sourceError;
2113
+ let sourceValidationFailed = false;
2114
+ if (source) {
2115
+ let sourceEvents;
2116
+ try {
2117
+ sourceEvents = await validateEvents(events, source);
2118
+ } catch (error) {
2119
+ sourceError = error;
2120
+ sourceValidationFailed = true;
2121
+ }
2122
+ if (sourceEvents) {
2123
+ if (source === target) {
2124
+ return sourceEvents;
2125
+ }
2126
+ const adapter = adaptationOptions.adapters[source.version];
2127
+ if (adapter) {
2128
+ return validateEvents(await adapter(sourceEvents), target);
2129
+ }
2130
+ }
2131
+ }
2132
+ const wildcardAdapter = adaptationOptions.adapters['*'];
2133
+ if (wildcardAdapter) {
2134
+ return validateEvents(await wildcardAdapter(events, adaptationOptions.from), target);
2135
+ }
2136
+ if (source) {
2137
+ if (sourceValidationFailed) {
2138
+ throw sourceError instanceof Error ? sourceError : new Error(`Unable to validate event history for machine '${source.id}' version '${source.version}'.`, {
2139
+ cause: sourceError
2140
+ });
2141
+ }
2142
+ throw new Error(`No event adapter from version '${source.version}' to '${target.version}' for machine '${target.id}'.`);
2143
+ }
2144
+ throw new Error(`Unknown event history source '${sourceId}' version '${adaptationOptions.from.version}'.`);
2145
+ },
2077
2146
  async migrateSnapshot(raw, migrationOptions) {
2078
2147
  const target = byIdentity.get(`${machineId}\0${migrationOptions.to}`);
2079
2148
  if (!target) {