@xstate/graph
Advanced tools
Comparing version 2.0.0-alpha.0 to 2.0.0-alpha.1
import type { Event, EventObject, AnyStateMachine, AnyState, StateFrom, EventFrom } from 'xstate'; | ||
import type { SerializedEvent, SerializedState, SimpleBehavior, StatePath, StatePlan, ValueAdjacencyMap, ValueAdjacencyMapOptions, DirectedGraphNode, TraversalOptions, AnyStateNode } from './types'; | ||
import type { SerializedEvent, SerializedState, SimpleBehavior, StatePath, DirectedGraphNode, TraversalOptions, AnyStateNode, TraversalConfig } from './types'; | ||
export declare function toEventObject<TEvent extends EventObject>(event: Event<TEvent>): TEvent; | ||
@@ -12,26 +12,19 @@ /** | ||
export declare function serializeEvent<TEvent extends EventObject>(event: TEvent): SerializedEvent; | ||
export declare function getValueAdjacencyMap<TMachine extends AnyStateMachine>(machine: TMachine, options?: ValueAdjacencyMapOptions<StateFrom<TMachine>, EventFrom<TMachine>>): ValueAdjacencyMap<StateFrom<TMachine>, EventFrom<TMachine>>; | ||
export declare function getShortestPlans<TMachine extends AnyStateMachine>(machine: TMachine, options?: Partial<TraversalOptions<StateFrom<TMachine>, EventFrom<TMachine>>>): Array<StatePlan<StateFrom<TMachine>, EventFrom<TMachine>>>; | ||
export declare function traverseShortestPlans<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, options?: Partial<TraversalOptions<TState, TEvent>>): Array<StatePlan<TState, TEvent>>; | ||
export declare function getSimplePlans<TMachine extends AnyStateMachine>(machine: TMachine, options?: Partial<TraversalOptions<StateFrom<TMachine>, EventFrom<TMachine>>>): Array<StatePlan<StateFrom<TMachine>, EventFrom<TMachine>>>; | ||
export declare function createDefaultMachineOptions<TMachine extends AnyStateMachine>(machine: TMachine, options?: TraversalOptions<StateFrom<TMachine>, EventFrom<TMachine>>): TraversalOptions<StateFrom<TMachine>, EventFrom<TMachine>>; | ||
export declare function createDefaultBehaviorOptions<TBehavior extends SimpleBehavior<any, any>>(_behavior: TBehavior): TraversalOptions<any, any>; | ||
export declare function toDirectedGraph(stateNode: AnyStateNode | AnyStateMachine): DirectedGraphNode; | ||
export declare function getPathFromEvents<TState, TEvent extends EventObject = EventObject>(behavior: SimpleBehavior<TState, TEvent>, events: TEvent[]): StatePath<TState, TEvent>; | ||
interface AdjacencyMap<TState, TEvent> { | ||
[key: SerializedState]: { | ||
state: TState; | ||
transitions: { | ||
[key: SerializedEvent]: { | ||
event: TEvent; | ||
state: TState; | ||
}; | ||
export interface AdjacencyValue<TState, TEvent> { | ||
state: TState; | ||
transitions: { | ||
[key: SerializedEvent]: { | ||
event: TEvent; | ||
state: TState; | ||
}; | ||
}; | ||
} | ||
export declare function performDepthFirstTraversal<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, options: TraversalOptions<TState, TEvent>): AdjacencyMap<TState, TEvent>; | ||
export declare function traverseSimplePlans<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, options: Partial<TraversalOptions<TState, TEvent>>): Array<StatePlan<TState, TEvent>>; | ||
export declare function traverseSimplePathsTo<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, predicate: (state: TState) => boolean, options: TraversalOptions<TState, TEvent>): Array<StatePlan<TState, TEvent>>; | ||
export declare function traverseSimplePathsFromTo<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, fromPredicate: (state: TState) => boolean, toPredicate: (state: TState) => boolean, options: TraversalOptions<TState, TEvent>): Array<StatePlan<TState, TEvent>>; | ||
export declare function traverseShortestPathsTo<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, predicate: (state: TState) => boolean, options: TraversalOptions<TState, TEvent>): Array<StatePlan<TState, TEvent>>; | ||
export declare function traverseShortestPathsFromTo<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, fromPredicate: (state: TState) => boolean, toPredicate: (state: TState) => boolean, options: TraversalOptions<TState, TEvent>): Array<StatePlan<TState, TEvent>>; | ||
export {}; | ||
export interface AdjacencyMap<TState, TEvent> { | ||
[key: SerializedState]: AdjacencyValue<TState, TEvent>; | ||
} | ||
export declare function resolveTraversalOptions<TState, TEvent extends EventObject>(traversalOptions?: TraversalOptions<TState, TEvent>, defaultOptions?: TraversalOptions<TState, TEvent>): TraversalConfig<TState, TEvent>; | ||
export declare function joinPaths<TState, TEvent extends EventObject>(path1: StatePath<TState, TEvent>, path2: StatePath<TState, TEvent>): StatePath<TState, TEvent>; | ||
//# sourceMappingURL=graph.d.ts.map |
428
es/graph.js
@@ -12,2 +12,13 @@ var __assign = (this && this.__assign) || function () { | ||
}; | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
@@ -38,13 +49,2 @@ var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
}; | ||
var __values = (this && this.__values) || function(o) { | ||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
if (m) return m.call(o); | ||
if (o && typeof o.length === "number") return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); | ||
}; | ||
function flatten(array) { | ||
@@ -90,178 +90,22 @@ var _a; | ||
} | ||
var defaultValueAdjacencyMapOptions = { | ||
events: {}, | ||
filter: function () { return true; }, | ||
stateSerializer: serializeMachineState, | ||
eventSerializer: serializeEvent | ||
}; | ||
function getValueAdjacencyMapOptions(options) { | ||
return __assign(__assign({}, defaultValueAdjacencyMapOptions), options); | ||
} | ||
export function getValueAdjacencyMap(machine, options) { | ||
var optionsWithDefaults = getValueAdjacencyMapOptions(options); | ||
var filter = optionsWithDefaults.filter, stateSerializer = optionsWithDefaults.stateSerializer, eventSerializer = optionsWithDefaults.eventSerializer; | ||
var events = optionsWithDefaults.events; | ||
var adjacency = {}; | ||
function findAdjacencies(state) { | ||
var e_1, _a; | ||
var nextEvents = state.nextEvents; | ||
var stateHash = stateSerializer(state); | ||
if (adjacency[stateHash]) { | ||
return; | ||
} | ||
adjacency[stateHash] = {}; | ||
var potentialEvents = flatten(nextEvents.map(function (nextEvent) { | ||
var getNextEvents = events[nextEvent]; | ||
if (!getNextEvents) { | ||
return [{ type: nextEvent }]; | ||
} | ||
if (typeof getNextEvents === 'function') { | ||
return getNextEvents(state); | ||
} | ||
return getNextEvents; | ||
})).map(function (event) { return toEventObject(event); }); | ||
try { | ||
for (var potentialEvents_1 = __values(potentialEvents), potentialEvents_1_1 = potentialEvents_1.next(); !potentialEvents_1_1.done; potentialEvents_1_1 = potentialEvents_1.next()) { | ||
var event_1 = potentialEvents_1_1.value; | ||
var nextState = void 0; | ||
try { | ||
nextState = machine.transition(state, event_1); | ||
export function createDefaultMachineOptions(machine, options) { | ||
var _a = options !== null && options !== void 0 ? options : {}, getEvents = _a.events, otherOptions = __rest(_a, ["events"]); | ||
var traversalOptions = __assign({ serializeState: serializeMachineState, serializeEvent: serializeEvent, events: function (state) { | ||
var events = typeof getEvents === 'function' ? getEvents(state) : getEvents !== null && getEvents !== void 0 ? getEvents : []; | ||
return flatten(state.nextEvents.map(function (type) { | ||
var matchingEvents = events.filter(function (ev) { return ev.type === type; }); | ||
if (matchingEvents.length) { | ||
return matchingEvents; | ||
} | ||
catch (e) { | ||
throw new Error("Unable to transition from state ".concat(stateSerializer(state), " on event ").concat(eventSerializer(event_1), ": ").concat(e.message)); | ||
} | ||
if ((!filter || filter(nextState)) && | ||
stateHash !== stateSerializer(nextState)) { | ||
adjacency[stateHash][eventSerializer(event_1)] = { | ||
state: nextState, | ||
event: event_1 | ||
}; | ||
findAdjacencies(nextState); | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (potentialEvents_1_1 && !potentialEvents_1_1.done && (_a = potentialEvents_1.return)) _a.call(potentialEvents_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
} | ||
findAdjacencies(machine.initialState); | ||
return adjacency; | ||
return [{ type: type }]; | ||
})); | ||
}, fromState: machine.initialState }, otherOptions); | ||
return traversalOptions; | ||
} | ||
var defaultMachineStateOptions = { | ||
serializeState: serializeMachineState, | ||
serializeEvent: serializeEvent, | ||
eventCases: {}, | ||
getEvents: function (state) { | ||
return state.nextEvents.map(function (type) { return ({ type: type }); }); | ||
} | ||
}; | ||
export function getShortestPlans(machine, options) { | ||
var resolvedOptions = resolveTraversalOptions(options, defaultMachineStateOptions); | ||
return traverseShortestPlans({ | ||
transition: function (state, event) { return machine.transition(state, event); }, | ||
initialState: machine.initialState | ||
}, resolvedOptions); | ||
export function createDefaultBehaviorOptions(_behavior) { | ||
return { | ||
serializeState: function (state) { return JSON.stringify(state); }, | ||
serializeEvent: serializeEvent | ||
}; | ||
} | ||
export function traverseShortestPlans(behavior, options) { | ||
var e_2, _a, e_3, _b; | ||
var optionsWithDefaults = resolveTraversalOptions(options); | ||
var serializeState = optionsWithDefaults.serializeState; | ||
var adjacency = performDepthFirstTraversal(behavior, optionsWithDefaults); | ||
// weight, state, event | ||
var weightMap = new Map(); | ||
var stateMap = new Map(); | ||
var initialSerializedState = serializeState(behavior.initialState, null); | ||
stateMap.set(initialSerializedState, behavior.initialState); | ||
weightMap.set(initialSerializedState, [0, undefined, undefined]); | ||
var unvisited = new Set(); | ||
var visited = new Set(); | ||
unvisited.add(initialSerializedState); | ||
while (unvisited.size > 0) { | ||
try { | ||
for (var unvisited_1 = (e_2 = void 0, __values(unvisited)), unvisited_1_1 = unvisited_1.next(); !unvisited_1_1.done; unvisited_1_1 = unvisited_1.next()) { | ||
var serializedState = unvisited_1_1.value; | ||
var _c = __read(weightMap.get(serializedState), 1), weight = _c[0]; | ||
try { | ||
for (var _d = (e_3 = void 0, __values(Object.keys(adjacency[serializedState].transitions))), _e = _d.next(); !_e.done; _e = _d.next()) { | ||
var event_2 = _e.value; | ||
var _f = adjacency[serializedState].transitions[event_2], nextState = _f.state, eventObject = _f.event; | ||
var nextSerializedState = serializeState(nextState, eventObject); | ||
stateMap.set(nextSerializedState, nextState); | ||
if (!weightMap.has(nextSerializedState)) { | ||
weightMap.set(nextSerializedState, [ | ||
weight + 1, | ||
serializedState, | ||
eventObject | ||
]); | ||
} | ||
else { | ||
var _g = __read(weightMap.get(nextSerializedState), 1), nextWeight = _g[0]; | ||
if (nextWeight > weight + 1) { | ||
weightMap.set(nextSerializedState, [ | ||
weight + 1, | ||
serializedState, | ||
eventObject | ||
]); | ||
} | ||
} | ||
if (!visited.has(nextSerializedState)) { | ||
unvisited.add(nextSerializedState); | ||
} | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (_e && !_e.done && (_b = _d.return)) _b.call(_d); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
visited.add(serializedState); | ||
unvisited.delete(serializedState); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (unvisited_1_1 && !unvisited_1_1.done && (_a = unvisited_1.return)) _a.call(unvisited_1); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
} | ||
var statePlanMap = {}; | ||
weightMap.forEach(function (_a, stateSerial) { | ||
var _b = __read(_a, 3), weight = _b[0], fromState = _b[1], fromEvent = _b[2]; | ||
var state = stateMap.get(stateSerial); | ||
statePlanMap[stateSerial] = { | ||
state: state, | ||
paths: !fromState | ||
? [ | ||
{ | ||
state: state, | ||
steps: [], | ||
weight: weight | ||
} | ||
] | ||
: [ | ||
{ | ||
state: state, | ||
steps: statePlanMap[fromState].paths[0].steps.concat({ | ||
state: stateMap.get(fromState), | ||
event: fromEvent | ||
}), | ||
weight: weight | ||
} | ||
] | ||
}; | ||
}); | ||
return Object.values(statePlanMap); | ||
} | ||
export function getSimplePlans(machine, options) { | ||
var resolvedOptions = resolveTraversalOptions(options, defaultMachineStateOptions); | ||
return traverseSimplePlans(machine, resolvedOptions); | ||
} | ||
export function toDirectedGraph(stateNode) { | ||
@@ -300,206 +144,22 @@ var edges = flatten(stateNode.transitions.map(function (t, transitionIndex) { | ||
} | ||
export function getPathFromEvents(behavior, events) { | ||
var e_4, _a; | ||
var optionsWithDefaults = resolveTraversalOptions({ | ||
getEvents: function () { | ||
return events; | ||
} | ||
}, defaultMachineStateOptions); | ||
var serializeState = optionsWithDefaults.serializeState, serializeEvent = optionsWithDefaults.serializeEvent; | ||
var adjacency = performDepthFirstTraversal(behavior, optionsWithDefaults); | ||
var stateMap = new Map(); | ||
var path = []; | ||
var initialStateSerial = serializeState(behavior.initialState, null); | ||
stateMap.set(initialStateSerial, behavior.initialState); | ||
var stateSerial = initialStateSerial; | ||
var state = behavior.initialState; | ||
try { | ||
for (var events_1 = __values(events), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) { | ||
var event_3 = events_1_1.value; | ||
path.push({ | ||
state: stateMap.get(stateSerial), | ||
event: event_3 | ||
}); | ||
var eventSerial = serializeEvent(event_3); | ||
var _b = adjacency[stateSerial].transitions[eventSerial], nextState = _b.state, _nextEvent = _b.event; | ||
if (!nextState) { | ||
throw new Error("Invalid transition from ".concat(stateSerial, " with ").concat(eventSerial)); | ||
} | ||
var nextStateSerial = serializeState(nextState, event_3); | ||
stateMap.set(nextStateSerial, nextState); | ||
stateSerial = nextStateSerial; | ||
state = nextState; | ||
} | ||
} | ||
catch (e_4_1) { e_4 = { error: e_4_1 }; } | ||
finally { | ||
try { | ||
if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1); | ||
} | ||
finally { if (e_4) throw e_4.error; } | ||
} | ||
return { | ||
state: state, | ||
steps: path, | ||
weight: path.length | ||
}; | ||
} | ||
export function performDepthFirstTraversal(behavior, options) { | ||
var e_5, _a; | ||
var transition = behavior.transition, initialState = behavior.initialState; | ||
var _b = resolveTraversalOptions(options), serializeEvent = _b.serializeEvent, serializeState = _b.serializeState, getEvents = _b.getEvents, eventCases = _b.eventCases, limit = _b.traversalLimit; | ||
var adj = {}; | ||
var iterations = 0; | ||
var queue = [[initialState, null]]; | ||
while (queue.length) { | ||
var _c = __read(queue.shift(), 2), state = _c[0], event_4 = _c[1]; | ||
if (iterations++ > limit) { | ||
throw new Error('Traversal limit exceeded'); | ||
} | ||
var serializedState = serializeState(state, event_4); | ||
if (adj[serializedState]) { | ||
continue; | ||
} | ||
adj[serializedState] = { | ||
state: state, | ||
transitions: {} | ||
}; | ||
var events = getEvents(state, eventCases); | ||
try { | ||
for (var events_2 = (e_5 = void 0, __values(events)), events_2_1 = events_2.next(); !events_2_1.done; events_2_1 = events_2.next()) { | ||
var subEvent = events_2_1.value; | ||
var nextState = transition(state, subEvent); | ||
if (!options.filter || options.filter(nextState, subEvent)) { | ||
adj[serializedState].transitions[serializeEvent(subEvent)] = { | ||
event: subEvent, | ||
state: nextState | ||
}; | ||
queue.push([nextState, subEvent]); | ||
} | ||
} | ||
} | ||
catch (e_5_1) { e_5 = { error: e_5_1 }; } | ||
finally { | ||
try { | ||
if (events_2_1 && !events_2_1.done && (_a = events_2.return)) _a.call(events_2); | ||
} | ||
finally { if (e_5) throw e_5.error; } | ||
} | ||
} | ||
return adj; | ||
} | ||
function resolveTraversalOptions(traversalOptions, defaultOptions) { | ||
export function resolveTraversalOptions(traversalOptions, defaultOptions) { | ||
var _a, _b; | ||
var serializeState = (_b = (_a = traversalOptions === null || traversalOptions === void 0 ? void 0 : traversalOptions.serializeState) !== null && _a !== void 0 ? _a : defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.serializeState) !== null && _b !== void 0 ? _b : (function (state) { return JSON.stringify(state); }); | ||
return __assign(__assign({ serializeState: serializeState, serializeEvent: serializeEvent, filter: function () { return true; }, eventCases: {}, getEvents: function () { return []; }, traversalLimit: Infinity }, defaultOptions), traversalOptions); | ||
var traversalConfig = __assign(__assign({ serializeState: serializeState, serializeEvent: serializeEvent, filter: function () { return true; }, events: [], traversalLimit: Infinity, fromState: undefined, toState: undefined, | ||
// Traversal should not continue past the `toState` predicate | ||
// since the target state has already been reached at that point | ||
stopCondition: traversalOptions === null || traversalOptions === void 0 ? void 0 : traversalOptions.toState }, defaultOptions), traversalOptions); | ||
return traversalConfig; | ||
} | ||
export function traverseSimplePlans(behavior, options) { | ||
var e_6, _a; | ||
var initialState = behavior.initialState; | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var serializeState = resolvedOptions.serializeState; | ||
var adjacency = performDepthFirstTraversal(behavior, resolvedOptions); | ||
var stateMap = new Map(); | ||
var visitCtx = { | ||
vertices: new Set(), | ||
edges: new Set() | ||
export function joinPaths(path1, path2) { | ||
var _a, _b; | ||
var secondPathSource = (_b = (_a = path2.steps[0]) === null || _a === void 0 ? void 0 : _a.state) !== null && _b !== void 0 ? _b : path2.state; | ||
if (secondPathSource !== path1.state) { | ||
throw new Error("Paths cannot be joined"); | ||
} | ||
return { | ||
state: path2.state, | ||
steps: path1.steps.concat(path2.steps), | ||
weight: path1.weight + path2.weight | ||
}; | ||
var path = []; | ||
var pathMap = {}; | ||
function util(fromState, toStateSerial, event) { | ||
var e_7, _a; | ||
var fromStateSerial = serializeState(fromState, event); | ||
visitCtx.vertices.add(fromStateSerial); | ||
if (fromStateSerial === toStateSerial) { | ||
if (!pathMap[toStateSerial]) { | ||
pathMap[toStateSerial] = { | ||
state: stateMap.get(toStateSerial), | ||
paths: [] | ||
}; | ||
} | ||
var toStatePlan = pathMap[toStateSerial]; | ||
var path2 = { | ||
state: fromState, | ||
weight: path.length, | ||
steps: __spreadArray([], __read(path), false) | ||
}; | ||
toStatePlan.paths.push(path2); | ||
} | ||
else { | ||
try { | ||
for (var _b = __values(Object.keys(adjacency[fromStateSerial].transitions)), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var serializedEvent = _c.value; | ||
var _d = adjacency[fromStateSerial].transitions[serializedEvent], nextState = _d.state, subEvent = _d.event; | ||
if (!(serializedEvent in adjacency[fromStateSerial].transitions)) { | ||
continue; | ||
} | ||
var nextStateSerial = serializeState(nextState, subEvent); | ||
stateMap.set(nextStateSerial, nextState); | ||
if (!visitCtx.vertices.has(serializeState(nextState, subEvent))) { | ||
visitCtx.edges.add(serializedEvent); | ||
path.push({ | ||
state: stateMap.get(fromStateSerial), | ||
event: subEvent | ||
}); | ||
util(nextState, toStateSerial, subEvent); | ||
} | ||
} | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
} | ||
path.pop(); | ||
visitCtx.vertices.delete(fromStateSerial); | ||
} | ||
var initialStateSerial = serializeState(initialState, null); | ||
stateMap.set(initialStateSerial, initialState); | ||
try { | ||
for (var _b = __values(Object.keys(adjacency)), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var nextStateSerial = _c.value; | ||
util(initialState, nextStateSerial, null); | ||
} | ||
} | ||
catch (e_6_1) { e_6 = { error: e_6_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
} | ||
return Object.values(pathMap); | ||
} | ||
function filterPlans(plans, predicate) { | ||
var filteredPlans = plans.filter(function (plan) { return predicate(plan.state, plan); }); | ||
return filteredPlans; | ||
} | ||
export function traverseSimplePathsTo(behavior, predicate, options) { | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var simplePlansMap = traverseSimplePlans(behavior, resolvedOptions); | ||
return filterPlans(simplePlansMap, predicate); | ||
} | ||
export function traverseSimplePathsFromTo(behavior, fromPredicate, toPredicate, options) { | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var simplePlansMap = traverseSimplePlans(behavior, resolvedOptions); | ||
// Return all plans that contain a "from" state and target a "to" state | ||
return filterPlans(simplePlansMap, function (state, plan) { | ||
return (toPredicate(state) && plan.paths.some(function (path) { return fromPredicate(path.state); })); | ||
}); | ||
} | ||
export function traverseShortestPathsTo(behavior, predicate, options) { | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var simplePlansMap = traverseShortestPlans(behavior, resolvedOptions); | ||
return filterPlans(simplePlansMap, predicate); | ||
} | ||
export function traverseShortestPathsFromTo(behavior, fromPredicate, toPredicate, options) { | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var shortesPlansMap = traverseShortestPlans(behavior, resolvedOptions); | ||
// Return all plans that contain a "from" state and target a "to" state | ||
return filterPlans(shortesPlansMap, function (state, plan) { | ||
return (toPredicate(state) && plan.paths.some(function (path) { return fromPredicate(path.state); })); | ||
}); | ||
} |
@@ -1,3 +0,7 @@ | ||
export { getStateNodes, getPathFromEvents, getSimplePlans, getShortestPlans, serializeEvent, serializeMachineState as serializeState, toDirectedGraph, performDepthFirstTraversal, traverseShortestPlans, traverseSimplePlans, traverseSimplePathsTo } from './graph'; | ||
export { getStateNodes, serializeEvent, serializeMachineState as serializeState, toDirectedGraph, joinPaths, AdjacencyMap, AdjacencyValue } from './graph'; | ||
export { getMachineSimplePaths, getSimplePaths } from './simplePaths'; | ||
export { getShortestPaths, getMachineShortestPaths } from './shortestPaths'; | ||
export { getPathsFromEvents } from './pathFromEvents'; | ||
export { getAdjacencyMap } from './adjacency'; | ||
export * from './types'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,2 +0,6 @@ | ||
export { getStateNodes, getPathFromEvents, getSimplePlans, getShortestPlans, serializeEvent, serializeMachineState as serializeState, toDirectedGraph, performDepthFirstTraversal, traverseShortestPlans, traverseSimplePlans, traverseSimplePathsTo } from './graph'; | ||
export { getStateNodes, serializeEvent, serializeMachineState as serializeState, toDirectedGraph, joinPaths } from './graph'; | ||
export { getMachineSimplePaths, getSimplePaths } from './simplePaths'; | ||
export { getShortestPaths, getMachineShortestPaths } from './shortestPaths'; | ||
export { getPathsFromEvents } from './pathFromEvents'; | ||
export { getAdjacencyMap } from './adjacency'; | ||
export * from './types'; |
@@ -89,4 +89,4 @@ import { EventObject, StateValue, StateNode, TransitionDefinition } from 'xstate'; | ||
filter?: (state: TState) => boolean; | ||
stateSerializer?: (state: TState) => string; | ||
eventSerializer?: (event: TEvent) => string; | ||
serializeState?: (state: TState) => string; | ||
serializeEvent?: (event: TEvent) => string; | ||
} | ||
@@ -98,25 +98,16 @@ export interface VisitedContext<TState, TEvent> { | ||
} | ||
export interface SerializationOptions<TState, TEvent extends EventObject> { | ||
eventCases: EventCaseMap<TState, TEvent>; | ||
serializeState: (state: TState, event: TEvent | null) => string; | ||
export interface SerializationConfig<TState, TEvent extends EventObject> { | ||
serializeState: (state: TState, event: TEvent | undefined, prevState?: TState) => string; | ||
serializeEvent: (event: TEvent) => string; | ||
} | ||
/** | ||
* A sample event object payload (_without_ the `type` property). | ||
* | ||
* @example | ||
* | ||
* ```js | ||
* { | ||
* value: 'testValue', | ||
* other: 'something', | ||
* id: 42 | ||
* } | ||
* ``` | ||
*/ | ||
declare type EventCase<TEvent extends EventObject> = Omit<TEvent, 'type'>; | ||
export interface TraversalOptions<TState, TEvent extends EventObject> extends SerializationOptions<TState, TEvent> { | ||
filter?: (state: TState, event: TEvent) => boolean; | ||
getEvents?: (state: TState, cases: EventCaseMap<TState, TEvent>) => ReadonlyArray<TEvent>; | ||
export declare type SerializationOptions<TState, TEvent extends EventObject> = Partial<Pick<SerializationConfig<TState, TEvent>, 'serializeState' | 'serializeEvent'>>; | ||
export declare type TraversalOptions<TState, TEvent extends EventObject> = SerializationOptions<TState, TEvent> & Partial<Pick<TraversalConfig<TState, TEvent>, 'filter' | 'events' | 'traversalLimit' | 'fromState' | 'stopCondition' | 'toState'>>; | ||
export interface TraversalConfig<TState, TEvent extends EventObject> extends SerializationConfig<TState, TEvent> { | ||
/** | ||
* Determines whether to traverse a transition from `state` on | ||
* `event` when building the adjacency map. | ||
*/ | ||
filter: (state: TState, event: TEvent) => boolean; | ||
events: readonly TEvent[] | ((state: TState) => readonly TEvent[]); | ||
/** | ||
* The maximum number of traversals to perform when calculating | ||
@@ -127,7 +118,11 @@ * the state transition adjacency map. | ||
*/ | ||
traversalLimit?: number; | ||
traversalLimit: number; | ||
fromState: TState | undefined; | ||
/** | ||
* When true, traversal of the adjacency map will stop | ||
* for that current state. | ||
*/ | ||
stopCondition: ((state: TState) => boolean) | undefined; | ||
toState: ((state: TState) => boolean) | undefined; | ||
} | ||
export declare type EventCaseMap<TState, TEvent extends EventObject> = { | ||
[E in TEvent as E['type']]?: ((state: TState) => Array<EventCase<E>>) | Array<EventCase<E>>; | ||
}; | ||
declare type Brand<T, Tag extends string> = T & { | ||
@@ -134,0 +129,0 @@ __tag: Tag; |
import type { Event, EventObject, AnyStateMachine, AnyState, StateFrom, EventFrom } from 'xstate'; | ||
import type { SerializedEvent, SerializedState, SimpleBehavior, StatePath, StatePlan, ValueAdjacencyMap, ValueAdjacencyMapOptions, DirectedGraphNode, TraversalOptions, AnyStateNode } from './types'; | ||
import type { SerializedEvent, SerializedState, SimpleBehavior, StatePath, DirectedGraphNode, TraversalOptions, AnyStateNode, TraversalConfig } from './types'; | ||
export declare function toEventObject<TEvent extends EventObject>(event: Event<TEvent>): TEvent; | ||
@@ -12,26 +12,19 @@ /** | ||
export declare function serializeEvent<TEvent extends EventObject>(event: TEvent): SerializedEvent; | ||
export declare function getValueAdjacencyMap<TMachine extends AnyStateMachine>(machine: TMachine, options?: ValueAdjacencyMapOptions<StateFrom<TMachine>, EventFrom<TMachine>>): ValueAdjacencyMap<StateFrom<TMachine>, EventFrom<TMachine>>; | ||
export declare function getShortestPlans<TMachine extends AnyStateMachine>(machine: TMachine, options?: Partial<TraversalOptions<StateFrom<TMachine>, EventFrom<TMachine>>>): Array<StatePlan<StateFrom<TMachine>, EventFrom<TMachine>>>; | ||
export declare function traverseShortestPlans<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, options?: Partial<TraversalOptions<TState, TEvent>>): Array<StatePlan<TState, TEvent>>; | ||
export declare function getSimplePlans<TMachine extends AnyStateMachine>(machine: TMachine, options?: Partial<TraversalOptions<StateFrom<TMachine>, EventFrom<TMachine>>>): Array<StatePlan<StateFrom<TMachine>, EventFrom<TMachine>>>; | ||
export declare function createDefaultMachineOptions<TMachine extends AnyStateMachine>(machine: TMachine, options?: TraversalOptions<StateFrom<TMachine>, EventFrom<TMachine>>): TraversalOptions<StateFrom<TMachine>, EventFrom<TMachine>>; | ||
export declare function createDefaultBehaviorOptions<TBehavior extends SimpleBehavior<any, any>>(_behavior: TBehavior): TraversalOptions<any, any>; | ||
export declare function toDirectedGraph(stateNode: AnyStateNode | AnyStateMachine): DirectedGraphNode; | ||
export declare function getPathFromEvents<TState, TEvent extends EventObject = EventObject>(behavior: SimpleBehavior<TState, TEvent>, events: TEvent[]): StatePath<TState, TEvent>; | ||
interface AdjacencyMap<TState, TEvent> { | ||
[key: SerializedState]: { | ||
state: TState; | ||
transitions: { | ||
[key: SerializedEvent]: { | ||
event: TEvent; | ||
state: TState; | ||
}; | ||
export interface AdjacencyValue<TState, TEvent> { | ||
state: TState; | ||
transitions: { | ||
[key: SerializedEvent]: { | ||
event: TEvent; | ||
state: TState; | ||
}; | ||
}; | ||
} | ||
export declare function performDepthFirstTraversal<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, options: TraversalOptions<TState, TEvent>): AdjacencyMap<TState, TEvent>; | ||
export declare function traverseSimplePlans<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, options: Partial<TraversalOptions<TState, TEvent>>): Array<StatePlan<TState, TEvent>>; | ||
export declare function traverseSimplePathsTo<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, predicate: (state: TState) => boolean, options: TraversalOptions<TState, TEvent>): Array<StatePlan<TState, TEvent>>; | ||
export declare function traverseSimplePathsFromTo<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, fromPredicate: (state: TState) => boolean, toPredicate: (state: TState) => boolean, options: TraversalOptions<TState, TEvent>): Array<StatePlan<TState, TEvent>>; | ||
export declare function traverseShortestPathsTo<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, predicate: (state: TState) => boolean, options: TraversalOptions<TState, TEvent>): Array<StatePlan<TState, TEvent>>; | ||
export declare function traverseShortestPathsFromTo<TState, TEvent extends EventObject>(behavior: SimpleBehavior<TState, TEvent>, fromPredicate: (state: TState) => boolean, toPredicate: (state: TState) => boolean, options: TraversalOptions<TState, TEvent>): Array<StatePlan<TState, TEvent>>; | ||
export {}; | ||
export interface AdjacencyMap<TState, TEvent> { | ||
[key: SerializedState]: AdjacencyValue<TState, TEvent>; | ||
} | ||
export declare function resolveTraversalOptions<TState, TEvent extends EventObject>(traversalOptions?: TraversalOptions<TState, TEvent>, defaultOptions?: TraversalOptions<TState, TEvent>): TraversalConfig<TState, TEvent>; | ||
export declare function joinPaths<TState, TEvent extends EventObject>(path1: StatePath<TState, TEvent>, path2: StatePath<TState, TEvent>): StatePath<TState, TEvent>; | ||
//# sourceMappingURL=graph.d.ts.map |
443
lib/graph.js
@@ -13,2 +13,13 @@ "use strict"; | ||
}; | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
@@ -39,15 +50,4 @@ var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
}; | ||
var __values = (this && this.__values) || function(o) { | ||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; | ||
if (m) return m.call(o); | ||
if (o && typeof o.length === "number") return { | ||
next: function () { | ||
if (o && i >= o.length) o = void 0; | ||
return { value: o && o[i++], done: !o }; | ||
} | ||
}; | ||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.traverseShortestPathsFromTo = exports.traverseShortestPathsTo = exports.traverseSimplePathsFromTo = exports.traverseSimplePathsTo = exports.traverseSimplePlans = exports.performDepthFirstTraversal = exports.getPathFromEvents = exports.toDirectedGraph = exports.getSimplePlans = exports.traverseShortestPlans = exports.getShortestPlans = exports.getValueAdjacencyMap = exports.serializeEvent = exports.serializeMachineState = exports.getChildren = exports.getStateNodes = exports.toEventObject = void 0; | ||
exports.joinPaths = exports.resolveTraversalOptions = exports.toDirectedGraph = exports.createDefaultBehaviorOptions = exports.createDefaultMachineOptions = exports.serializeEvent = exports.serializeMachineState = exports.getChildren = exports.getStateNodes = exports.toEventObject = void 0; | ||
function flatten(array) { | ||
@@ -98,182 +98,24 @@ var _a; | ||
exports.serializeEvent = serializeEvent; | ||
var defaultValueAdjacencyMapOptions = { | ||
events: {}, | ||
filter: function () { return true; }, | ||
stateSerializer: serializeMachineState, | ||
eventSerializer: serializeEvent | ||
}; | ||
function getValueAdjacencyMapOptions(options) { | ||
return __assign(__assign({}, defaultValueAdjacencyMapOptions), options); | ||
} | ||
function getValueAdjacencyMap(machine, options) { | ||
var optionsWithDefaults = getValueAdjacencyMapOptions(options); | ||
var filter = optionsWithDefaults.filter, stateSerializer = optionsWithDefaults.stateSerializer, eventSerializer = optionsWithDefaults.eventSerializer; | ||
var events = optionsWithDefaults.events; | ||
var adjacency = {}; | ||
function findAdjacencies(state) { | ||
var e_1, _a; | ||
var nextEvents = state.nextEvents; | ||
var stateHash = stateSerializer(state); | ||
if (adjacency[stateHash]) { | ||
return; | ||
} | ||
adjacency[stateHash] = {}; | ||
var potentialEvents = flatten(nextEvents.map(function (nextEvent) { | ||
var getNextEvents = events[nextEvent]; | ||
if (!getNextEvents) { | ||
return [{ type: nextEvent }]; | ||
} | ||
if (typeof getNextEvents === 'function') { | ||
return getNextEvents(state); | ||
} | ||
return getNextEvents; | ||
})).map(function (event) { return toEventObject(event); }); | ||
try { | ||
for (var potentialEvents_1 = __values(potentialEvents), potentialEvents_1_1 = potentialEvents_1.next(); !potentialEvents_1_1.done; potentialEvents_1_1 = potentialEvents_1.next()) { | ||
var event_1 = potentialEvents_1_1.value; | ||
var nextState = void 0; | ||
try { | ||
nextState = machine.transition(state, event_1); | ||
function createDefaultMachineOptions(machine, options) { | ||
var _a = options !== null && options !== void 0 ? options : {}, getEvents = _a.events, otherOptions = __rest(_a, ["events"]); | ||
var traversalOptions = __assign({ serializeState: serializeMachineState, serializeEvent: serializeEvent, events: function (state) { | ||
var events = typeof getEvents === 'function' ? getEvents(state) : getEvents !== null && getEvents !== void 0 ? getEvents : []; | ||
return flatten(state.nextEvents.map(function (type) { | ||
var matchingEvents = events.filter(function (ev) { return ev.type === type; }); | ||
if (matchingEvents.length) { | ||
return matchingEvents; | ||
} | ||
catch (e) { | ||
throw new Error("Unable to transition from state ".concat(stateSerializer(state), " on event ").concat(eventSerializer(event_1), ": ").concat(e.message)); | ||
} | ||
if ((!filter || filter(nextState)) && | ||
stateHash !== stateSerializer(nextState)) { | ||
adjacency[stateHash][eventSerializer(event_1)] = { | ||
state: nextState, | ||
event: event_1 | ||
}; | ||
findAdjacencies(nextState); | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (potentialEvents_1_1 && !potentialEvents_1_1.done && (_a = potentialEvents_1.return)) _a.call(potentialEvents_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
} | ||
findAdjacencies(machine.initialState); | ||
return adjacency; | ||
return [{ type: type }]; | ||
})); | ||
}, fromState: machine.initialState }, otherOptions); | ||
return traversalOptions; | ||
} | ||
exports.getValueAdjacencyMap = getValueAdjacencyMap; | ||
var defaultMachineStateOptions = { | ||
serializeState: serializeMachineState, | ||
serializeEvent: serializeEvent, | ||
eventCases: {}, | ||
getEvents: function (state) { | ||
return state.nextEvents.map(function (type) { return ({ type: type }); }); | ||
} | ||
}; | ||
function getShortestPlans(machine, options) { | ||
var resolvedOptions = resolveTraversalOptions(options, defaultMachineStateOptions); | ||
return traverseShortestPlans({ | ||
transition: function (state, event) { return machine.transition(state, event); }, | ||
initialState: machine.initialState | ||
}, resolvedOptions); | ||
exports.createDefaultMachineOptions = createDefaultMachineOptions; | ||
function createDefaultBehaviorOptions(_behavior) { | ||
return { | ||
serializeState: function (state) { return JSON.stringify(state); }, | ||
serializeEvent: serializeEvent | ||
}; | ||
} | ||
exports.getShortestPlans = getShortestPlans; | ||
function traverseShortestPlans(behavior, options) { | ||
var e_2, _a, e_3, _b; | ||
var optionsWithDefaults = resolveTraversalOptions(options); | ||
var serializeState = optionsWithDefaults.serializeState; | ||
var adjacency = performDepthFirstTraversal(behavior, optionsWithDefaults); | ||
// weight, state, event | ||
var weightMap = new Map(); | ||
var stateMap = new Map(); | ||
var initialSerializedState = serializeState(behavior.initialState, null); | ||
stateMap.set(initialSerializedState, behavior.initialState); | ||
weightMap.set(initialSerializedState, [0, undefined, undefined]); | ||
var unvisited = new Set(); | ||
var visited = new Set(); | ||
unvisited.add(initialSerializedState); | ||
while (unvisited.size > 0) { | ||
try { | ||
for (var unvisited_1 = (e_2 = void 0, __values(unvisited)), unvisited_1_1 = unvisited_1.next(); !unvisited_1_1.done; unvisited_1_1 = unvisited_1.next()) { | ||
var serializedState = unvisited_1_1.value; | ||
var _c = __read(weightMap.get(serializedState), 1), weight = _c[0]; | ||
try { | ||
for (var _d = (e_3 = void 0, __values(Object.keys(adjacency[serializedState].transitions))), _e = _d.next(); !_e.done; _e = _d.next()) { | ||
var event_2 = _e.value; | ||
var _f = adjacency[serializedState].transitions[event_2], nextState = _f.state, eventObject = _f.event; | ||
var nextSerializedState = serializeState(nextState, eventObject); | ||
stateMap.set(nextSerializedState, nextState); | ||
if (!weightMap.has(nextSerializedState)) { | ||
weightMap.set(nextSerializedState, [ | ||
weight + 1, | ||
serializedState, | ||
eventObject | ||
]); | ||
} | ||
else { | ||
var _g = __read(weightMap.get(nextSerializedState), 1), nextWeight = _g[0]; | ||
if (nextWeight > weight + 1) { | ||
weightMap.set(nextSerializedState, [ | ||
weight + 1, | ||
serializedState, | ||
eventObject | ||
]); | ||
} | ||
} | ||
if (!visited.has(nextSerializedState)) { | ||
unvisited.add(nextSerializedState); | ||
} | ||
} | ||
} | ||
catch (e_3_1) { e_3 = { error: e_3_1 }; } | ||
finally { | ||
try { | ||
if (_e && !_e.done && (_b = _d.return)) _b.call(_d); | ||
} | ||
finally { if (e_3) throw e_3.error; } | ||
} | ||
visited.add(serializedState); | ||
unvisited.delete(serializedState); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (unvisited_1_1 && !unvisited_1_1.done && (_a = unvisited_1.return)) _a.call(unvisited_1); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
} | ||
var statePlanMap = {}; | ||
weightMap.forEach(function (_a, stateSerial) { | ||
var _b = __read(_a, 3), weight = _b[0], fromState = _b[1], fromEvent = _b[2]; | ||
var state = stateMap.get(stateSerial); | ||
statePlanMap[stateSerial] = { | ||
state: state, | ||
paths: !fromState | ||
? [ | ||
{ | ||
state: state, | ||
steps: [], | ||
weight: weight | ||
} | ||
] | ||
: [ | ||
{ | ||
state: state, | ||
steps: statePlanMap[fromState].paths[0].steps.concat({ | ||
state: stateMap.get(fromState), | ||
event: fromEvent | ||
}), | ||
weight: weight | ||
} | ||
] | ||
}; | ||
}); | ||
return Object.values(statePlanMap); | ||
} | ||
exports.traverseShortestPlans = traverseShortestPlans; | ||
function getSimplePlans(machine, options) { | ||
var resolvedOptions = resolveTraversalOptions(options, defaultMachineStateOptions); | ||
return traverseSimplePlans(machine, resolvedOptions); | ||
} | ||
exports.getSimplePlans = getSimplePlans; | ||
exports.createDefaultBehaviorOptions = createDefaultBehaviorOptions; | ||
function toDirectedGraph(stateNode) { | ||
@@ -313,213 +155,24 @@ var edges = flatten(stateNode.transitions.map(function (t, transitionIndex) { | ||
exports.toDirectedGraph = toDirectedGraph; | ||
function getPathFromEvents(behavior, events) { | ||
var e_4, _a; | ||
var optionsWithDefaults = resolveTraversalOptions({ | ||
getEvents: function () { | ||
return events; | ||
} | ||
}, defaultMachineStateOptions); | ||
var serializeState = optionsWithDefaults.serializeState, serializeEvent = optionsWithDefaults.serializeEvent; | ||
var adjacency = performDepthFirstTraversal(behavior, optionsWithDefaults); | ||
var stateMap = new Map(); | ||
var path = []; | ||
var initialStateSerial = serializeState(behavior.initialState, null); | ||
stateMap.set(initialStateSerial, behavior.initialState); | ||
var stateSerial = initialStateSerial; | ||
var state = behavior.initialState; | ||
try { | ||
for (var events_1 = __values(events), events_1_1 = events_1.next(); !events_1_1.done; events_1_1 = events_1.next()) { | ||
var event_3 = events_1_1.value; | ||
path.push({ | ||
state: stateMap.get(stateSerial), | ||
event: event_3 | ||
}); | ||
var eventSerial = serializeEvent(event_3); | ||
var _b = adjacency[stateSerial].transitions[eventSerial], nextState = _b.state, _nextEvent = _b.event; | ||
if (!nextState) { | ||
throw new Error("Invalid transition from ".concat(stateSerial, " with ").concat(eventSerial)); | ||
} | ||
var nextStateSerial = serializeState(nextState, event_3); | ||
stateMap.set(nextStateSerial, nextState); | ||
stateSerial = nextStateSerial; | ||
state = nextState; | ||
} | ||
} | ||
catch (e_4_1) { e_4 = { error: e_4_1 }; } | ||
finally { | ||
try { | ||
if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1); | ||
} | ||
finally { if (e_4) throw e_4.error; } | ||
} | ||
return { | ||
state: state, | ||
steps: path, | ||
weight: path.length | ||
}; | ||
} | ||
exports.getPathFromEvents = getPathFromEvents; | ||
function performDepthFirstTraversal(behavior, options) { | ||
var e_5, _a; | ||
var transition = behavior.transition, initialState = behavior.initialState; | ||
var _b = resolveTraversalOptions(options), serializeEvent = _b.serializeEvent, serializeState = _b.serializeState, getEvents = _b.getEvents, eventCases = _b.eventCases, limit = _b.traversalLimit; | ||
var adj = {}; | ||
var iterations = 0; | ||
var queue = [[initialState, null]]; | ||
while (queue.length) { | ||
var _c = __read(queue.shift(), 2), state = _c[0], event_4 = _c[1]; | ||
if (iterations++ > limit) { | ||
throw new Error('Traversal limit exceeded'); | ||
} | ||
var serializedState = serializeState(state, event_4); | ||
if (adj[serializedState]) { | ||
continue; | ||
} | ||
adj[serializedState] = { | ||
state: state, | ||
transitions: {} | ||
}; | ||
var events = getEvents(state, eventCases); | ||
try { | ||
for (var events_2 = (e_5 = void 0, __values(events)), events_2_1 = events_2.next(); !events_2_1.done; events_2_1 = events_2.next()) { | ||
var subEvent = events_2_1.value; | ||
var nextState = transition(state, subEvent); | ||
if (!options.filter || options.filter(nextState, subEvent)) { | ||
adj[serializedState].transitions[serializeEvent(subEvent)] = { | ||
event: subEvent, | ||
state: nextState | ||
}; | ||
queue.push([nextState, subEvent]); | ||
} | ||
} | ||
} | ||
catch (e_5_1) { e_5 = { error: e_5_1 }; } | ||
finally { | ||
try { | ||
if (events_2_1 && !events_2_1.done && (_a = events_2.return)) _a.call(events_2); | ||
} | ||
finally { if (e_5) throw e_5.error; } | ||
} | ||
} | ||
return adj; | ||
} | ||
exports.performDepthFirstTraversal = performDepthFirstTraversal; | ||
function resolveTraversalOptions(traversalOptions, defaultOptions) { | ||
var _a, _b; | ||
var serializeState = (_b = (_a = traversalOptions === null || traversalOptions === void 0 ? void 0 : traversalOptions.serializeState) !== null && _a !== void 0 ? _a : defaultOptions === null || defaultOptions === void 0 ? void 0 : defaultOptions.serializeState) !== null && _b !== void 0 ? _b : (function (state) { return JSON.stringify(state); }); | ||
return __assign(__assign({ serializeState: serializeState, serializeEvent: serializeEvent, filter: function () { return true; }, eventCases: {}, getEvents: function () { return []; }, traversalLimit: Infinity }, defaultOptions), traversalOptions); | ||
var traversalConfig = __assign(__assign({ serializeState: serializeState, serializeEvent: serializeEvent, filter: function () { return true; }, events: [], traversalLimit: Infinity, fromState: undefined, toState: undefined, | ||
// Traversal should not continue past the `toState` predicate | ||
// since the target state has already been reached at that point | ||
stopCondition: traversalOptions === null || traversalOptions === void 0 ? void 0 : traversalOptions.toState }, defaultOptions), traversalOptions); | ||
return traversalConfig; | ||
} | ||
function traverseSimplePlans(behavior, options) { | ||
var e_6, _a; | ||
var initialState = behavior.initialState; | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var serializeState = resolvedOptions.serializeState; | ||
var adjacency = performDepthFirstTraversal(behavior, resolvedOptions); | ||
var stateMap = new Map(); | ||
var visitCtx = { | ||
vertices: new Set(), | ||
edges: new Set() | ||
exports.resolveTraversalOptions = resolveTraversalOptions; | ||
function joinPaths(path1, path2) { | ||
var _a, _b; | ||
var secondPathSource = (_b = (_a = path2.steps[0]) === null || _a === void 0 ? void 0 : _a.state) !== null && _b !== void 0 ? _b : path2.state; | ||
if (secondPathSource !== path1.state) { | ||
throw new Error("Paths cannot be joined"); | ||
} | ||
return { | ||
state: path2.state, | ||
steps: path1.steps.concat(path2.steps), | ||
weight: path1.weight + path2.weight | ||
}; | ||
var path = []; | ||
var pathMap = {}; | ||
function util(fromState, toStateSerial, event) { | ||
var e_7, _a; | ||
var fromStateSerial = serializeState(fromState, event); | ||
visitCtx.vertices.add(fromStateSerial); | ||
if (fromStateSerial === toStateSerial) { | ||
if (!pathMap[toStateSerial]) { | ||
pathMap[toStateSerial] = { | ||
state: stateMap.get(toStateSerial), | ||
paths: [] | ||
}; | ||
} | ||
var toStatePlan = pathMap[toStateSerial]; | ||
var path2 = { | ||
state: fromState, | ||
weight: path.length, | ||
steps: __spreadArray([], __read(path), false) | ||
}; | ||
toStatePlan.paths.push(path2); | ||
} | ||
else { | ||
try { | ||
for (var _b = __values(Object.keys(adjacency[fromStateSerial].transitions)), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var serializedEvent = _c.value; | ||
var _d = adjacency[fromStateSerial].transitions[serializedEvent], nextState = _d.state, subEvent = _d.event; | ||
if (!(serializedEvent in adjacency[fromStateSerial].transitions)) { | ||
continue; | ||
} | ||
var nextStateSerial = serializeState(nextState, subEvent); | ||
stateMap.set(nextStateSerial, nextState); | ||
if (!visitCtx.vertices.has(serializeState(nextState, subEvent))) { | ||
visitCtx.edges.add(serializedEvent); | ||
path.push({ | ||
state: stateMap.get(fromStateSerial), | ||
event: subEvent | ||
}); | ||
util(nextState, toStateSerial, subEvent); | ||
} | ||
} | ||
} | ||
catch (e_7_1) { e_7 = { error: e_7_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_7) throw e_7.error; } | ||
} | ||
} | ||
path.pop(); | ||
visitCtx.vertices.delete(fromStateSerial); | ||
} | ||
var initialStateSerial = serializeState(initialState, null); | ||
stateMap.set(initialStateSerial, initialState); | ||
try { | ||
for (var _b = __values(Object.keys(adjacency)), _c = _b.next(); !_c.done; _c = _b.next()) { | ||
var nextStateSerial = _c.value; | ||
util(initialState, nextStateSerial, null); | ||
} | ||
} | ||
catch (e_6_1) { e_6 = { error: e_6_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b); | ||
} | ||
finally { if (e_6) throw e_6.error; } | ||
} | ||
return Object.values(pathMap); | ||
} | ||
exports.traverseSimplePlans = traverseSimplePlans; | ||
function filterPlans(plans, predicate) { | ||
var filteredPlans = plans.filter(function (plan) { return predicate(plan.state, plan); }); | ||
return filteredPlans; | ||
} | ||
function traverseSimplePathsTo(behavior, predicate, options) { | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var simplePlansMap = traverseSimplePlans(behavior, resolvedOptions); | ||
return filterPlans(simplePlansMap, predicate); | ||
} | ||
exports.traverseSimplePathsTo = traverseSimplePathsTo; | ||
function traverseSimplePathsFromTo(behavior, fromPredicate, toPredicate, options) { | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var simplePlansMap = traverseSimplePlans(behavior, resolvedOptions); | ||
// Return all plans that contain a "from" state and target a "to" state | ||
return filterPlans(simplePlansMap, function (state, plan) { | ||
return (toPredicate(state) && plan.paths.some(function (path) { return fromPredicate(path.state); })); | ||
}); | ||
} | ||
exports.traverseSimplePathsFromTo = traverseSimplePathsFromTo; | ||
function traverseShortestPathsTo(behavior, predicate, options) { | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var simplePlansMap = traverseShortestPlans(behavior, resolvedOptions); | ||
return filterPlans(simplePlansMap, predicate); | ||
} | ||
exports.traverseShortestPathsTo = traverseShortestPathsTo; | ||
function traverseShortestPathsFromTo(behavior, fromPredicate, toPredicate, options) { | ||
var resolvedOptions = resolveTraversalOptions(options); | ||
var shortesPlansMap = traverseShortestPlans(behavior, resolvedOptions); | ||
// Return all plans that contain a "from" state and target a "to" state | ||
return filterPlans(shortesPlansMap, function (state, plan) { | ||
return (toPredicate(state) && plan.paths.some(function (path) { return fromPredicate(path.state); })); | ||
}); | ||
} | ||
exports.traverseShortestPathsFromTo = traverseShortestPathsFromTo; | ||
exports.joinPaths = joinPaths; |
@@ -1,3 +0,7 @@ | ||
export { getStateNodes, getPathFromEvents, getSimplePlans, getShortestPlans, serializeEvent, serializeMachineState as serializeState, toDirectedGraph, performDepthFirstTraversal, traverseShortestPlans, traverseSimplePlans, traverseSimplePathsTo } from './graph'; | ||
export { getStateNodes, serializeEvent, serializeMachineState as serializeState, toDirectedGraph, joinPaths, AdjacencyMap, AdjacencyValue } from './graph'; | ||
export { getMachineSimplePaths, getSimplePaths } from './simplePaths'; | ||
export { getShortestPaths, getMachineShortestPaths } from './shortestPaths'; | ||
export { getPathsFromEvents } from './pathFromEvents'; | ||
export { getAdjacencyMap } from './adjacency'; | ||
export * from './types'; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -13,15 +17,19 @@ if (k2 === undefined) k2 = k; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.traverseSimplePathsTo = exports.traverseSimplePlans = exports.traverseShortestPlans = exports.performDepthFirstTraversal = exports.toDirectedGraph = exports.serializeState = exports.serializeEvent = exports.getShortestPlans = exports.getSimplePlans = exports.getPathFromEvents = exports.getStateNodes = void 0; | ||
exports.getAdjacencyMap = exports.getPathsFromEvents = exports.getMachineShortestPaths = exports.getShortestPaths = exports.getSimplePaths = exports.getMachineSimplePaths = exports.joinPaths = exports.toDirectedGraph = exports.serializeState = exports.serializeEvent = exports.getStateNodes = void 0; | ||
var graph_1 = require("./graph"); | ||
Object.defineProperty(exports, "getStateNodes", { enumerable: true, get: function () { return graph_1.getStateNodes; } }); | ||
Object.defineProperty(exports, "getPathFromEvents", { enumerable: true, get: function () { return graph_1.getPathFromEvents; } }); | ||
Object.defineProperty(exports, "getSimplePlans", { enumerable: true, get: function () { return graph_1.getSimplePlans; } }); | ||
Object.defineProperty(exports, "getShortestPlans", { enumerable: true, get: function () { return graph_1.getShortestPlans; } }); | ||
Object.defineProperty(exports, "serializeEvent", { enumerable: true, get: function () { return graph_1.serializeEvent; } }); | ||
Object.defineProperty(exports, "serializeState", { enumerable: true, get: function () { return graph_1.serializeMachineState; } }); | ||
Object.defineProperty(exports, "toDirectedGraph", { enumerable: true, get: function () { return graph_1.toDirectedGraph; } }); | ||
Object.defineProperty(exports, "performDepthFirstTraversal", { enumerable: true, get: function () { return graph_1.performDepthFirstTraversal; } }); | ||
Object.defineProperty(exports, "traverseShortestPlans", { enumerable: true, get: function () { return graph_1.traverseShortestPlans; } }); | ||
Object.defineProperty(exports, "traverseSimplePlans", { enumerable: true, get: function () { return graph_1.traverseSimplePlans; } }); | ||
Object.defineProperty(exports, "traverseSimplePathsTo", { enumerable: true, get: function () { return graph_1.traverseSimplePathsTo; } }); | ||
Object.defineProperty(exports, "joinPaths", { enumerable: true, get: function () { return graph_1.joinPaths; } }); | ||
var simplePaths_1 = require("./simplePaths"); | ||
Object.defineProperty(exports, "getMachineSimplePaths", { enumerable: true, get: function () { return simplePaths_1.getMachineSimplePaths; } }); | ||
Object.defineProperty(exports, "getSimplePaths", { enumerable: true, get: function () { return simplePaths_1.getSimplePaths; } }); | ||
var shortestPaths_1 = require("./shortestPaths"); | ||
Object.defineProperty(exports, "getShortestPaths", { enumerable: true, get: function () { return shortestPaths_1.getShortestPaths; } }); | ||
Object.defineProperty(exports, "getMachineShortestPaths", { enumerable: true, get: function () { return shortestPaths_1.getMachineShortestPaths; } }); | ||
var pathFromEvents_1 = require("./pathFromEvents"); | ||
Object.defineProperty(exports, "getPathsFromEvents", { enumerable: true, get: function () { return pathFromEvents_1.getPathsFromEvents; } }); | ||
var adjacency_1 = require("./adjacency"); | ||
Object.defineProperty(exports, "getAdjacencyMap", { enumerable: true, get: function () { return adjacency_1.getAdjacencyMap; } }); | ||
__exportStar(require("./types"), exports); |
@@ -89,4 +89,4 @@ import { EventObject, StateValue, StateNode, TransitionDefinition } from 'xstate'; | ||
filter?: (state: TState) => boolean; | ||
stateSerializer?: (state: TState) => string; | ||
eventSerializer?: (event: TEvent) => string; | ||
serializeState?: (state: TState) => string; | ||
serializeEvent?: (event: TEvent) => string; | ||
} | ||
@@ -98,25 +98,16 @@ export interface VisitedContext<TState, TEvent> { | ||
} | ||
export interface SerializationOptions<TState, TEvent extends EventObject> { | ||
eventCases: EventCaseMap<TState, TEvent>; | ||
serializeState: (state: TState, event: TEvent | null) => string; | ||
export interface SerializationConfig<TState, TEvent extends EventObject> { | ||
serializeState: (state: TState, event: TEvent | undefined, prevState?: TState) => string; | ||
serializeEvent: (event: TEvent) => string; | ||
} | ||
/** | ||
* A sample event object payload (_without_ the `type` property). | ||
* | ||
* @example | ||
* | ||
* ```js | ||
* { | ||
* value: 'testValue', | ||
* other: 'something', | ||
* id: 42 | ||
* } | ||
* ``` | ||
*/ | ||
declare type EventCase<TEvent extends EventObject> = Omit<TEvent, 'type'>; | ||
export interface TraversalOptions<TState, TEvent extends EventObject> extends SerializationOptions<TState, TEvent> { | ||
filter?: (state: TState, event: TEvent) => boolean; | ||
getEvents?: (state: TState, cases: EventCaseMap<TState, TEvent>) => ReadonlyArray<TEvent>; | ||
export declare type SerializationOptions<TState, TEvent extends EventObject> = Partial<Pick<SerializationConfig<TState, TEvent>, 'serializeState' | 'serializeEvent'>>; | ||
export declare type TraversalOptions<TState, TEvent extends EventObject> = SerializationOptions<TState, TEvent> & Partial<Pick<TraversalConfig<TState, TEvent>, 'filter' | 'events' | 'traversalLimit' | 'fromState' | 'stopCondition' | 'toState'>>; | ||
export interface TraversalConfig<TState, TEvent extends EventObject> extends SerializationConfig<TState, TEvent> { | ||
/** | ||
* Determines whether to traverse a transition from `state` on | ||
* `event` when building the adjacency map. | ||
*/ | ||
filter: (state: TState, event: TEvent) => boolean; | ||
events: readonly TEvent[] | ((state: TState) => readonly TEvent[]); | ||
/** | ||
* The maximum number of traversals to perform when calculating | ||
@@ -127,7 +118,11 @@ * the state transition adjacency map. | ||
*/ | ||
traversalLimit?: number; | ||
traversalLimit: number; | ||
fromState: TState | undefined; | ||
/** | ||
* When true, traversal of the adjacency map will stop | ||
* for that current state. | ||
*/ | ||
stopCondition: ((state: TState) => boolean) | undefined; | ||
toState: ((state: TState) => boolean) | undefined; | ||
} | ||
export declare type EventCaseMap<TState, TEvent extends EventObject> = { | ||
[E in TEvent as E['type']]?: ((state: TState) => Array<EventCase<E>>) | Array<EventCase<E>>; | ||
}; | ||
declare type Brand<T, Tag extends string> = T & { | ||
@@ -134,0 +129,0 @@ __tag: Tag; |
{ | ||
"name": "@xstate/graph", | ||
"version": "2.0.0-alpha.0", | ||
"version": "2.0.0-alpha.1", | ||
"description": "XState graph utilities", | ||
@@ -40,3 +40,3 @@ "keywords": [ | ||
"peerDependencies": { | ||
"xstate": "^4.32.1" | ||
"xstate": "^4.37.0" | ||
}, | ||
@@ -47,3 +47,3 @@ "devDependencies": { | ||
"ts-jest": "^26.5.6", | ||
"typescript": "^4.5.2", | ||
"typescript": "^4.8.4", | ||
"xstate": "*" | ||
@@ -50,0 +50,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
74411
35
1582
1