@doeixd/machine 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +1070 -0
- package/dist/cjs/development/index.js +198 -0
- package/dist/cjs/development/index.js.map +7 -0
- package/dist/cjs/production/index.js +1 -0
- package/dist/esm/development/index.js +175 -0
- package/dist/esm/development/index.js.map +7 -0
- package/dist/esm/production/index.js +1 -0
- package/dist/types/generators.d.ts +314 -0
- package/dist/types/generators.d.ts.map +1 -0
- package/dist/types/index.d.ts +339 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +110 -0
- package/src/devtools.ts +74 -0
- package/src/extract.ts +190 -0
- package/src/generators.ts +421 -0
- package/src/index.ts +528 -0
- package/src/primitives.ts +191 -0
- package/src/react.ts +44 -0
- package/src/solid.ts +502 -0
- package/src/test.ts +207 -0
- package/src/utils.ts +167 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
MachineBase: () => MachineBase,
|
|
24
|
+
createAsyncMachine: () => createAsyncMachine,
|
|
25
|
+
createFlow: () => createFlow,
|
|
26
|
+
createMachine: () => createMachine,
|
|
27
|
+
createMachineBuilder: () => createMachineBuilder,
|
|
28
|
+
createMachineFactory: () => createMachineFactory,
|
|
29
|
+
extendTransitions: () => extendTransitions,
|
|
30
|
+
hasState: () => hasState,
|
|
31
|
+
matchMachine: () => matchMachine,
|
|
32
|
+
next: () => next,
|
|
33
|
+
overrideTransitions: () => overrideTransitions,
|
|
34
|
+
run: () => run,
|
|
35
|
+
runAsync: () => runAsync,
|
|
36
|
+
runMachine: () => runMachine,
|
|
37
|
+
runSequence: () => runSequence,
|
|
38
|
+
runWithDebug: () => runWithDebug,
|
|
39
|
+
setContext: () => setContext,
|
|
40
|
+
step: () => step,
|
|
41
|
+
stepAsync: () => stepAsync,
|
|
42
|
+
yieldMachine: () => yieldMachine
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(src_exports);
|
|
45
|
+
|
|
46
|
+
// src/generators.ts
|
|
47
|
+
function run(flow, initial) {
|
|
48
|
+
const generator = flow(initial);
|
|
49
|
+
let current = initial;
|
|
50
|
+
while (true) {
|
|
51
|
+
const { value, done } = generator.next(current);
|
|
52
|
+
if (done) {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
current = value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function step(m) {
|
|
59
|
+
return function* () {
|
|
60
|
+
const received = yield m;
|
|
61
|
+
return received;
|
|
62
|
+
}();
|
|
63
|
+
}
|
|
64
|
+
function yieldMachine(m) {
|
|
65
|
+
return m;
|
|
66
|
+
}
|
|
67
|
+
function runSequence(initial, flows) {
|
|
68
|
+
return flows.reduce((machine, flow) => {
|
|
69
|
+
return run(flow, machine);
|
|
70
|
+
}, initial);
|
|
71
|
+
}
|
|
72
|
+
function createFlow(flow) {
|
|
73
|
+
return flow;
|
|
74
|
+
}
|
|
75
|
+
function runWithDebug(flow, initial, logger = (step2, m) => {
|
|
76
|
+
console.log(`Step ${step2}:`, m.context);
|
|
77
|
+
}) {
|
|
78
|
+
const generator = flow(initial);
|
|
79
|
+
let current = initial;
|
|
80
|
+
let stepCount = 0;
|
|
81
|
+
logger(stepCount, current);
|
|
82
|
+
while (true) {
|
|
83
|
+
const { value, done } = generator.next(current);
|
|
84
|
+
if (done) {
|
|
85
|
+
console.log("Final:", value);
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
current = value;
|
|
89
|
+
stepCount++;
|
|
90
|
+
logger(stepCount, current);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function runAsync(flow, initial) {
|
|
94
|
+
const generator = flow(initial);
|
|
95
|
+
let current = initial;
|
|
96
|
+
while (true) {
|
|
97
|
+
const { value, done } = await generator.next(current);
|
|
98
|
+
if (done) {
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
current = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function* stepAsync(m) {
|
|
105
|
+
const received = yield m;
|
|
106
|
+
return received;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/index.ts
|
|
110
|
+
function createMachine(context, fns) {
|
|
111
|
+
return Object.assign({ context }, fns);
|
|
112
|
+
}
|
|
113
|
+
function createAsyncMachine(context, fns) {
|
|
114
|
+
return Object.assign({ context }, fns);
|
|
115
|
+
}
|
|
116
|
+
function createMachineFactory() {
|
|
117
|
+
return (transformers) => {
|
|
118
|
+
const fns = Object.fromEntries(
|
|
119
|
+
Object.entries(transformers).map(([key, transform]) => [
|
|
120
|
+
key,
|
|
121
|
+
function(...args) {
|
|
122
|
+
const newContext = transform(this, ...args);
|
|
123
|
+
return createMachine(newContext, fns);
|
|
124
|
+
}
|
|
125
|
+
])
|
|
126
|
+
);
|
|
127
|
+
return (initialContext) => {
|
|
128
|
+
return createMachine(initialContext, fns);
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function setContext(machine, newContextOrFn) {
|
|
133
|
+
const { context, ...transitions } = machine;
|
|
134
|
+
const newContext = typeof newContextOrFn === "function" ? newContextOrFn(context) : newContextOrFn;
|
|
135
|
+
return createMachine(newContext, transitions);
|
|
136
|
+
}
|
|
137
|
+
function overrideTransitions(machine, overrides) {
|
|
138
|
+
const { context, ...originalTransitions } = machine;
|
|
139
|
+
const newTransitions = { ...originalTransitions, ...overrides };
|
|
140
|
+
return createMachine(context, newTransitions);
|
|
141
|
+
}
|
|
142
|
+
function extendTransitions(machine, newTransitions) {
|
|
143
|
+
const { context, ...originalTransitions } = machine;
|
|
144
|
+
const combinedTransitions = { ...originalTransitions, ...newTransitions };
|
|
145
|
+
return createMachine(context, combinedTransitions);
|
|
146
|
+
}
|
|
147
|
+
function createMachineBuilder(templateMachine) {
|
|
148
|
+
const { context, ...transitions } = templateMachine;
|
|
149
|
+
return (newContext) => {
|
|
150
|
+
return createMachine(newContext, transitions);
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function matchMachine(machine, discriminantKey, handlers) {
|
|
154
|
+
const discriminant = machine.context[discriminantKey];
|
|
155
|
+
const handler = handlers[discriminant];
|
|
156
|
+
if (!handler) {
|
|
157
|
+
throw new Error(`No handler found for state: ${String(discriminant)}`);
|
|
158
|
+
}
|
|
159
|
+
return handler(machine.context);
|
|
160
|
+
}
|
|
161
|
+
function hasState(machine, key, value) {
|
|
162
|
+
return machine.context[key] === value;
|
|
163
|
+
}
|
|
164
|
+
function runMachine(initial, onChange) {
|
|
165
|
+
let current = initial;
|
|
166
|
+
async function dispatch(event) {
|
|
167
|
+
const fn = current[event.type];
|
|
168
|
+
if (typeof fn !== "function") {
|
|
169
|
+
throw new Error(`[Machine] Unknown event type '${String(event.type)}' on current state.`);
|
|
170
|
+
}
|
|
171
|
+
const nextState = await fn.apply(current.context, event.args);
|
|
172
|
+
current = nextState;
|
|
173
|
+
onChange == null ? void 0 : onChange(current);
|
|
174
|
+
return current;
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
/** Gets the context of the current state of the machine. */
|
|
178
|
+
get state() {
|
|
179
|
+
return current.context;
|
|
180
|
+
},
|
|
181
|
+
/** Dispatches a type-safe event to the machine, triggering a transition. */
|
|
182
|
+
dispatch
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
var MachineBase = class {
|
|
186
|
+
/**
|
|
187
|
+
* Initializes a new machine instance with its starting context.
|
|
188
|
+
* @param context - The initial state of the machine.
|
|
189
|
+
*/
|
|
190
|
+
constructor(context) {
|
|
191
|
+
this.context = context;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
function next(m, update) {
|
|
195
|
+
const { context, ...transitions } = m;
|
|
196
|
+
return createMachine(update(context), transitions);
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/index.ts", "../../../src/generators.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * @file A tiny, immutable, and type-safe state machine library for TypeScript.\n * @author doeixd\n * @version 1.0.0\n */\n\n// =============================================================================\n// SECTION: CORE TYPES & INTERFACES\n// =============================================================================\n\n/**\n * A utility type that represents either a value of type T or a Promise that resolves to T.\n * @template T - The value type.\n */\nexport type MaybePromise<T> = T | Promise<T>;\n\n/**\n * The fundamental shape of any machine: a `context` object for state, and methods for transitions.\n * @template C - The context (state) object type.\n */\nexport type Machine<C extends object> = {\n /** The readonly state of the machine. */\n readonly context: C;\n} & Record<string, (...args: any[]) => Machine<any>>;\n\n/**\n * The shape of an asynchronous machine, where transitions can return Promises.\n * @template C - The context (state) object type.\n */\nexport type AsyncMachine<C extends object> = {\n /** The readonly state of the machine. */\n readonly context: C;\n} & Record<string, (...args: any[]) => MaybePromise<AsyncMachine<any>>>;\n\n\n// =============================================================================\n// SECTION: TYPE UTILITIES & INTROSPECTION\n// =============================================================================\n\n/**\n * Extracts the context type `C` from a machine type `M`.\n * @template M - The machine type.\n * @example type Ctx = Context<Machine<{ count: number }>> // { count: number }\n */\nexport type Context<M extends { context: any }> = M[\"context\"];\n\n/**\n * Extracts the transition function signatures from a machine, excluding the context property.\n * @template M - The machine type.\n */\nexport type Transitions<M extends BaseMachine<any>> = Omit<M, \"context\">;\n\n/**\n * Extracts the argument types for a specific transition function in a Machine.\n * @template M - The machine type.\n * @template K - The transition function name.\n */\nexport type TransitionArgs<M extends Machine<any>, K extends keyof M & string> =\n M[K] extends (...args: infer A) => any ? A : never;\n\n/**\n * Extracts the names of all transitions as a string union type.\n * @template M - The machine type.\n * @example\n * type Names = TransitionNames<Machine<{ count: number }> & { increment: () => any }>\n * // Names = \"increment\"\n */\nexport type TransitionNames<M extends BaseMachine<any>> = keyof Omit<M, \"context\"> & string;\n\n/**\n * Base machine type that both Machine and AsyncMachine extend from.\n * @template C - The context object type.\n */\nexport type BaseMachine<C extends object> = {\n /** The readonly state of the machine. */\n readonly context: C;\n} & Record<string, (...args: any[]) => any>;\n\n/**\n * Helper to make a type deeply readonly (freezes nested objects).\n * Useful for ensuring immutability of context at the type level.\n * @template T - The type to make readonly.\n */\nexport type DeepReadonly<T> = {\n readonly [P in keyof T]: T[P] extends object\n ? T[P] extends (...args: any[]) => any\n ? T[P]\n : DeepReadonly<T[P]>\n : T[P];\n};\n\n/**\n * Infers the machine type from a machine factory function.\n * @template F - The factory function type.\n * @example\n * const factory = () => createMachine({ count: 0 }, { ... });\n * type MyMachine = InferMachine<typeof factory>; // Extracts the return type\n */\nexport type InferMachine<F extends (...args: any[]) => any> = ReturnType<F>;\n\n/**\n * A discriminated union type representing an event that can be dispatched to a machine.\n * This is automatically generated from a machine's type signature, ensuring full type safety.\n * @template M - The machine type.\n * @example\n * type CounterEvent = Event<Machine<{ count: number }>& { add: (n: number) => any }>\n * // CounterEvent = { type: \"add\"; args: [number] }\n */\nexport type Event<M extends BaseMachine<any>> = {\n [K in keyof Omit<M, \"context\"> & string]: M[K] extends (...args: infer A) => any\n ? { type: K; args: A }\n : never\n}[keyof Omit<M, \"context\"> & string];\n\n\n// =============================================================================\n// SECTION: MACHINE CREATION (FUNCTIONAL & OOP)\n// =============================================================================\n\n/**\n * Creates a synchronous state machine from a context and transition functions.\n * This is the core factory for the functional approach.\n *\n * @template C - The context object type.\n * @param context - The initial state context.\n * @param fns - An object containing transition function definitions.\n * @returns A new machine instance.\n */\nexport function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(\n context: C,\n fns: T\n): { context: C } & T {\n return Object.assign({ context }, fns);\n}\n\n/**\n * Creates an asynchronous state machine from a context and async transition functions.\n *\n * @template C - The context object type.\n * @param context - The initial state context.\n * @param fns - An object containing async transition function definitions.\n * @returns A new async machine instance.\n */\nexport function createAsyncMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(\n context: C,\n fns: T\n): { context: C } & T {\n return Object.assign({ context }, fns);\n}\n\n/**\n * Creates a machine factory - a higher-order function that simplifies machine creation.\n * Instead of writing transition logic that creates new machines, you just write\n * pure context transformation functions.\n *\n * @template C - The context object type.\n * @returns A factory configurator function.\n *\n * @example\n * const counterFactory = createMachineFactory<{ count: number }>()({\n * increment: (ctx) => ({ count: ctx.count + 1 }),\n * add: (ctx, n: number) => ({ count: ctx.count + n })\n * });\n *\n * const counter = counterFactory({ count: 0 });\n * const next = counter.increment(); // Returns new machine with count: 1\n */\nexport function createMachineFactory<C extends object>() {\n return <T extends Record<string, (ctx: C, ...args: any[]) => C>>(\n transformers: T\n ) => {\n type MachineFns = {\n [K in keyof T]: (\n this: C,\n ...args: T[K] extends (ctx: C, ...args: infer A) => C ? A : never\n ) => Machine<C>;\n };\n\n const fns = Object.fromEntries(\n Object.entries(transformers).map(([key, transform]) => [\n key,\n function (this: C, ...args: any[]) {\n const newContext = (transform as any)(this, ...args);\n return createMachine(newContext, fns as any);\n },\n ])\n ) as MachineFns;\n\n return (initialContext: C): Machine<C> & MachineFns => {\n return createMachine(initialContext, fns);\n };\n };\n}\n\n\n// =============================================================================\n// SECTION: ADVANCED CREATION & IMMUTABLE HELPERS\n// =============================================================================\n\n/**\n * Creates a new machine instance with an updated context, preserving all original transitions.\n * This is the primary, type-safe utility for applying state changes.\n *\n * @template M - The machine type.\n * @param machine - The original machine instance.\n * @param newContextOrFn - The new context object or an updater function.\n * @returns A new machine instance of the same type with the updated context.\n */\nexport function setContext<M extends Machine<any>>(\n machine: M,\n newContextOrFn: Context<M> | ((ctx: Readonly<Context<M>>) => Context<M>)\n): M {\n const { context, ...transitions } = machine;\n const newContext =\n typeof newContextOrFn === \"function\"\n ? (newContextOrFn as (ctx: Readonly<Context<M>>) => Context<M>)(context)\n : newContextOrFn;\n\n return createMachine(newContext, transitions) as M;\n}\n\n/**\n * Creates a new machine by overriding or adding transition functions to an existing machine.\n * Ideal for mocking in tests or decorating functionality. The original machine is unchanged.\n *\n * @template M - The original machine type.\n * @template T - An object of new or overriding transition functions.\n * @param machine - The base machine instance.\n * @param overrides - An object containing the transitions to add or overwrite.\n * @returns A new machine instance with the merged transitions.\n */\nexport function overrideTransitions<\n M extends Machine<any>,\n T extends Record<string, (this: Context<M>, ...args: any[]) => any>\n>(\n machine: M,\n overrides: T\n): Machine<Context<M>> & Omit<Transitions<M>, keyof T> & T {\n const { context, ...originalTransitions } = machine;\n const newTransitions = { ...originalTransitions, ...overrides };\n return createMachine(context, newTransitions) as any;\n}\n\n/**\n * Creates a new machine by adding new transition functions.\n * This utility will produce a compile-time error if you attempt to add a\n * transition that already exists, preventing accidental overrides.\n *\n * @template M - The original machine type.\n * @template T - An object of new transition functions, whose keys must not exist in M.\n * @param machine - The base machine instance.\n * @param newTransitions - An object containing the new transitions to add.\n * @returns A new machine instance with the combined original and new transitions.\n */\nexport function extendTransitions<\n M extends Machine<any>,\n T extends Record<string, (this: Context<M>, ...args: any[]) => any> & {\n [K in keyof T]: K extends keyof M ? never : T[K];\n }\n>(machine: M, newTransitions: T): M & T {\n const { context, ...originalTransitions } = machine;\n const combinedTransitions = { ...originalTransitions, ...newTransitions };\n return createMachine(context, combinedTransitions) as M & T;\n}\n\n/**\n * Creates a builder function from a \"template\" machine instance.\n * This captures the behavior of a machine and returns a factory that can stamp out\n * new instances with different initial contexts. Excellent for class-based machines.\n *\n * @template M - The machine type.\n * @param templateMachine - An instance of a machine to use as the template.\n * @returns A function that builds new machines of type M.\n */\nexport function createMachineBuilder<M extends Machine<any>>(\n templateMachine: M\n): (context: Context<M>) => M {\n const { context, ...transitions } = templateMachine;\n return (newContext: Context<M>): M => {\n return createMachine(newContext, transitions) as M;\n };\n}\n\n/**\n * Pattern match on a machine's state based on a discriminant property in the context.\n * This provides type-safe exhaustive matching for state machines.\n *\n * @template M - The machine type.\n * @template K - The discriminant key in the context.\n * @template R - The return type.\n * @param machine - The machine to match against.\n * @param discriminantKey - The key in the context to use for matching (e.g., \"status\").\n * @param handlers - An object mapping each possible value to a handler function.\n * @returns The result of the matched handler.\n *\n * @example\n * const result = matchMachine(\n * machine,\n * 'status',\n * {\n * idle: (ctx) => \"Machine is idle\",\n * loading: (ctx) => \"Loading...\",\n * success: (ctx) => `Success: ${ctx.data}`,\n * error: (ctx) => `Error: ${ctx.error}`\n * }\n * );\n */\nexport function matchMachine<\n M extends Machine<any>,\n K extends keyof Context<M> & string,\n R\n>(\n machine: M,\n discriminantKey: K,\n handlers: {\n [V in Context<M>[K] & string]: (ctx: Context<M>) => R;\n }\n): R {\n const discriminant = machine.context[discriminantKey] as Context<M>[K] & string;\n const handler = handlers[discriminant];\n if (!handler) {\n throw new Error(`No handler found for state: ${String(discriminant)}`);\n }\n return handler(machine.context);\n}\n\n/**\n * Type-safe helper to assert that a machine's context has a specific discriminant value.\n * This narrows the type of the context based on the discriminant.\n *\n * @template M - The machine type.\n * @template K - The discriminant key.\n * @template V - The discriminant value.\n * @param machine - The machine to check.\n * @param key - The discriminant key to check.\n * @param value - The expected value.\n * @returns True if the discriminant matches, with type narrowing.\n *\n * @example\n * if (hasState(machine, 'status', 'loading')) {\n * // machine.context.status is narrowed to 'loading'\n * }\n */\nexport function hasState<\n M extends Machine<any>,\n K extends keyof Context<M>,\n V extends Context<M>[K]\n>(\n machine: M,\n key: K,\n value: V\n): machine is M & { context: Context<M> & { [P in K]: V } } {\n return machine.context[key] === value;\n}\n\n\n// =============================================================================\n// SECTION: RUNTIME & EVENT DISPATCHER\n// =============================================================================\n\n/**\n * Runs an asynchronous state machine with a managed lifecycle and event dispatch capability.\n * This is the \"interpreter\" for async machines, handling state updates and side effects.\n *\n * @template M - The initial machine type.\n * @param initial - The initial machine state.\n * @param onChange - Optional callback invoked with the new machine state after every transition.\n * @returns An object with a `state` getter for the current context and an async `dispatch` function.\n */\nexport function runMachine<M extends AsyncMachine<any>>(\n initial: M,\n onChange?: (m: M) => void\n) {\n let current = initial;\n\n async function dispatch<E extends Event<typeof current>>(event: E): Promise<M> {\n const fn = (current as any)[event.type];\n if (typeof fn !== 'function') {\n throw new Error(`[Machine] Unknown event type '${String(event.type)}' on current state.`);\n }\n const nextState = await fn.apply(current.context, event.args);\n current = nextState;\n onChange?.(current);\n return current;\n }\n\n return {\n /** Gets the context of the current state of the machine. */\n get state(): Context<M> {\n return current.context;\n },\n /** Dispatches a type-safe event to the machine, triggering a transition. */\n dispatch,\n };\n}\n\n/**\n * An optional base class for creating machines using an Object-Oriented style.\n *\n * This class provides the fundamental structure required by the library: a `context`\n * property to hold the state. By extending `MachineBase`, you get a clear and\n * type-safe starting point for defining states and transitions as classes and methods.\n *\n * Transitions should be implemented as methods that return a new instance of a\n * state machine class (often `new MyClass(...)` or by using a `createMachineBuilder`).\n * The `context` is marked `readonly` to enforce the immutable update pattern.\n *\n * @template C - The context object type that defines the state for this machine.\n *\n * @example\n * // Define a simple counter state\n * class Counter extends MachineBase<{ readonly count: number }> {\n * constructor(count = 0) {\n * super({ count });\n * }\n *\n * increment(): Counter {\n * // Return a new instance for the next state\n * return new Counter(this.context.count + 1);\n * }\n *\n * add(n: number): Counter {\n * return new Counter(this.context.count + n);\n * }\n * }\n *\n * const machine = new Counter(5);\n * const nextState = machine.increment(); // Returns a new Counter instance\n *\n * console.log(machine.context.count); // 5 (original is unchanged)\n * console.log(nextState.context.count); // 6 (new state)\n */\nexport class MachineBase<C extends object> {\n /**\n * The immutable state of the machine.\n * To change the state, a transition method must return a new machine instance\n * with a new context object.\n */\n public readonly context: C;\n\n /**\n * Initializes a new machine instance with its starting context.\n * @param context - The initial state of the machine.\n */\n constructor(context: C) {\n this.context = context;\n // Object.freeze can provide additional runtime safety against accidental mutation,\n // though it comes with a minor performance cost. It's a good practice for ensuring purity.\n // Object.freeze(this.context);\n }\n}\n\n\n/**\n * Applies an update function to a machine's context, returning a new machine.\n * This is a simpler alternative to `setContext` when you always use an updater function.\n *\n * @template C - The context object type.\n * @param m - The machine to update.\n * @param update - A function that takes the current context and returns the new context.\n * @returns A new machine with the updated context.\n *\n * @example\n * const updated = next(counter, (ctx) => ({ count: ctx.count + 1 }));\n */\nexport function next<C extends object>(\n m: Machine<C>,\n update: (ctx: Readonly<C>) => C\n): Machine<C> {\n const { context, ...transitions } = m;\n return createMachine(update(context), transitions) as Machine<C>;\n}\n\n/**\n * A type representing either a synchronous Machine or a Promise that resolves to a Machine.\n * Useful for functions that can return either sync or async machines.\n *\n * @template C - The context object type.\n *\n * @example\n * function getMachine(): MachineLike<{ count: number }> {\n * if (Math.random() > 0.5) {\n * return createMachine({ count: 0 }, { ... });\n * } else {\n * return Promise.resolve(createMachine({ count: 0 }, { ... }));\n * }\n * }\n */\nexport type MachineLike<C extends object> =\n | Machine<C>\n | Promise<Machine<C>>;\n\n/**\n * A type representing the result of a machine transition.\n * Can be either:\n * - A new machine state\n * - A tuple of [machine, cleanup function] where cleanup is called when leaving the state\n *\n * This enables state machines with side effects that need cleanup (e.g., subscriptions, timers).\n *\n * @template C - The context object type.\n *\n * @example\n * function transition(): MachineResult<{ count: number }> {\n * const interval = setInterval(() => console.log(\"tick\"), 1000);\n * const machine = createMachine({ count: 0 }, { ... });\n * return [machine, () => clearInterval(interval)];\n * }\n */\nexport type MachineResult<C extends object> =\n | Machine<C>\n | [Machine<C>, () => void | Promise<void>];\n\n\n// =============================================================================\n// SECTION: GENERATOR-BASED COMPOSITION\n// =============================================================================\n\nexport {\n run,\n step,\n yieldMachine,\n runSequence,\n createFlow,\n runWithDebug,\n runAsync,\n stepAsync\n} from './generators';\n", "/**\n * @file Generator-based state machine composition utilities.\n * @description\n * This module provides a generator-based approach to composing state machine transitions.\n * Instead of chaining method calls or using composition functions, you can write\n * imperative-style code using generators that feels like sequential, synchronous code\n * while maintaining the immutability and type safety of the state machine model.\n *\n * This pattern is particularly useful for:\n * - Multi-step workflows where each step depends on the previous\n * - Complex transition logic that would be unwieldy with chaining\n * - When you want imperative control flow (if/else, loops) with immutable state\n * - Testing scenarios where you want to control the flow step-by-step\n *\n * @example\n * ```typescript\n * const result = run(function* (machine) {\n * // Each yield passes control back and receives the next state\n * let m = yield* step(machine.increment());\n * m = yield* step(m.add(5));\n * if (m.context.count > 10) {\n * m = yield* step(m.reset());\n * }\n * return m.context.count;\n * }, initialMachine);\n * ```\n */\n\nimport { Machine } from './index';\n\n/**\n * Runs a generator-based state machine flow to completion.\n *\n * This function executes a generator that yields machine states and returns a final value.\n * Each yield passes the current machine state back to the generator, allowing you to\n * write imperative-style code while maintaining immutability.\n *\n * **How it works:**\n * 1. The generator function receives the initial machine\n * 2. Each `yield` expression produces a new machine state\n * 3. That state is sent back into the generator via `next()`\n * 4. The generator can use the received state for the next operation\n * 5. When the generator returns, that value is returned from `run()`\n *\n * **Key insight:** The generator doesn't mutate state—it yields new immutable states\n * at each step, creating a clear audit trail of state transitions.\n *\n * @template C - The context object type for the machine.\n * @template T - The return type of the generator (can be any type).\n *\n * @param flow - A generator function that receives a machine and yields machines,\n * eventually returning a value of type T.\n * @param initial - The initial machine state to start the flow.\n *\n * @returns The final value returned by the generator.\n *\n * @example Basic usage with counter\n * ```typescript\n * const counter = createMachine({ count: 0 }, {\n * increment: function() {\n * return createMachine({ count: this.count + 1 }, this);\n * },\n * add: function(n: number) {\n * return createMachine({ count: this.count + n }, this);\n * }\n * });\n *\n * const finalCount = run(function* (m) {\n * m = yield* step(m.increment()); // count: 1\n * m = yield* step(m.add(5)); // count: 6\n * m = yield* step(m.increment()); // count: 7\n * return m.context.count;\n * }, counter);\n *\n * console.log(finalCount); // 7\n * ```\n *\n * @example Conditional logic\n * ```typescript\n * const result = run(function* (m) {\n * m = yield* step(m.increment());\n *\n * if (m.context.count > 5) {\n * m = yield* step(m.reset());\n * } else {\n * m = yield* step(m.add(10));\n * }\n *\n * return m;\n * }, counter);\n * ```\n *\n * @example Loops and accumulation\n * ```typescript\n * const sum = run(function* (m) {\n * let total = 0;\n *\n * for (let i = 0; i < 5; i++) {\n * m = yield* step(m.increment());\n * total += m.context.count;\n * }\n *\n * return total;\n * }, counter);\n * ```\n *\n * @example Error handling\n * ```typescript\n * const result = run(function* (m) {\n * try {\n * m = yield* step(m.riskyOperation());\n * m = yield* step(m.processResult());\n * } catch (error) {\n * m = yield* step(m.handleError(error));\n * }\n * return m;\n * }, machine);\n * ```\n */\nexport function run<C extends object, T>(\n flow: (m: Machine<C>) => Generator<Machine<C>, T, Machine<C>>,\n initial: Machine<C>\n): T {\n // Create the generator by calling the flow function with the initial machine\n const generator = flow(initial);\n\n // Track the current machine state as we iterate\n let current = initial;\n\n // Iterate the generator until completion\n while (true) {\n // Send the current machine state into the generator and get the next yielded value\n // The generator receives `current` as the result of its last yield expression\n const { value, done } = generator.next(current);\n\n // If the generator has returned (done), we have our final value\n if (done) {\n return value;\n }\n\n // Otherwise, the yielded value becomes our new current state\n // This state will be sent back into the generator on the next iteration\n current = value;\n }\n}\n\n/**\n * A helper function to yield a machine state and receive the next state back.\n *\n * This function creates a mini-generator that yields the provided machine and\n * returns whatever value the outer runner sends back. It's designed to be used\n * with `yield*` (yield delegation) inside your main generator.\n *\n * **Why use this helper?**\n * - Makes the intent clear: \"step to this state\"\n * - Provides a consistent API for state transitions\n * - Enables type inference for the received state\n * - Works seamlessly with the `run()` function\n *\n * **What `yield*` does:**\n * `yield*` delegates to another generator. When you write `yield* step(m)`,\n * control passes to the `step` generator, which yields `m`, then returns the\n * value sent back by the runner.\n *\n * @template C - The context object type for the machine.\n *\n * @param m - The machine state to yield.\n *\n * @returns A generator that yields the machine and returns the received state.\n *\n * @example Basic stepping\n * ```typescript\n * run(function* (machine) {\n * // Yield this state and receive the next one\n * const next = yield* step(machine.increment());\n * console.log(next.context.count);\n * return next;\n * }, counter);\n * ```\n *\n * @example Without step (more verbose)\n * ```typescript\n * run(function* (machine) {\n * // This is what step() does internally\n * const next = yield machine.increment();\n * return next;\n * }, counter);\n * ```\n *\n * @example Chaining with step\n * ```typescript\n * run(function* (m) {\n * m = yield* step(m.action1());\n * m = yield* step(m.action2());\n * m = yield* step(m.action3());\n * return m;\n * }, machine);\n * ```\n */\nexport function step<C extends object>(\n m: Machine<C>\n): Generator<Machine<C>, Machine<C>, Machine<C>> {\n // Create an immediately-invoked generator that:\n // 1. Yields the provided machine\n // 2. Receives a value back (the next state)\n // 3. Returns that received value\n return (function* () {\n const received = yield m;\n return received;\n })();\n}\n\n/**\n * Alternative to `step` that doesn't require `yield*`.\n * This is semantically identical but uses direct yielding.\n *\n * Use this if you prefer the simpler syntax without delegation.\n *\n * @template C - The context object type.\n * @param m - The machine to yield.\n * @returns The same machine (passed through).\n *\n * @example\n * ```typescript\n * run(function* (m) {\n * m = yield m.increment(); // No yield* needed\n * m = yield m.add(5);\n * return m;\n * }, counter);\n * ```\n */\nexport function yieldMachine<C extends object>(m: Machine<C>): Machine<C> {\n return m;\n}\n\n/**\n * Runs multiple generator flows in sequence, passing the result of each to the next.\n *\n * This is useful for composing multiple generator-based workflows into a pipeline.\n *\n * @template C - The context object type.\n * @param initial - The initial machine state.\n * @param flows - An array of generator functions to run in sequence.\n * @returns The final machine state after all flows complete.\n *\n * @example\n * ```typescript\n * const flow1 = function* (m: Machine<{ count: number }>) {\n * m = yield* step(m.increment());\n * return m;\n * };\n *\n * const flow2 = function* (m: Machine<{ count: number }>) {\n * m = yield* step(m.add(5));\n * return m;\n * };\n *\n * const result = runSequence(counter, [flow1, flow2]);\n * console.log(result.context.count); // 6\n * ```\n */\nexport function runSequence<C extends object>(\n initial: Machine<C>,\n flows: Array<(m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>>>\n): Machine<C> {\n return flows.reduce((machine, flow) => {\n return run(flow, machine);\n }, initial);\n}\n\n/**\n * Creates a reusable generator flow that can be composed into other flows.\n *\n * This allows you to define common state machine patterns as reusable building blocks.\n *\n * @template C - The context object type.\n * @param flow - A generator function representing a reusable flow.\n * @returns A function that can be used with `yield*` in other generators.\n *\n * @example\n * ```typescript\n * // Define a reusable flow\n * const incrementThrice = createFlow(function* (m: Machine<{ count: number }>) {\n * m = yield* step(m.increment());\n * m = yield* step(m.increment());\n * m = yield* step(m.increment());\n * return m;\n * });\n *\n * // Use it in another flow\n * const result = run(function* (m) {\n * m = yield* incrementThrice(m);\n * m = yield* step(m.add(10));\n * return m;\n * }, counter);\n * ```\n */\nexport function createFlow<C extends object>(\n flow: (m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>>\n): (m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>> {\n return flow;\n}\n\n/**\n * Runs a generator flow with debugging output at each step.\n *\n * This is useful for understanding the state transitions in your flow.\n *\n * @template C - The context object type.\n * @template T - The return type.\n * @param flow - The generator function to run.\n * @param initial - The initial machine state.\n * @param logger - Optional custom logger function.\n * @returns The final value from the generator.\n *\n * @example\n * ```typescript\n * const result = runWithDebug(function* (m) {\n * m = yield* step(m.increment());\n * m = yield* step(m.add(5));\n * return m.context.count;\n * }, counter);\n *\n * // Output:\n * // Step 0: { count: 0 }\n * // Step 1: { count: 1 }\n * // Step 2: { count: 6 }\n * // Final: 6\n * ```\n */\nexport function runWithDebug<C extends object, T>(\n flow: (m: Machine<C>) => Generator<Machine<C>, T, Machine<C>>,\n initial: Machine<C>,\n logger: (step: number, machine: Machine<C>) => void = (step, m) => {\n console.log(`Step ${step}:`, m.context);\n }\n): T {\n const generator = flow(initial);\n let current = initial;\n let stepCount = 0;\n\n logger(stepCount, current);\n\n while (true) {\n const { value, done } = generator.next(current);\n\n if (done) {\n console.log('Final:', value);\n return value;\n }\n\n current = value;\n stepCount++;\n logger(stepCount, current);\n }\n}\n\n// =============================================================================\n// ASYNC GENERATOR SUPPORT\n// =============================================================================\n\n/**\n * Async version of `run` for async state machines.\n *\n * This allows you to use async/await inside your generator flows while maintaining\n * the same compositional benefits.\n *\n * @template C - The context object type.\n * @template T - The return type.\n * @param flow - An async generator function.\n * @param initial - The initial machine state.\n * @returns A promise that resolves to the final value.\n *\n * @example\n * ```typescript\n * const result = await runAsync(async function* (m) {\n * m = yield* stepAsync(await m.fetchData());\n * m = yield* stepAsync(await m.processData());\n * return m.context;\n * }, asyncMachine);\n * ```\n */\nexport async function runAsync<C extends object, T>(\n flow: (m: Machine<C>) => AsyncGenerator<Machine<C>, T, Machine<C>>,\n initial: Machine<C>\n): Promise<T> {\n const generator = flow(initial);\n let current = initial;\n\n while (true) {\n const { value, done } = await generator.next(current);\n\n if (done) {\n return value;\n }\n\n current = value;\n }\n}\n\n/**\n * Async version of `step` for async generators.\n *\n * @template C - The context object type.\n * @param m - The machine to yield.\n * @returns An async generator.\n *\n * @example\n * ```typescript\n * await runAsync(async function* (m) {\n * m = yield* stepAsync(await m.asyncOperation());\n * return m;\n * }, machine);\n * ```\n */\nexport async function* stepAsync<C extends object>(\n m: Machine<C>\n): AsyncGenerator<Machine<C>, Machine<C>, Machine<C>> {\n const received = yield m;\n return received;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["step"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var s=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var l=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var m=(n,e)=>{for(var t in e)s(n,t,{get:e[t],enumerable:!0})},j=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of l(e))!b.call(n,r)&&r!==t&&s(n,r,{get:()=>e[r],enumerable:!(o=g(e,r))||o.enumerable});return n};var A=n=>j(s({},"__esModule",{value:!0}),n);var O={};m(O,{MachineBase:()=>M,createAsyncMachine:()=>K,createFlow:()=>d,createMachine:()=>a,createMachineBuilder:()=>w,createMachineFactory:()=>R,extendTransitions:()=>k,hasState:()=>S,matchMachine:()=>G,next:()=>F,overrideTransitions:()=>P,run:()=>x,runAsync:()=>p,runMachine:()=>E,runSequence:()=>h,runWithDebug:()=>u,setContext:()=>v,step:()=>y,stepAsync:()=>f,yieldMachine:()=>C});module.exports=A(O);function x(n,e){let t=n(e),o=e;for(;;){let{value:r,done:c}=t.next(o);if(c)return r;o=r}}function y(n){return function*(){return yield n}()}function C(n){return n}function h(n,e){return e.reduce((t,o)=>x(o,t),n)}function d(n){return n}function u(n,e,t=(o,r)=>{console.log(`Step ${o}:`,r.context)}){let o=n(e),r=e,c=0;for(t(c,r);;){let{value:i,done:T}=o.next(r);if(T)return console.log("Final:",i),i;r=i,c++,t(c,r)}}async function p(n,e){let t=n(e),o=e;for(;;){let{value:r,done:c}=await t.next(o);if(c)return r;o=r}}async function*f(n){return yield n}function a(n,e){return Object.assign({context:n},e)}function K(n,e){return Object.assign({context:n},e)}function R(){return n=>{let e=Object.fromEntries(Object.entries(n).map(([t,o])=>[t,function(...r){let c=o(this,...r);return a(c,e)}]));return t=>a(t,e)}}function v(n,e){let{context:t,...o}=n,r=typeof e=="function"?e(t):e;return a(r,o)}function P(n,e){let{context:t,...o}=n,r={...o,...e};return a(t,r)}function k(n,e){let{context:t,...o}=n,r={...o,...e};return a(t,r)}function w(n){let{context:e,...t}=n;return o=>a(o,t)}function G(n,e,t){let o=n.context[e],r=t[o];if(!r)throw new Error(`No handler found for state: ${String(o)}`);return r(n.context)}function S(n,e,t){return n.context[e]===t}function E(n,e){let t=n;async function o(r){let c=t[r.type];if(typeof c!="function")throw new Error(`[Machine] Unknown event type '${String(r.type)}' on current state.`);return t=await c.apply(t.context,r.args),e==null||e(t),t}return{get state(){return t.context},dispatch:o}}var M=class{constructor(e){this.context=e}};function F(n,e){let{context:t,...o}=n;return a(e(t),o)}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// src/generators.ts
|
|
2
|
+
function run(flow, initial) {
|
|
3
|
+
const generator = flow(initial);
|
|
4
|
+
let current = initial;
|
|
5
|
+
while (true) {
|
|
6
|
+
const { value, done } = generator.next(current);
|
|
7
|
+
if (done) {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
current = value;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function step(m) {
|
|
14
|
+
return function* () {
|
|
15
|
+
const received = yield m;
|
|
16
|
+
return received;
|
|
17
|
+
}();
|
|
18
|
+
}
|
|
19
|
+
function yieldMachine(m) {
|
|
20
|
+
return m;
|
|
21
|
+
}
|
|
22
|
+
function runSequence(initial, flows) {
|
|
23
|
+
return flows.reduce((machine, flow) => {
|
|
24
|
+
return run(flow, machine);
|
|
25
|
+
}, initial);
|
|
26
|
+
}
|
|
27
|
+
function createFlow(flow) {
|
|
28
|
+
return flow;
|
|
29
|
+
}
|
|
30
|
+
function runWithDebug(flow, initial, logger = (step2, m) => {
|
|
31
|
+
console.log(`Step ${step2}:`, m.context);
|
|
32
|
+
}) {
|
|
33
|
+
const generator = flow(initial);
|
|
34
|
+
let current = initial;
|
|
35
|
+
let stepCount = 0;
|
|
36
|
+
logger(stepCount, current);
|
|
37
|
+
while (true) {
|
|
38
|
+
const { value, done } = generator.next(current);
|
|
39
|
+
if (done) {
|
|
40
|
+
console.log("Final:", value);
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
current = value;
|
|
44
|
+
stepCount++;
|
|
45
|
+
logger(stepCount, current);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function runAsync(flow, initial) {
|
|
49
|
+
const generator = flow(initial);
|
|
50
|
+
let current = initial;
|
|
51
|
+
while (true) {
|
|
52
|
+
const { value, done } = await generator.next(current);
|
|
53
|
+
if (done) {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
current = value;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function* stepAsync(m) {
|
|
60
|
+
const received = yield m;
|
|
61
|
+
return received;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/index.ts
|
|
65
|
+
function createMachine(context, fns) {
|
|
66
|
+
return Object.assign({ context }, fns);
|
|
67
|
+
}
|
|
68
|
+
function createAsyncMachine(context, fns) {
|
|
69
|
+
return Object.assign({ context }, fns);
|
|
70
|
+
}
|
|
71
|
+
function createMachineFactory() {
|
|
72
|
+
return (transformers) => {
|
|
73
|
+
const fns = Object.fromEntries(
|
|
74
|
+
Object.entries(transformers).map(([key, transform]) => [
|
|
75
|
+
key,
|
|
76
|
+
function(...args) {
|
|
77
|
+
const newContext = transform(this, ...args);
|
|
78
|
+
return createMachine(newContext, fns);
|
|
79
|
+
}
|
|
80
|
+
])
|
|
81
|
+
);
|
|
82
|
+
return (initialContext) => {
|
|
83
|
+
return createMachine(initialContext, fns);
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function setContext(machine, newContextOrFn) {
|
|
88
|
+
const { context, ...transitions } = machine;
|
|
89
|
+
const newContext = typeof newContextOrFn === "function" ? newContextOrFn(context) : newContextOrFn;
|
|
90
|
+
return createMachine(newContext, transitions);
|
|
91
|
+
}
|
|
92
|
+
function overrideTransitions(machine, overrides) {
|
|
93
|
+
const { context, ...originalTransitions } = machine;
|
|
94
|
+
const newTransitions = { ...originalTransitions, ...overrides };
|
|
95
|
+
return createMachine(context, newTransitions);
|
|
96
|
+
}
|
|
97
|
+
function extendTransitions(machine, newTransitions) {
|
|
98
|
+
const { context, ...originalTransitions } = machine;
|
|
99
|
+
const combinedTransitions = { ...originalTransitions, ...newTransitions };
|
|
100
|
+
return createMachine(context, combinedTransitions);
|
|
101
|
+
}
|
|
102
|
+
function createMachineBuilder(templateMachine) {
|
|
103
|
+
const { context, ...transitions } = templateMachine;
|
|
104
|
+
return (newContext) => {
|
|
105
|
+
return createMachine(newContext, transitions);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function matchMachine(machine, discriminantKey, handlers) {
|
|
109
|
+
const discriminant = machine.context[discriminantKey];
|
|
110
|
+
const handler = handlers[discriminant];
|
|
111
|
+
if (!handler) {
|
|
112
|
+
throw new Error(`No handler found for state: ${String(discriminant)}`);
|
|
113
|
+
}
|
|
114
|
+
return handler(machine.context);
|
|
115
|
+
}
|
|
116
|
+
function hasState(machine, key, value) {
|
|
117
|
+
return machine.context[key] === value;
|
|
118
|
+
}
|
|
119
|
+
function runMachine(initial, onChange) {
|
|
120
|
+
let current = initial;
|
|
121
|
+
async function dispatch(event) {
|
|
122
|
+
const fn = current[event.type];
|
|
123
|
+
if (typeof fn !== "function") {
|
|
124
|
+
throw new Error(`[Machine] Unknown event type '${String(event.type)}' on current state.`);
|
|
125
|
+
}
|
|
126
|
+
const nextState = await fn.apply(current.context, event.args);
|
|
127
|
+
current = nextState;
|
|
128
|
+
onChange == null ? void 0 : onChange(current);
|
|
129
|
+
return current;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
/** Gets the context of the current state of the machine. */
|
|
133
|
+
get state() {
|
|
134
|
+
return current.context;
|
|
135
|
+
},
|
|
136
|
+
/** Dispatches a type-safe event to the machine, triggering a transition. */
|
|
137
|
+
dispatch
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
var MachineBase = class {
|
|
141
|
+
/**
|
|
142
|
+
* Initializes a new machine instance with its starting context.
|
|
143
|
+
* @param context - The initial state of the machine.
|
|
144
|
+
*/
|
|
145
|
+
constructor(context) {
|
|
146
|
+
this.context = context;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
function next(m, update) {
|
|
150
|
+
const { context, ...transitions } = m;
|
|
151
|
+
return createMachine(update(context), transitions);
|
|
152
|
+
}
|
|
153
|
+
export {
|
|
154
|
+
MachineBase,
|
|
155
|
+
createAsyncMachine,
|
|
156
|
+
createFlow,
|
|
157
|
+
createMachine,
|
|
158
|
+
createMachineBuilder,
|
|
159
|
+
createMachineFactory,
|
|
160
|
+
extendTransitions,
|
|
161
|
+
hasState,
|
|
162
|
+
matchMachine,
|
|
163
|
+
next,
|
|
164
|
+
overrideTransitions,
|
|
165
|
+
run,
|
|
166
|
+
runAsync,
|
|
167
|
+
runMachine,
|
|
168
|
+
runSequence,
|
|
169
|
+
runWithDebug,
|
|
170
|
+
setContext,
|
|
171
|
+
step,
|
|
172
|
+
stepAsync,
|
|
173
|
+
yieldMachine
|
|
174
|
+
};
|
|
175
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/generators.ts", "../../../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * @file Generator-based state machine composition utilities.\n * @description\n * This module provides a generator-based approach to composing state machine transitions.\n * Instead of chaining method calls or using composition functions, you can write\n * imperative-style code using generators that feels like sequential, synchronous code\n * while maintaining the immutability and type safety of the state machine model.\n *\n * This pattern is particularly useful for:\n * - Multi-step workflows where each step depends on the previous\n * - Complex transition logic that would be unwieldy with chaining\n * - When you want imperative control flow (if/else, loops) with immutable state\n * - Testing scenarios where you want to control the flow step-by-step\n *\n * @example\n * ```typescript\n * const result = run(function* (machine) {\n * // Each yield passes control back and receives the next state\n * let m = yield* step(machine.increment());\n * m = yield* step(m.add(5));\n * if (m.context.count > 10) {\n * m = yield* step(m.reset());\n * }\n * return m.context.count;\n * }, initialMachine);\n * ```\n */\n\nimport { Machine } from './index';\n\n/**\n * Runs a generator-based state machine flow to completion.\n *\n * This function executes a generator that yields machine states and returns a final value.\n * Each yield passes the current machine state back to the generator, allowing you to\n * write imperative-style code while maintaining immutability.\n *\n * **How it works:**\n * 1. The generator function receives the initial machine\n * 2. Each `yield` expression produces a new machine state\n * 3. That state is sent back into the generator via `next()`\n * 4. The generator can use the received state for the next operation\n * 5. When the generator returns, that value is returned from `run()`\n *\n * **Key insight:** The generator doesn't mutate state—it yields new immutable states\n * at each step, creating a clear audit trail of state transitions.\n *\n * @template C - The context object type for the machine.\n * @template T - The return type of the generator (can be any type).\n *\n * @param flow - A generator function that receives a machine and yields machines,\n * eventually returning a value of type T.\n * @param initial - The initial machine state to start the flow.\n *\n * @returns The final value returned by the generator.\n *\n * @example Basic usage with counter\n * ```typescript\n * const counter = createMachine({ count: 0 }, {\n * increment: function() {\n * return createMachine({ count: this.count + 1 }, this);\n * },\n * add: function(n: number) {\n * return createMachine({ count: this.count + n }, this);\n * }\n * });\n *\n * const finalCount = run(function* (m) {\n * m = yield* step(m.increment()); // count: 1\n * m = yield* step(m.add(5)); // count: 6\n * m = yield* step(m.increment()); // count: 7\n * return m.context.count;\n * }, counter);\n *\n * console.log(finalCount); // 7\n * ```\n *\n * @example Conditional logic\n * ```typescript\n * const result = run(function* (m) {\n * m = yield* step(m.increment());\n *\n * if (m.context.count > 5) {\n * m = yield* step(m.reset());\n * } else {\n * m = yield* step(m.add(10));\n * }\n *\n * return m;\n * }, counter);\n * ```\n *\n * @example Loops and accumulation\n * ```typescript\n * const sum = run(function* (m) {\n * let total = 0;\n *\n * for (let i = 0; i < 5; i++) {\n * m = yield* step(m.increment());\n * total += m.context.count;\n * }\n *\n * return total;\n * }, counter);\n * ```\n *\n * @example Error handling\n * ```typescript\n * const result = run(function* (m) {\n * try {\n * m = yield* step(m.riskyOperation());\n * m = yield* step(m.processResult());\n * } catch (error) {\n * m = yield* step(m.handleError(error));\n * }\n * return m;\n * }, machine);\n * ```\n */\nexport function run<C extends object, T>(\n flow: (m: Machine<C>) => Generator<Machine<C>, T, Machine<C>>,\n initial: Machine<C>\n): T {\n // Create the generator by calling the flow function with the initial machine\n const generator = flow(initial);\n\n // Track the current machine state as we iterate\n let current = initial;\n\n // Iterate the generator until completion\n while (true) {\n // Send the current machine state into the generator and get the next yielded value\n // The generator receives `current` as the result of its last yield expression\n const { value, done } = generator.next(current);\n\n // If the generator has returned (done), we have our final value\n if (done) {\n return value;\n }\n\n // Otherwise, the yielded value becomes our new current state\n // This state will be sent back into the generator on the next iteration\n current = value;\n }\n}\n\n/**\n * A helper function to yield a machine state and receive the next state back.\n *\n * This function creates a mini-generator that yields the provided machine and\n * returns whatever value the outer runner sends back. It's designed to be used\n * with `yield*` (yield delegation) inside your main generator.\n *\n * **Why use this helper?**\n * - Makes the intent clear: \"step to this state\"\n * - Provides a consistent API for state transitions\n * - Enables type inference for the received state\n * - Works seamlessly with the `run()` function\n *\n * **What `yield*` does:**\n * `yield*` delegates to another generator. When you write `yield* step(m)`,\n * control passes to the `step` generator, which yields `m`, then returns the\n * value sent back by the runner.\n *\n * @template C - The context object type for the machine.\n *\n * @param m - The machine state to yield.\n *\n * @returns A generator that yields the machine and returns the received state.\n *\n * @example Basic stepping\n * ```typescript\n * run(function* (machine) {\n * // Yield this state and receive the next one\n * const next = yield* step(machine.increment());\n * console.log(next.context.count);\n * return next;\n * }, counter);\n * ```\n *\n * @example Without step (more verbose)\n * ```typescript\n * run(function* (machine) {\n * // This is what step() does internally\n * const next = yield machine.increment();\n * return next;\n * }, counter);\n * ```\n *\n * @example Chaining with step\n * ```typescript\n * run(function* (m) {\n * m = yield* step(m.action1());\n * m = yield* step(m.action2());\n * m = yield* step(m.action3());\n * return m;\n * }, machine);\n * ```\n */\nexport function step<C extends object>(\n m: Machine<C>\n): Generator<Machine<C>, Machine<C>, Machine<C>> {\n // Create an immediately-invoked generator that:\n // 1. Yields the provided machine\n // 2. Receives a value back (the next state)\n // 3. Returns that received value\n return (function* () {\n const received = yield m;\n return received;\n })();\n}\n\n/**\n * Alternative to `step` that doesn't require `yield*`.\n * This is semantically identical but uses direct yielding.\n *\n * Use this if you prefer the simpler syntax without delegation.\n *\n * @template C - The context object type.\n * @param m - The machine to yield.\n * @returns The same machine (passed through).\n *\n * @example\n * ```typescript\n * run(function* (m) {\n * m = yield m.increment(); // No yield* needed\n * m = yield m.add(5);\n * return m;\n * }, counter);\n * ```\n */\nexport function yieldMachine<C extends object>(m: Machine<C>): Machine<C> {\n return m;\n}\n\n/**\n * Runs multiple generator flows in sequence, passing the result of each to the next.\n *\n * This is useful for composing multiple generator-based workflows into a pipeline.\n *\n * @template C - The context object type.\n * @param initial - The initial machine state.\n * @param flows - An array of generator functions to run in sequence.\n * @returns The final machine state after all flows complete.\n *\n * @example\n * ```typescript\n * const flow1 = function* (m: Machine<{ count: number }>) {\n * m = yield* step(m.increment());\n * return m;\n * };\n *\n * const flow2 = function* (m: Machine<{ count: number }>) {\n * m = yield* step(m.add(5));\n * return m;\n * };\n *\n * const result = runSequence(counter, [flow1, flow2]);\n * console.log(result.context.count); // 6\n * ```\n */\nexport function runSequence<C extends object>(\n initial: Machine<C>,\n flows: Array<(m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>>>\n): Machine<C> {\n return flows.reduce((machine, flow) => {\n return run(flow, machine);\n }, initial);\n}\n\n/**\n * Creates a reusable generator flow that can be composed into other flows.\n *\n * This allows you to define common state machine patterns as reusable building blocks.\n *\n * @template C - The context object type.\n * @param flow - A generator function representing a reusable flow.\n * @returns A function that can be used with `yield*` in other generators.\n *\n * @example\n * ```typescript\n * // Define a reusable flow\n * const incrementThrice = createFlow(function* (m: Machine<{ count: number }>) {\n * m = yield* step(m.increment());\n * m = yield* step(m.increment());\n * m = yield* step(m.increment());\n * return m;\n * });\n *\n * // Use it in another flow\n * const result = run(function* (m) {\n * m = yield* incrementThrice(m);\n * m = yield* step(m.add(10));\n * return m;\n * }, counter);\n * ```\n */\nexport function createFlow<C extends object>(\n flow: (m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>>\n): (m: Machine<C>) => Generator<Machine<C>, Machine<C>, Machine<C>> {\n return flow;\n}\n\n/**\n * Runs a generator flow with debugging output at each step.\n *\n * This is useful for understanding the state transitions in your flow.\n *\n * @template C - The context object type.\n * @template T - The return type.\n * @param flow - The generator function to run.\n * @param initial - The initial machine state.\n * @param logger - Optional custom logger function.\n * @returns The final value from the generator.\n *\n * @example\n * ```typescript\n * const result = runWithDebug(function* (m) {\n * m = yield* step(m.increment());\n * m = yield* step(m.add(5));\n * return m.context.count;\n * }, counter);\n *\n * // Output:\n * // Step 0: { count: 0 }\n * // Step 1: { count: 1 }\n * // Step 2: { count: 6 }\n * // Final: 6\n * ```\n */\nexport function runWithDebug<C extends object, T>(\n flow: (m: Machine<C>) => Generator<Machine<C>, T, Machine<C>>,\n initial: Machine<C>,\n logger: (step: number, machine: Machine<C>) => void = (step, m) => {\n console.log(`Step ${step}:`, m.context);\n }\n): T {\n const generator = flow(initial);\n let current = initial;\n let stepCount = 0;\n\n logger(stepCount, current);\n\n while (true) {\n const { value, done } = generator.next(current);\n\n if (done) {\n console.log('Final:', value);\n return value;\n }\n\n current = value;\n stepCount++;\n logger(stepCount, current);\n }\n}\n\n// =============================================================================\n// ASYNC GENERATOR SUPPORT\n// =============================================================================\n\n/**\n * Async version of `run` for async state machines.\n *\n * This allows you to use async/await inside your generator flows while maintaining\n * the same compositional benefits.\n *\n * @template C - The context object type.\n * @template T - The return type.\n * @param flow - An async generator function.\n * @param initial - The initial machine state.\n * @returns A promise that resolves to the final value.\n *\n * @example\n * ```typescript\n * const result = await runAsync(async function* (m) {\n * m = yield* stepAsync(await m.fetchData());\n * m = yield* stepAsync(await m.processData());\n * return m.context;\n * }, asyncMachine);\n * ```\n */\nexport async function runAsync<C extends object, T>(\n flow: (m: Machine<C>) => AsyncGenerator<Machine<C>, T, Machine<C>>,\n initial: Machine<C>\n): Promise<T> {\n const generator = flow(initial);\n let current = initial;\n\n while (true) {\n const { value, done } = await generator.next(current);\n\n if (done) {\n return value;\n }\n\n current = value;\n }\n}\n\n/**\n * Async version of `step` for async generators.\n *\n * @template C - The context object type.\n * @param m - The machine to yield.\n * @returns An async generator.\n *\n * @example\n * ```typescript\n * await runAsync(async function* (m) {\n * m = yield* stepAsync(await m.asyncOperation());\n * return m;\n * }, machine);\n * ```\n */\nexport async function* stepAsync<C extends object>(\n m: Machine<C>\n): AsyncGenerator<Machine<C>, Machine<C>, Machine<C>> {\n const received = yield m;\n return received;\n}\n", "/**\n * @file A tiny, immutable, and type-safe state machine library for TypeScript.\n * @author doeixd\n * @version 1.0.0\n */\n\n// =============================================================================\n// SECTION: CORE TYPES & INTERFACES\n// =============================================================================\n\n/**\n * A utility type that represents either a value of type T or a Promise that resolves to T.\n * @template T - The value type.\n */\nexport type MaybePromise<T> = T | Promise<T>;\n\n/**\n * The fundamental shape of any machine: a `context` object for state, and methods for transitions.\n * @template C - The context (state) object type.\n */\nexport type Machine<C extends object> = {\n /** The readonly state of the machine. */\n readonly context: C;\n} & Record<string, (...args: any[]) => Machine<any>>;\n\n/**\n * The shape of an asynchronous machine, where transitions can return Promises.\n * @template C - The context (state) object type.\n */\nexport type AsyncMachine<C extends object> = {\n /** The readonly state of the machine. */\n readonly context: C;\n} & Record<string, (...args: any[]) => MaybePromise<AsyncMachine<any>>>;\n\n\n// =============================================================================\n// SECTION: TYPE UTILITIES & INTROSPECTION\n// =============================================================================\n\n/**\n * Extracts the context type `C` from a machine type `M`.\n * @template M - The machine type.\n * @example type Ctx = Context<Machine<{ count: number }>> // { count: number }\n */\nexport type Context<M extends { context: any }> = M[\"context\"];\n\n/**\n * Extracts the transition function signatures from a machine, excluding the context property.\n * @template M - The machine type.\n */\nexport type Transitions<M extends BaseMachine<any>> = Omit<M, \"context\">;\n\n/**\n * Extracts the argument types for a specific transition function in a Machine.\n * @template M - The machine type.\n * @template K - The transition function name.\n */\nexport type TransitionArgs<M extends Machine<any>, K extends keyof M & string> =\n M[K] extends (...args: infer A) => any ? A : never;\n\n/**\n * Extracts the names of all transitions as a string union type.\n * @template M - The machine type.\n * @example\n * type Names = TransitionNames<Machine<{ count: number }> & { increment: () => any }>\n * // Names = \"increment\"\n */\nexport type TransitionNames<M extends BaseMachine<any>> = keyof Omit<M, \"context\"> & string;\n\n/**\n * Base machine type that both Machine and AsyncMachine extend from.\n * @template C - The context object type.\n */\nexport type BaseMachine<C extends object> = {\n /** The readonly state of the machine. */\n readonly context: C;\n} & Record<string, (...args: any[]) => any>;\n\n/**\n * Helper to make a type deeply readonly (freezes nested objects).\n * Useful for ensuring immutability of context at the type level.\n * @template T - The type to make readonly.\n */\nexport type DeepReadonly<T> = {\n readonly [P in keyof T]: T[P] extends object\n ? T[P] extends (...args: any[]) => any\n ? T[P]\n : DeepReadonly<T[P]>\n : T[P];\n};\n\n/**\n * Infers the machine type from a machine factory function.\n * @template F - The factory function type.\n * @example\n * const factory = () => createMachine({ count: 0 }, { ... });\n * type MyMachine = InferMachine<typeof factory>; // Extracts the return type\n */\nexport type InferMachine<F extends (...args: any[]) => any> = ReturnType<F>;\n\n/**\n * A discriminated union type representing an event that can be dispatched to a machine.\n * This is automatically generated from a machine's type signature, ensuring full type safety.\n * @template M - The machine type.\n * @example\n * type CounterEvent = Event<Machine<{ count: number }>& { add: (n: number) => any }>\n * // CounterEvent = { type: \"add\"; args: [number] }\n */\nexport type Event<M extends BaseMachine<any>> = {\n [K in keyof Omit<M, \"context\"> & string]: M[K] extends (...args: infer A) => any\n ? { type: K; args: A }\n : never\n}[keyof Omit<M, \"context\"> & string];\n\n\n// =============================================================================\n// SECTION: MACHINE CREATION (FUNCTIONAL & OOP)\n// =============================================================================\n\n/**\n * Creates a synchronous state machine from a context and transition functions.\n * This is the core factory for the functional approach.\n *\n * @template C - The context object type.\n * @param context - The initial state context.\n * @param fns - An object containing transition function definitions.\n * @returns A new machine instance.\n */\nexport function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(\n context: C,\n fns: T\n): { context: C } & T {\n return Object.assign({ context }, fns);\n}\n\n/**\n * Creates an asynchronous state machine from a context and async transition functions.\n *\n * @template C - The context object type.\n * @param context - The initial state context.\n * @param fns - An object containing async transition function definitions.\n * @returns A new async machine instance.\n */\nexport function createAsyncMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(\n context: C,\n fns: T\n): { context: C } & T {\n return Object.assign({ context }, fns);\n}\n\n/**\n * Creates a machine factory - a higher-order function that simplifies machine creation.\n * Instead of writing transition logic that creates new machines, you just write\n * pure context transformation functions.\n *\n * @template C - The context object type.\n * @returns A factory configurator function.\n *\n * @example\n * const counterFactory = createMachineFactory<{ count: number }>()({\n * increment: (ctx) => ({ count: ctx.count + 1 }),\n * add: (ctx, n: number) => ({ count: ctx.count + n })\n * });\n *\n * const counter = counterFactory({ count: 0 });\n * const next = counter.increment(); // Returns new machine with count: 1\n */\nexport function createMachineFactory<C extends object>() {\n return <T extends Record<string, (ctx: C, ...args: any[]) => C>>(\n transformers: T\n ) => {\n type MachineFns = {\n [K in keyof T]: (\n this: C,\n ...args: T[K] extends (ctx: C, ...args: infer A) => C ? A : never\n ) => Machine<C>;\n };\n\n const fns = Object.fromEntries(\n Object.entries(transformers).map(([key, transform]) => [\n key,\n function (this: C, ...args: any[]) {\n const newContext = (transform as any)(this, ...args);\n return createMachine(newContext, fns as any);\n },\n ])\n ) as MachineFns;\n\n return (initialContext: C): Machine<C> & MachineFns => {\n return createMachine(initialContext, fns);\n };\n };\n}\n\n\n// =============================================================================\n// SECTION: ADVANCED CREATION & IMMUTABLE HELPERS\n// =============================================================================\n\n/**\n * Creates a new machine instance with an updated context, preserving all original transitions.\n * This is the primary, type-safe utility for applying state changes.\n *\n * @template M - The machine type.\n * @param machine - The original machine instance.\n * @param newContextOrFn - The new context object or an updater function.\n * @returns A new machine instance of the same type with the updated context.\n */\nexport function setContext<M extends Machine<any>>(\n machine: M,\n newContextOrFn: Context<M> | ((ctx: Readonly<Context<M>>) => Context<M>)\n): M {\n const { context, ...transitions } = machine;\n const newContext =\n typeof newContextOrFn === \"function\"\n ? (newContextOrFn as (ctx: Readonly<Context<M>>) => Context<M>)(context)\n : newContextOrFn;\n\n return createMachine(newContext, transitions) as M;\n}\n\n/**\n * Creates a new machine by overriding or adding transition functions to an existing machine.\n * Ideal for mocking in tests or decorating functionality. The original machine is unchanged.\n *\n * @template M - The original machine type.\n * @template T - An object of new or overriding transition functions.\n * @param machine - The base machine instance.\n * @param overrides - An object containing the transitions to add or overwrite.\n * @returns A new machine instance with the merged transitions.\n */\nexport function overrideTransitions<\n M extends Machine<any>,\n T extends Record<string, (this: Context<M>, ...args: any[]) => any>\n>(\n machine: M,\n overrides: T\n): Machine<Context<M>> & Omit<Transitions<M>, keyof T> & T {\n const { context, ...originalTransitions } = machine;\n const newTransitions = { ...originalTransitions, ...overrides };\n return createMachine(context, newTransitions) as any;\n}\n\n/**\n * Creates a new machine by adding new transition functions.\n * This utility will produce a compile-time error if you attempt to add a\n * transition that already exists, preventing accidental overrides.\n *\n * @template M - The original machine type.\n * @template T - An object of new transition functions, whose keys must not exist in M.\n * @param machine - The base machine instance.\n * @param newTransitions - An object containing the new transitions to add.\n * @returns A new machine instance with the combined original and new transitions.\n */\nexport function extendTransitions<\n M extends Machine<any>,\n T extends Record<string, (this: Context<M>, ...args: any[]) => any> & {\n [K in keyof T]: K extends keyof M ? never : T[K];\n }\n>(machine: M, newTransitions: T): M & T {\n const { context, ...originalTransitions } = machine;\n const combinedTransitions = { ...originalTransitions, ...newTransitions };\n return createMachine(context, combinedTransitions) as M & T;\n}\n\n/**\n * Creates a builder function from a \"template\" machine instance.\n * This captures the behavior of a machine and returns a factory that can stamp out\n * new instances with different initial contexts. Excellent for class-based machines.\n *\n * @template M - The machine type.\n * @param templateMachine - An instance of a machine to use as the template.\n * @returns A function that builds new machines of type M.\n */\nexport function createMachineBuilder<M extends Machine<any>>(\n templateMachine: M\n): (context: Context<M>) => M {\n const { context, ...transitions } = templateMachine;\n return (newContext: Context<M>): M => {\n return createMachine(newContext, transitions) as M;\n };\n}\n\n/**\n * Pattern match on a machine's state based on a discriminant property in the context.\n * This provides type-safe exhaustive matching for state machines.\n *\n * @template M - The machine type.\n * @template K - The discriminant key in the context.\n * @template R - The return type.\n * @param machine - The machine to match against.\n * @param discriminantKey - The key in the context to use for matching (e.g., \"status\").\n * @param handlers - An object mapping each possible value to a handler function.\n * @returns The result of the matched handler.\n *\n * @example\n * const result = matchMachine(\n * machine,\n * 'status',\n * {\n * idle: (ctx) => \"Machine is idle\",\n * loading: (ctx) => \"Loading...\",\n * success: (ctx) => `Success: ${ctx.data}`,\n * error: (ctx) => `Error: ${ctx.error}`\n * }\n * );\n */\nexport function matchMachine<\n M extends Machine<any>,\n K extends keyof Context<M> & string,\n R\n>(\n machine: M,\n discriminantKey: K,\n handlers: {\n [V in Context<M>[K] & string]: (ctx: Context<M>) => R;\n }\n): R {\n const discriminant = machine.context[discriminantKey] as Context<M>[K] & string;\n const handler = handlers[discriminant];\n if (!handler) {\n throw new Error(`No handler found for state: ${String(discriminant)}`);\n }\n return handler(machine.context);\n}\n\n/**\n * Type-safe helper to assert that a machine's context has a specific discriminant value.\n * This narrows the type of the context based on the discriminant.\n *\n * @template M - The machine type.\n * @template K - The discriminant key.\n * @template V - The discriminant value.\n * @param machine - The machine to check.\n * @param key - The discriminant key to check.\n * @param value - The expected value.\n * @returns True if the discriminant matches, with type narrowing.\n *\n * @example\n * if (hasState(machine, 'status', 'loading')) {\n * // machine.context.status is narrowed to 'loading'\n * }\n */\nexport function hasState<\n M extends Machine<any>,\n K extends keyof Context<M>,\n V extends Context<M>[K]\n>(\n machine: M,\n key: K,\n value: V\n): machine is M & { context: Context<M> & { [P in K]: V } } {\n return machine.context[key] === value;\n}\n\n\n// =============================================================================\n// SECTION: RUNTIME & EVENT DISPATCHER\n// =============================================================================\n\n/**\n * Runs an asynchronous state machine with a managed lifecycle and event dispatch capability.\n * This is the \"interpreter\" for async machines, handling state updates and side effects.\n *\n * @template M - The initial machine type.\n * @param initial - The initial machine state.\n * @param onChange - Optional callback invoked with the new machine state after every transition.\n * @returns An object with a `state` getter for the current context and an async `dispatch` function.\n */\nexport function runMachine<M extends AsyncMachine<any>>(\n initial: M,\n onChange?: (m: M) => void\n) {\n let current = initial;\n\n async function dispatch<E extends Event<typeof current>>(event: E): Promise<M> {\n const fn = (current as any)[event.type];\n if (typeof fn !== 'function') {\n throw new Error(`[Machine] Unknown event type '${String(event.type)}' on current state.`);\n }\n const nextState = await fn.apply(current.context, event.args);\n current = nextState;\n onChange?.(current);\n return current;\n }\n\n return {\n /** Gets the context of the current state of the machine. */\n get state(): Context<M> {\n return current.context;\n },\n /** Dispatches a type-safe event to the machine, triggering a transition. */\n dispatch,\n };\n}\n\n/**\n * An optional base class for creating machines using an Object-Oriented style.\n *\n * This class provides the fundamental structure required by the library: a `context`\n * property to hold the state. By extending `MachineBase`, you get a clear and\n * type-safe starting point for defining states and transitions as classes and methods.\n *\n * Transitions should be implemented as methods that return a new instance of a\n * state machine class (often `new MyClass(...)` or by using a `createMachineBuilder`).\n * The `context` is marked `readonly` to enforce the immutable update pattern.\n *\n * @template C - The context object type that defines the state for this machine.\n *\n * @example\n * // Define a simple counter state\n * class Counter extends MachineBase<{ readonly count: number }> {\n * constructor(count = 0) {\n * super({ count });\n * }\n *\n * increment(): Counter {\n * // Return a new instance for the next state\n * return new Counter(this.context.count + 1);\n * }\n *\n * add(n: number): Counter {\n * return new Counter(this.context.count + n);\n * }\n * }\n *\n * const machine = new Counter(5);\n * const nextState = machine.increment(); // Returns a new Counter instance\n *\n * console.log(machine.context.count); // 5 (original is unchanged)\n * console.log(nextState.context.count); // 6 (new state)\n */\nexport class MachineBase<C extends object> {\n /**\n * The immutable state of the machine.\n * To change the state, a transition method must return a new machine instance\n * with a new context object.\n */\n public readonly context: C;\n\n /**\n * Initializes a new machine instance with its starting context.\n * @param context - The initial state of the machine.\n */\n constructor(context: C) {\n this.context = context;\n // Object.freeze can provide additional runtime safety against accidental mutation,\n // though it comes with a minor performance cost. It's a good practice for ensuring purity.\n // Object.freeze(this.context);\n }\n}\n\n\n/**\n * Applies an update function to a machine's context, returning a new machine.\n * This is a simpler alternative to `setContext` when you always use an updater function.\n *\n * @template C - The context object type.\n * @param m - The machine to update.\n * @param update - A function that takes the current context and returns the new context.\n * @returns A new machine with the updated context.\n *\n * @example\n * const updated = next(counter, (ctx) => ({ count: ctx.count + 1 }));\n */\nexport function next<C extends object>(\n m: Machine<C>,\n update: (ctx: Readonly<C>) => C\n): Machine<C> {\n const { context, ...transitions } = m;\n return createMachine(update(context), transitions) as Machine<C>;\n}\n\n/**\n * A type representing either a synchronous Machine or a Promise that resolves to a Machine.\n * Useful for functions that can return either sync or async machines.\n *\n * @template C - The context object type.\n *\n * @example\n * function getMachine(): MachineLike<{ count: number }> {\n * if (Math.random() > 0.5) {\n * return createMachine({ count: 0 }, { ... });\n * } else {\n * return Promise.resolve(createMachine({ count: 0 }, { ... }));\n * }\n * }\n */\nexport type MachineLike<C extends object> =\n | Machine<C>\n | Promise<Machine<C>>;\n\n/**\n * A type representing the result of a machine transition.\n * Can be either:\n * - A new machine state\n * - A tuple of [machine, cleanup function] where cleanup is called when leaving the state\n *\n * This enables state machines with side effects that need cleanup (e.g., subscriptions, timers).\n *\n * @template C - The context object type.\n *\n * @example\n * function transition(): MachineResult<{ count: number }> {\n * const interval = setInterval(() => console.log(\"tick\"), 1000);\n * const machine = createMachine({ count: 0 }, { ... });\n * return [machine, () => clearInterval(interval)];\n * }\n */\nexport type MachineResult<C extends object> =\n | Machine<C>\n | [Machine<C>, () => void | Promise<void>];\n\n\n// =============================================================================\n// SECTION: GENERATOR-BASED COMPOSITION\n// =============================================================================\n\nexport {\n run,\n step,\n yieldMachine,\n runSequence,\n createFlow,\n runWithDebug,\n runAsync,\n stepAsync\n} from './generators';\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["step"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function s(n,e){let t=n(e),r=e;for(;;){let{value:o,done:c}=t.next(r);if(c)return o;r=o}}function y(n){return function*(){return yield n}()}function C(n){return n}function h(n,e){return e.reduce((t,r)=>s(r,t),n)}function d(n){return n}function u(n,e,t=(r,o)=>{console.log(`Step ${r}:`,o.context)}){let r=n(e),o=e,c=0;for(t(c,o);;){let{value:i,done:M}=r.next(o);if(M)return console.log("Final:",i),i;o=i,c++,t(c,o)}}async function p(n,e){let t=n(e),r=e;for(;;){let{value:o,done:c}=await t.next(r);if(c)return o;r=o}}async function*f(n){return yield n}function a(n,e){return Object.assign({context:n},e)}function g(n,e){return Object.assign({context:n},e)}function l(){return n=>{let e=Object.fromEntries(Object.entries(n).map(([t,r])=>[t,function(...o){let c=r(this,...o);return a(c,e)}]));return t=>a(t,e)}}function b(n,e){let{context:t,...r}=n,o=typeof e=="function"?e(t):e;return a(o,r)}function m(n,e){let{context:t,...r}=n,o={...r,...e};return a(t,o)}function j(n,e){let{context:t,...r}=n,o={...r,...e};return a(t,o)}function A(n){let{context:e,...t}=n;return r=>a(r,t)}function K(n,e,t){let r=n.context[e],o=t[r];if(!o)throw new Error(`No handler found for state: ${String(r)}`);return o(n.context)}function R(n,e,t){return n.context[e]===t}function v(n,e){let t=n;async function r(o){let c=t[o.type];if(typeof c!="function")throw new Error(`[Machine] Unknown event type '${String(o.type)}' on current state.`);return t=await c.apply(t.context,o.args),e==null||e(t),t}return{get state(){return t.context},dispatch:r}}var x=class{constructor(e){this.context=e}};function P(n,e){let{context:t,...r}=n;return a(e(t),r)}export{x as MachineBase,g as createAsyncMachine,d as createFlow,a as createMachine,A as createMachineBuilder,l as createMachineFactory,j as extendTransitions,R as hasState,K as matchMachine,P as next,m as overrideTransitions,s as run,p as runAsync,v as runMachine,h as runSequence,u as runWithDebug,b as setContext,y as step,f as stepAsync,C as yieldMachine};
|