@doeixd/machine 0.0.13 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -15
- package/dist/cjs/development/core.js +1852 -0
- package/dist/cjs/development/core.js.map +7 -0
- package/dist/cjs/development/index.js +1341 -1374
- package/dist/cjs/development/index.js.map +4 -4
- package/dist/cjs/production/core.js +1 -0
- package/dist/cjs/production/index.js +5 -5
- package/dist/esm/development/core.js +1829 -0
- package/dist/esm/development/core.js.map +7 -0
- package/dist/esm/development/index.js +1341 -1374
- package/dist/esm/development/index.js.map +4 -4
- package/dist/esm/production/core.js +1 -0
- package/dist/esm/production/index.js +5 -5
- package/dist/types/core.d.ts +18 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/functional-combinators.d.ts +3 -5
- package/dist/types/functional-combinators.d.ts.map +1 -1
- package/dist/types/index.d.ts +241 -18
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/middleware/composition.d.ts +460 -0
- package/dist/types/middleware/composition.d.ts.map +1 -0
- package/dist/types/middleware/core.d.ts +196 -0
- package/dist/types/middleware/core.d.ts.map +1 -0
- package/dist/types/middleware/history.d.ts +54 -0
- package/dist/types/middleware/history.d.ts.map +1 -0
- package/dist/types/middleware/index.d.ts +10 -0
- package/dist/types/middleware/index.d.ts.map +1 -0
- package/dist/types/middleware/snapshot.d.ts +63 -0
- package/dist/types/middleware/snapshot.d.ts.map +1 -0
- package/dist/types/middleware/time-travel.d.ts +81 -0
- package/dist/types/middleware/time-travel.d.ts.map +1 -0
- package/package.json +19 -6
- package/src/core.ts +167 -0
- package/src/entry-react.ts +9 -0
- package/src/entry-solid.ts +9 -0
- package/src/functional-combinators.ts +3 -3
- package/src/index.ts +374 -101
- package/src/middleware/composition.ts +944 -0
- package/src/middleware/core.ts +573 -0
- package/src/middleware/history.ts +104 -0
- package/src/middleware/index.ts +13 -0
- package/src/middleware/snapshot.ts +153 -0
- package/src/middleware/time-travel.ts +236 -0
- package/src/middleware.ts +735 -1614
- package/src/prototype_functional.ts +46 -0
- package/src/reproduce_issue.ts +26 -0
- package/dist/types/middleware.d.ts +0 -1048
- package/dist/types/middleware.d.ts.map +0 -1
- package/dist/types/runtime-extract.d.ts +0 -53
- package/dist/types/runtime-extract.d.ts.map +0 -1
|
@@ -243,1209 +243,1083 @@ function metadata(_meta, value) {
|
|
|
243
243
|
return value;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
// src/
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return {};
|
|
260
|
-
}
|
|
261
|
-
const result = {};
|
|
262
|
-
for (const prop of obj.getProperties()) {
|
|
263
|
-
if (Node.isPropertyAssignment(prop)) {
|
|
264
|
-
const name = prop.getName();
|
|
265
|
-
const init = prop.getInitializer();
|
|
266
|
-
if (init) {
|
|
267
|
-
if (Node.isStringLiteral(init)) {
|
|
268
|
-
result[name] = init.getLiteralValue();
|
|
269
|
-
} else if (Node.isNumericLiteral(init)) {
|
|
270
|
-
result[name] = init.getLiteralValue();
|
|
271
|
-
} else if (init.getText() === "true" || init.getText() === "false") {
|
|
272
|
-
result[name] = init.getText() === "true";
|
|
273
|
-
} else if (Node.isIdentifier(init)) {
|
|
274
|
-
result[name] = init.getText();
|
|
275
|
-
} else if (Node.isObjectLiteralExpression(init)) {
|
|
276
|
-
result[name] = parseObjectLiteral(init);
|
|
277
|
-
} else if (Node.isArrayLiteralExpression(init)) {
|
|
278
|
-
result[name] = init.getElements().map((el) => {
|
|
279
|
-
if (Node.isObjectLiteralExpression(el)) {
|
|
280
|
-
return parseObjectLiteral(el);
|
|
281
|
-
}
|
|
282
|
-
return el.getText();
|
|
283
|
-
});
|
|
284
|
-
}
|
|
246
|
+
// src/multi.ts
|
|
247
|
+
function createRunner(initialMachine, onChange) {
|
|
248
|
+
let currentMachine = initialMachine;
|
|
249
|
+
const setState = (newState) => {
|
|
250
|
+
currentMachine = newState;
|
|
251
|
+
onChange == null ? void 0 : onChange(newState);
|
|
252
|
+
};
|
|
253
|
+
const { context: _initialContext, ...originalTransitions } = initialMachine;
|
|
254
|
+
const actions = new Proxy({}, {
|
|
255
|
+
get(_target, prop) {
|
|
256
|
+
const transition = currentMachine[prop];
|
|
257
|
+
if (typeof transition !== "function") {
|
|
258
|
+
return void 0;
|
|
285
259
|
}
|
|
260
|
+
return (...args) => {
|
|
261
|
+
const nextState = transition.apply(currentMachine.context, args);
|
|
262
|
+
const nextStateWithTransitions = Object.assign(
|
|
263
|
+
{ context: nextState.context },
|
|
264
|
+
originalTransitions
|
|
265
|
+
);
|
|
266
|
+
setState(nextStateWithTransitions);
|
|
267
|
+
return nextStateWithTransitions;
|
|
268
|
+
};
|
|
286
269
|
}
|
|
287
|
-
}
|
|
288
|
-
return
|
|
270
|
+
});
|
|
271
|
+
return {
|
|
272
|
+
get state() {
|
|
273
|
+
return currentMachine;
|
|
274
|
+
},
|
|
275
|
+
get context() {
|
|
276
|
+
return currentMachine.context;
|
|
277
|
+
},
|
|
278
|
+
actions,
|
|
279
|
+
setState
|
|
280
|
+
};
|
|
289
281
|
}
|
|
290
|
-
function
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
282
|
+
function createEnsemble(store, factories, getDiscriminant) {
|
|
283
|
+
const getCurrentMachine = () => {
|
|
284
|
+
const context = store.getContext();
|
|
285
|
+
const currentStateName = getDiscriminant(context);
|
|
286
|
+
const factory = factories[currentStateName];
|
|
287
|
+
if (!factory) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`[Ensemble] Invalid state: No factory found for state "${String(currentStateName)}".`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
return factory(context);
|
|
293
|
+
};
|
|
294
|
+
const actions = new Proxy({}, {
|
|
295
|
+
get(_target, prop) {
|
|
296
|
+
const currentMachine = getCurrentMachine();
|
|
297
|
+
const action2 = currentMachine[prop];
|
|
298
|
+
if (typeof action2 !== "function") {
|
|
299
|
+
throw new Error(
|
|
300
|
+
`[Ensemble] Transition "${prop}" is not valid in the current state.`
|
|
301
|
+
);
|
|
306
302
|
}
|
|
303
|
+
return (...args) => {
|
|
304
|
+
return action2.apply(currentMachine.context, args);
|
|
305
|
+
};
|
|
307
306
|
}
|
|
308
|
-
}
|
|
309
|
-
return
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
get context() {
|
|
310
|
+
return store.getContext();
|
|
311
|
+
},
|
|
312
|
+
get state() {
|
|
313
|
+
return getCurrentMachine();
|
|
314
|
+
},
|
|
315
|
+
actions
|
|
316
|
+
};
|
|
310
317
|
}
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
return
|
|
314
|
-
}
|
|
315
|
-
const expression = call2.getExpression();
|
|
316
|
-
const fnName = Node.isIdentifier(expression) ? expression.getText() : null;
|
|
317
|
-
if (!fnName) {
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
const metadata2 = {};
|
|
321
|
-
const args = call2.getArguments();
|
|
322
|
-
switch (fnName) {
|
|
323
|
-
case "transitionTo":
|
|
324
|
-
if (args[0]) {
|
|
325
|
-
metadata2.target = resolveClassName(args[0]);
|
|
326
|
-
}
|
|
327
|
-
break;
|
|
328
|
-
case "describe":
|
|
329
|
-
if (args[0] && Node.isStringLiteral(args[0])) {
|
|
330
|
-
metadata2.description = args[0].getLiteralValue();
|
|
331
|
-
}
|
|
332
|
-
if (args[1] && Node.isCallExpression(args[1])) {
|
|
333
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
334
|
-
if (nested) {
|
|
335
|
-
Object.assign(metadata2, nested);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
break;
|
|
339
|
-
case "guarded":
|
|
340
|
-
if (args[0]) {
|
|
341
|
-
const guard2 = parseObjectLiteral(args[0]);
|
|
342
|
-
if (Object.keys(guard2).length > 0) {
|
|
343
|
-
metadata2.guards = [guard2];
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
if (args[1] && Node.isCallExpression(args[1])) {
|
|
347
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
348
|
-
if (nested) {
|
|
349
|
-
Object.assign(metadata2, nested);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
break;
|
|
353
|
-
case "invoke":
|
|
354
|
-
if (args[0]) {
|
|
355
|
-
const service = parseInvokeService(args[0]);
|
|
356
|
-
if (Object.keys(service).length > 0) {
|
|
357
|
-
metadata2.invoke = service;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
break;
|
|
361
|
-
case "action":
|
|
362
|
-
if (args[0]) {
|
|
363
|
-
const actionMeta = parseObjectLiteral(args[0]);
|
|
364
|
-
if (Object.keys(actionMeta).length > 0) {
|
|
365
|
-
metadata2.actions = [actionMeta];
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
if (args[1] && Node.isCallExpression(args[1])) {
|
|
369
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
370
|
-
if (nested) {
|
|
371
|
-
Object.assign(metadata2, nested);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
break;
|
|
375
|
-
case "guard":
|
|
376
|
-
if (args[2]) {
|
|
377
|
-
const options = parseObjectLiteral(args[2]);
|
|
378
|
-
if (options.description) {
|
|
379
|
-
metadata2.description = options.description;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
metadata2.guards = [{ name: "runtime_guard", description: metadata2.description || "Synchronous condition check" }];
|
|
383
|
-
if (args[1] && Node.isCallExpression(args[1])) {
|
|
384
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
385
|
-
if (nested) {
|
|
386
|
-
Object.assign(metadata2, nested);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
break;
|
|
390
|
-
case "guardAsync":
|
|
391
|
-
if (args[2]) {
|
|
392
|
-
const options = parseObjectLiteral(args[2]);
|
|
393
|
-
if (options.description) {
|
|
394
|
-
metadata2.description = options.description;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
metadata2.guards = [{ name: "runtime_guard_async", description: metadata2.description || "Asynchronous condition check" }];
|
|
398
|
-
if (args[1] && Node.isCallExpression(args[1])) {
|
|
399
|
-
const nested = extractFromCallExpression(args[1], verbose);
|
|
400
|
-
if (nested) {
|
|
401
|
-
Object.assign(metadata2, nested);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
break;
|
|
405
|
-
default:
|
|
406
|
-
return null;
|
|
407
|
-
}
|
|
408
|
-
return Object.keys(metadata2).length > 0 ? metadata2 : null;
|
|
318
|
+
function createEnsembleFactory(store, getDiscriminant) {
|
|
319
|
+
return function withFactories(factories) {
|
|
320
|
+
return createEnsemble(store, factories, getDiscriminant);
|
|
321
|
+
};
|
|
409
322
|
}
|
|
410
|
-
function
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (!initializer) {
|
|
417
|
-
if (verbose) console.error(` ⚠️ No initializer`);
|
|
418
|
-
return null;
|
|
419
|
-
}
|
|
420
|
-
if (!Node.isCallExpression(initializer)) {
|
|
421
|
-
if (verbose) console.error(` ⚠️ Initializer is not a call expression`);
|
|
422
|
-
return null;
|
|
323
|
+
function runWithRunner(flow, initialMachine) {
|
|
324
|
+
const runner = createRunner(initialMachine);
|
|
325
|
+
const generator = flow(runner);
|
|
326
|
+
let result = generator.next();
|
|
327
|
+
while (!result.done) {
|
|
328
|
+
result = generator.next();
|
|
423
329
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
330
|
+
return result.value;
|
|
331
|
+
}
|
|
332
|
+
function runWithEnsemble(flow, ensemble) {
|
|
333
|
+
const generator = flow(ensemble);
|
|
334
|
+
let result = generator.next();
|
|
335
|
+
while (!result.done) {
|
|
336
|
+
result = generator.next();
|
|
427
337
|
}
|
|
428
|
-
return
|
|
338
|
+
return result.value;
|
|
429
339
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
return chartNode;
|
|
340
|
+
var MultiMachineBase = class {
|
|
341
|
+
/**
|
|
342
|
+
* @param store - The StateStore that will manage this machine's context.
|
|
343
|
+
*/
|
|
344
|
+
constructor(store) {
|
|
345
|
+
this.store = store;
|
|
438
346
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
347
|
+
/**
|
|
348
|
+
* Read-only access to the current context from the external store.
|
|
349
|
+
* This getter always returns the latest context from the store.
|
|
350
|
+
*
|
|
351
|
+
* @protected
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* const currentStatus = this.context.status;
|
|
355
|
+
* const currentData = this.context.data;
|
|
356
|
+
*/
|
|
357
|
+
get context() {
|
|
358
|
+
return this.store.getContext();
|
|
442
359
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
360
|
+
/**
|
|
361
|
+
* Update the shared context in the external store.
|
|
362
|
+
* Call this method in your transition methods to update the state.
|
|
363
|
+
*
|
|
364
|
+
* @protected
|
|
365
|
+
* @param newContext - The new context object. Should typically be a shallow
|
|
366
|
+
* copy with only the properties you're changing, merged with the current
|
|
367
|
+
* context using spread operators.
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* // In a transition method:
|
|
371
|
+
* this.setContext({ ...this.context, status: 'loading' });
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* // Updating nested properties:
|
|
375
|
+
* this.setContext({
|
|
376
|
+
* ...this.context,
|
|
377
|
+
* user: { ...this.context.user, name: 'Alice' }
|
|
378
|
+
* });
|
|
379
|
+
*/
|
|
380
|
+
setContext(newContext) {
|
|
381
|
+
this.store.setContext(newContext);
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
function createMultiMachine(MachineClass, store) {
|
|
385
|
+
const instance = new MachineClass(store);
|
|
386
|
+
return new Proxy({}, {
|
|
387
|
+
get(_target, prop) {
|
|
388
|
+
const context = store.getContext();
|
|
389
|
+
if (prop in context) {
|
|
390
|
+
return context[prop];
|
|
470
391
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
392
|
+
const method = instance[prop];
|
|
393
|
+
if (typeof method === "function") {
|
|
394
|
+
return (...args) => {
|
|
395
|
+
return method.apply(instance, args);
|
|
396
|
+
};
|
|
476
397
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
398
|
+
return void 0;
|
|
399
|
+
},
|
|
400
|
+
set(_target, prop, value) {
|
|
401
|
+
const context = store.getContext();
|
|
402
|
+
if (prop in context) {
|
|
403
|
+
const newContext = { ...context, [prop]: value };
|
|
404
|
+
store.setContext(newContext);
|
|
405
|
+
return true;
|
|
482
406
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
407
|
+
return false;
|
|
408
|
+
},
|
|
409
|
+
has(_target, prop) {
|
|
410
|
+
const context = store.getContext();
|
|
411
|
+
return prop in context || typeof instance[prop] === "function";
|
|
412
|
+
},
|
|
413
|
+
ownKeys(_target) {
|
|
414
|
+
const context = store.getContext();
|
|
415
|
+
const contextKeys = Object.keys(context);
|
|
416
|
+
const methodKeys = Object.getOwnPropertyNames(
|
|
417
|
+
Object.getPrototypeOf(instance)
|
|
418
|
+
).filter((key) => key !== "constructor" && typeof instance[key] === "function");
|
|
419
|
+
return Array.from(/* @__PURE__ */ new Set([...contextKeys, ...methodKeys]));
|
|
420
|
+
},
|
|
421
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
422
|
+
const context = store.getContext();
|
|
423
|
+
if (prop in context || typeof instance[prop] === "function") {
|
|
424
|
+
return {
|
|
425
|
+
value: void 0,
|
|
426
|
+
writable: true,
|
|
427
|
+
enumerable: true,
|
|
428
|
+
configurable: true
|
|
429
|
+
};
|
|
486
430
|
}
|
|
431
|
+
return void 0;
|
|
487
432
|
}
|
|
488
|
-
}
|
|
489
|
-
return chartNode;
|
|
433
|
+
});
|
|
490
434
|
}
|
|
491
|
-
function
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
435
|
+
function createMutableMachine(sharedContext, factories, getDiscriminant) {
|
|
436
|
+
const getCurrentMachine = () => {
|
|
437
|
+
const currentStateName = getDiscriminant(sharedContext);
|
|
438
|
+
const factory = factories[currentStateName];
|
|
439
|
+
if (!factory) {
|
|
440
|
+
throw new Error(
|
|
441
|
+
`[MutableMachine] Invalid state: No factory for state "${String(currentStateName)}".`
|
|
442
|
+
);
|
|
496
443
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if (
|
|
502
|
-
|
|
503
|
-
stateNode.states[childClassName] = analyzeStateNode(childSymbol, verbose);
|
|
504
|
-
} else {
|
|
505
|
-
console.warn(`⚠️ Warning: Child class '${childClassName}' not found.`);
|
|
444
|
+
return factory(sharedContext);
|
|
445
|
+
};
|
|
446
|
+
return new Proxy(sharedContext, {
|
|
447
|
+
get(target, prop, _receiver) {
|
|
448
|
+
if (prop in target) {
|
|
449
|
+
return target[prop];
|
|
506
450
|
}
|
|
451
|
+
const currentMachine = getCurrentMachine();
|
|
452
|
+
const transition = currentMachine[prop];
|
|
453
|
+
if (typeof transition === "function") {
|
|
454
|
+
return (...args) => {
|
|
455
|
+
const nextContext = transition.apply(currentMachine.context, args);
|
|
456
|
+
if (typeof nextContext !== "object" || nextContext === null) {
|
|
457
|
+
console.warn(`[MutableMachine] Transition "${String(prop)}" did not return a valid context object. State may be inconsistent.`);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
Object.keys(target).forEach((key) => delete target[key]);
|
|
461
|
+
Object.assign(target, nextContext);
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
return void 0;
|
|
465
|
+
},
|
|
466
|
+
set(target, prop, value, _receiver) {
|
|
467
|
+
target[prop] = value;
|
|
468
|
+
return true;
|
|
469
|
+
},
|
|
470
|
+
has(target, prop) {
|
|
471
|
+
const currentMachine = getCurrentMachine();
|
|
472
|
+
return prop in target || typeof currentMachine[prop] === "function";
|
|
507
473
|
}
|
|
508
|
-
}
|
|
509
|
-
return stateNode;
|
|
474
|
+
});
|
|
510
475
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
throw new Error(`Source file not found: ${config.input}`);
|
|
520
|
-
}
|
|
521
|
-
if (config.parallel) {
|
|
522
|
-
if (verbose) {
|
|
523
|
-
console.error(` ⏹️ Parallel machine detected. Analyzing regions.`);
|
|
476
|
+
|
|
477
|
+
// src/higher-order.ts
|
|
478
|
+
function delegateToChild(actionName) {
|
|
479
|
+
return function(...args) {
|
|
480
|
+
const child = this.context.child;
|
|
481
|
+
if (typeof child[actionName] === "function") {
|
|
482
|
+
const newChildState = child[actionName](...args);
|
|
483
|
+
return setContext(this, { ...this.context, child: newChildState });
|
|
524
484
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (
|
|
531
|
-
|
|
485
|
+
return this;
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function toggle(prop) {
|
|
489
|
+
return function() {
|
|
490
|
+
if (typeof this.context[prop] !== "boolean") {
|
|
491
|
+
console.warn(`[toggle primitive] Property '${String(prop)}' is not a boolean. Toggling may have unexpected results.`);
|
|
532
492
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
493
|
+
return setContext(this, {
|
|
494
|
+
...this.context,
|
|
495
|
+
[prop]: !this.context[prop]
|
|
496
|
+
});
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
var IdleMachine = class extends MachineBase {
|
|
500
|
+
constructor(config) {
|
|
501
|
+
super({ status: "idle" });
|
|
502
|
+
this.config = config;
|
|
503
|
+
this.fetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
var LoadingMachine = class extends MachineBase {
|
|
507
|
+
constructor(config, params, attempts) {
|
|
508
|
+
super({ status: "loading", abortController: new AbortController(), attempts });
|
|
509
|
+
this.config = config;
|
|
510
|
+
this.params = params;
|
|
511
|
+
this.succeed = (data) => {
|
|
512
|
+
var _a, _b;
|
|
513
|
+
(_b = (_a = this.config).onSuccess) == null ? void 0 : _b.call(_a, data);
|
|
514
|
+
return new SuccessMachine(this.config, { status: "success", data });
|
|
515
|
+
};
|
|
516
|
+
this.fail = (error) => {
|
|
517
|
+
var _a, _b, _c;
|
|
518
|
+
const maxRetries = (_a = this.config.maxRetries) != null ? _a : 3;
|
|
519
|
+
if (this.context.attempts < maxRetries) {
|
|
520
|
+
return new RetryingMachine(this.config, this.params, error, this.context.attempts);
|
|
546
521
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
522
|
+
(_c = (_b = this.config).onError) == null ? void 0 : _c.call(_b, error);
|
|
523
|
+
return new ErrorMachine(this.config, { status: "error", error });
|
|
524
|
+
};
|
|
525
|
+
this.cancel = () => {
|
|
526
|
+
this.context.abortController.abort();
|
|
527
|
+
return new CanceledMachine(this.config);
|
|
528
|
+
};
|
|
529
|
+
this.execute();
|
|
530
|
+
}
|
|
531
|
+
async execute() {
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
var RetryingMachine = class extends MachineBase {
|
|
535
|
+
constructor(config, params, error, attempts) {
|
|
536
|
+
super({ status: "retrying", error, attempts });
|
|
537
|
+
this.config = config;
|
|
538
|
+
this.params = params;
|
|
539
|
+
// This would be called after a delay.
|
|
540
|
+
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.params, this.context.attempts + 1);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
var SuccessMachine = class extends MachineBase {
|
|
544
|
+
constructor(config, context) {
|
|
545
|
+
super(context);
|
|
546
|
+
this.config = config;
|
|
547
|
+
this.refetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
var ErrorMachine = class extends MachineBase {
|
|
551
|
+
constructor(config, context) {
|
|
552
|
+
super(context);
|
|
553
|
+
this.config = config;
|
|
554
|
+
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
var CanceledMachine = class extends MachineBase {
|
|
558
|
+
constructor(config) {
|
|
559
|
+
super({ status: "canceled" });
|
|
560
|
+
this.config = config;
|
|
561
|
+
this.refetch = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
function createFetchMachine(config) {
|
|
565
|
+
return new IdleMachine(config);
|
|
566
|
+
}
|
|
567
|
+
function createParallelMachine(m1, m2) {
|
|
568
|
+
const combinedContext = { ...m1.context, ...m2.context };
|
|
569
|
+
const transitions1 = { ...m1 };
|
|
570
|
+
const transitions2 = { ...m2 };
|
|
571
|
+
delete transitions1.context;
|
|
572
|
+
delete transitions2.context;
|
|
573
|
+
const combinedTransitions = {};
|
|
574
|
+
for (const key in transitions1) {
|
|
575
|
+
const transitionFn = transitions1[key];
|
|
576
|
+
combinedTransitions[key] = (...args) => {
|
|
577
|
+
const nextM1 = transitionFn.apply(m1.context, args);
|
|
578
|
+
return createParallelMachine(nextM1, m2);
|
|
579
|
+
};
|
|
556
580
|
}
|
|
557
|
-
|
|
558
|
-
|
|
581
|
+
for (const key in transitions2) {
|
|
582
|
+
const transitionFn = transitions2[key];
|
|
583
|
+
combinedTransitions[key] = (...args) => {
|
|
584
|
+
const nextM2 = transitionFn.apply(m2.context, args);
|
|
585
|
+
return createParallelMachine(m1, nextM2);
|
|
586
|
+
};
|
|
559
587
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
states: {}
|
|
588
|
+
return {
|
|
589
|
+
context: combinedContext,
|
|
590
|
+
...combinedTransitions
|
|
564
591
|
};
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
575
|
-
const hasChildren = className === config.initialState && config.children;
|
|
576
|
-
const stateNode = analyzeStateNodeWithNesting(
|
|
577
|
-
className,
|
|
578
|
-
classSymbol,
|
|
579
|
-
sourceFile,
|
|
580
|
-
hasChildren ? config.children : void 0,
|
|
581
|
-
verbose
|
|
582
|
-
);
|
|
583
|
-
fullChart.states[className] = stateNode;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/extract.ts
|
|
595
|
+
import { Project, Node } from "ts-morph";
|
|
596
|
+
function resolveClassName(node) {
|
|
597
|
+
if (Node.isIdentifier(node)) {
|
|
598
|
+
return node.getText();
|
|
584
599
|
}
|
|
585
|
-
if (
|
|
586
|
-
|
|
600
|
+
if (Node.isTypeOfExpression(node)) {
|
|
601
|
+
return node.getExpression().getText();
|
|
587
602
|
}
|
|
588
|
-
return
|
|
603
|
+
return "unknown";
|
|
589
604
|
}
|
|
590
|
-
function
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
if (verbose) {
|
|
594
|
-
console.error(`
|
|
595
|
-
📊 Starting statechart extraction`);
|
|
596
|
-
console.error(` Machines to extract: ${config.machines.length}`);
|
|
605
|
+
function parseObjectLiteral(obj) {
|
|
606
|
+
if (!Node.isObjectLiteralExpression(obj)) {
|
|
607
|
+
return {};
|
|
597
608
|
}
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
609
|
+
const result = {};
|
|
610
|
+
for (const prop of obj.getProperties()) {
|
|
611
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
612
|
+
const name = prop.getName();
|
|
613
|
+
const init = prop.getInitializer();
|
|
614
|
+
if (init) {
|
|
615
|
+
if (Node.isStringLiteral(init)) {
|
|
616
|
+
result[name] = init.getLiteralValue();
|
|
617
|
+
} else if (Node.isNumericLiteral(init)) {
|
|
618
|
+
result[name] = init.getLiteralValue();
|
|
619
|
+
} else if (init.getText() === "true" || init.getText() === "false") {
|
|
620
|
+
result[name] = init.getText() === "true";
|
|
621
|
+
} else if (Node.isIdentifier(init)) {
|
|
622
|
+
result[name] = init.getText();
|
|
623
|
+
} else if (Node.isObjectLiteralExpression(init)) {
|
|
624
|
+
result[name] = parseObjectLiteral(init);
|
|
625
|
+
} else if (Node.isArrayLiteralExpression(init)) {
|
|
626
|
+
result[name] = init.getElements().map((el) => {
|
|
627
|
+
if (Node.isObjectLiteralExpression(el)) {
|
|
628
|
+
return parseObjectLiteral(el);
|
|
629
|
+
}
|
|
630
|
+
return el.getText();
|
|
631
|
+
});
|
|
632
|
+
}
|
|
610
633
|
}
|
|
611
634
|
}
|
|
612
635
|
}
|
|
613
|
-
|
|
614
|
-
console.error(`
|
|
615
|
-
✅ Extraction complete: ${results.length}/${config.machines.length} machines extracted`);
|
|
616
|
-
}
|
|
617
|
-
return results;
|
|
636
|
+
return result;
|
|
618
637
|
}
|
|
619
|
-
function
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
classes: [
|
|
623
|
-
"LoggedOutMachine",
|
|
624
|
-
"LoggingInMachine",
|
|
625
|
-
"LoggedInMachine",
|
|
626
|
-
"SessionExpiredMachine",
|
|
627
|
-
"ErrorMachine"
|
|
628
|
-
],
|
|
629
|
-
id: "auth",
|
|
630
|
-
initialState: "LoggedOutMachine",
|
|
631
|
-
description: "Authentication state machine"
|
|
632
|
-
};
|
|
633
|
-
console.error("🔍 Using legacy generateChart function");
|
|
634
|
-
console.error("⚠️ Consider using extractMachines() with a config file instead\n");
|
|
635
|
-
const project = new Project();
|
|
636
|
-
project.addSourceFilesAtPaths("src/**/*.ts");
|
|
637
|
-
project.addSourceFilesAtPaths("examples/**/*.ts");
|
|
638
|
-
try {
|
|
639
|
-
const chart = extractMachine(config, project, true);
|
|
640
|
-
console.log(JSON.stringify(chart, null, 2));
|
|
641
|
-
} catch (error) {
|
|
642
|
-
console.error(`❌ Error:`, error);
|
|
643
|
-
process.exit(1);
|
|
638
|
+
function parseInvokeService(obj) {
|
|
639
|
+
if (!Node.isObjectLiteralExpression(obj)) {
|
|
640
|
+
return {};
|
|
644
641
|
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
parallel: {
|
|
659
|
-
input: "examples/editorMachine.ts",
|
|
660
|
-
id: "editor",
|
|
661
|
-
parallel: {
|
|
662
|
-
regions: [
|
|
663
|
-
{
|
|
664
|
-
name: "fontWeight",
|
|
665
|
-
initialState: "NormalWeight",
|
|
666
|
-
classes: ["NormalWeight", "BoldWeight"]
|
|
667
|
-
},
|
|
668
|
-
{
|
|
669
|
-
name: "textDecoration",
|
|
670
|
-
initialState: "NoDecoration",
|
|
671
|
-
classes: ["NoDecoration", "UnderlineState"]
|
|
672
|
-
}
|
|
673
|
-
]
|
|
642
|
+
const service = {};
|
|
643
|
+
for (const prop of obj.getProperties()) {
|
|
644
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
645
|
+
const name = prop.getName();
|
|
646
|
+
const init = prop.getInitializer();
|
|
647
|
+
if (!init) continue;
|
|
648
|
+
if (name === "onDone" || name === "onError") {
|
|
649
|
+
service[name] = resolveClassName(init);
|
|
650
|
+
} else if (Node.isStringLiteral(init)) {
|
|
651
|
+
service[name] = init.getLiteralValue();
|
|
652
|
+
} else if (Node.isIdentifier(init)) {
|
|
653
|
+
service[name] = init.getText();
|
|
654
|
+
}
|
|
674
655
|
}
|
|
675
656
|
}
|
|
676
|
-
|
|
677
|
-
if (__require.main === module) {
|
|
678
|
-
generateChart();
|
|
657
|
+
return service;
|
|
679
658
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
function extractFunctionMetadata(fn) {
|
|
683
|
-
if (typeof fn !== "function") {
|
|
659
|
+
function extractFromCallExpression(call2, verbose = false) {
|
|
660
|
+
if (!Node.isCallExpression(call2)) {
|
|
684
661
|
return null;
|
|
685
662
|
}
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
if (
|
|
712
|
-
|
|
663
|
+
const expression = call2.getExpression();
|
|
664
|
+
const fnName = Node.isIdentifier(expression) ? expression.getText() : null;
|
|
665
|
+
if (!fnName) {
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
const metadata2 = {};
|
|
669
|
+
const args = call2.getArguments();
|
|
670
|
+
switch (fnName) {
|
|
671
|
+
case "transitionTo":
|
|
672
|
+
if (args[0]) {
|
|
673
|
+
metadata2.target = resolveClassName(args[0]);
|
|
674
|
+
}
|
|
675
|
+
break;
|
|
676
|
+
case "describe":
|
|
677
|
+
if (args[0] && Node.isStringLiteral(args[0])) {
|
|
678
|
+
metadata2.description = args[0].getLiteralValue();
|
|
679
|
+
}
|
|
680
|
+
if (args[1] && Node.isCallExpression(args[1])) {
|
|
681
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
682
|
+
if (nested) {
|
|
683
|
+
Object.assign(metadata2, nested);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
break;
|
|
687
|
+
case "guarded":
|
|
688
|
+
if (args[0]) {
|
|
689
|
+
const guard2 = parseObjectLiteral(args[0]);
|
|
690
|
+
if (Object.keys(guard2).length > 0) {
|
|
691
|
+
metadata2.guards = [guard2];
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (args[1] && Node.isCallExpression(args[1])) {
|
|
695
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
696
|
+
if (nested) {
|
|
697
|
+
Object.assign(metadata2, nested);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
break;
|
|
701
|
+
case "invoke":
|
|
702
|
+
if (args[0]) {
|
|
703
|
+
const service = parseInvokeService(args[0]);
|
|
704
|
+
if (Object.keys(service).length > 0) {
|
|
705
|
+
metadata2.invoke = service;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
break;
|
|
709
|
+
case "action":
|
|
710
|
+
if (args[0]) {
|
|
711
|
+
const actionMeta = parseObjectLiteral(args[0]);
|
|
712
|
+
if (Object.keys(actionMeta).length > 0) {
|
|
713
|
+
metadata2.actions = [actionMeta];
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (args[1] && Node.isCallExpression(args[1])) {
|
|
717
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
718
|
+
if (nested) {
|
|
719
|
+
Object.assign(metadata2, nested);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
break;
|
|
723
|
+
case "guard":
|
|
724
|
+
if (args[2]) {
|
|
725
|
+
const options = parseObjectLiteral(args[2]);
|
|
726
|
+
if (options.description) {
|
|
727
|
+
metadata2.description = options.description;
|
|
728
|
+
}
|
|
713
729
|
}
|
|
714
|
-
|
|
715
|
-
|
|
730
|
+
metadata2.guards = [{ name: "runtime_guard", description: metadata2.description || "Synchronous condition check" }];
|
|
731
|
+
if (args[1] && Node.isCallExpression(args[1])) {
|
|
732
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
733
|
+
if (nested) {
|
|
734
|
+
Object.assign(metadata2, nested);
|
|
735
|
+
}
|
|
716
736
|
}
|
|
717
|
-
|
|
718
|
-
|
|
737
|
+
break;
|
|
738
|
+
case "guardAsync":
|
|
739
|
+
if (args[2]) {
|
|
740
|
+
const options = parseObjectLiteral(args[2]);
|
|
741
|
+
if (options.description) {
|
|
742
|
+
metadata2.description = options.description;
|
|
743
|
+
}
|
|
719
744
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
};
|
|
727
|
-
if (meta.description) {
|
|
728
|
-
transition.description = meta.description;
|
|
745
|
+
metadata2.guards = [{ name: "runtime_guard_async", description: metadata2.description || "Asynchronous condition check" }];
|
|
746
|
+
if (args[1] && Node.isCallExpression(args[1])) {
|
|
747
|
+
const nested = extractFromCallExpression(args[1], verbose);
|
|
748
|
+
if (nested) {
|
|
749
|
+
Object.assign(metadata2, nested);
|
|
750
|
+
}
|
|
729
751
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
if (invoke2.length > 0) {
|
|
734
|
-
stateNode.invoke = invoke2;
|
|
752
|
+
break;
|
|
753
|
+
default:
|
|
754
|
+
return null;
|
|
735
755
|
}
|
|
736
|
-
return
|
|
756
|
+
return Object.keys(metadata2).length > 0 ? metadata2 : null;
|
|
737
757
|
}
|
|
738
|
-
function
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
states: {}
|
|
743
|
-
};
|
|
744
|
-
if (config.description) {
|
|
745
|
-
chart.description = config.description;
|
|
758
|
+
function extractMetaFromMember(member, verbose = false) {
|
|
759
|
+
if (!Node.isPropertyDeclaration(member)) {
|
|
760
|
+
if (verbose) console.error(` ⚠️ Not a property declaration`);
|
|
761
|
+
return null;
|
|
746
762
|
}
|
|
747
|
-
|
|
748
|
-
|
|
763
|
+
const initializer = member.getInitializer();
|
|
764
|
+
if (!initializer) {
|
|
765
|
+
if (verbose) console.error(` ⚠️ No initializer`);
|
|
766
|
+
return null;
|
|
749
767
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
const stateName = config.stateName || machineInstance.constructor.name || "State";
|
|
754
|
-
return {
|
|
755
|
-
id: config.id,
|
|
756
|
-
initial: stateName,
|
|
757
|
-
states: {
|
|
758
|
-
[stateName]: extractStateNode(machineInstance)
|
|
759
|
-
}
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// src/multi.ts
|
|
764
|
-
function createRunner(initialMachine, onChange) {
|
|
765
|
-
let currentMachine = initialMachine;
|
|
766
|
-
const setState = (newState) => {
|
|
767
|
-
currentMachine = newState;
|
|
768
|
-
onChange == null ? void 0 : onChange(newState);
|
|
769
|
-
};
|
|
770
|
-
const { context: _initialContext, ...originalTransitions } = initialMachine;
|
|
771
|
-
const actions = new Proxy({}, {
|
|
772
|
-
get(_target, prop) {
|
|
773
|
-
const transition = currentMachine[prop];
|
|
774
|
-
if (typeof transition !== "function") {
|
|
775
|
-
return void 0;
|
|
776
|
-
}
|
|
777
|
-
return (...args) => {
|
|
778
|
-
const nextState = transition.apply(currentMachine.context, args);
|
|
779
|
-
const nextStateWithTransitions = Object.assign(
|
|
780
|
-
{ context: nextState.context },
|
|
781
|
-
originalTransitions
|
|
782
|
-
);
|
|
783
|
-
setState(nextStateWithTransitions);
|
|
784
|
-
return nextStateWithTransitions;
|
|
785
|
-
};
|
|
786
|
-
}
|
|
787
|
-
});
|
|
788
|
-
return {
|
|
789
|
-
get state() {
|
|
790
|
-
return currentMachine;
|
|
791
|
-
},
|
|
792
|
-
get context() {
|
|
793
|
-
return currentMachine.context;
|
|
794
|
-
},
|
|
795
|
-
actions,
|
|
796
|
-
setState
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
function createEnsemble(store, factories, getDiscriminant) {
|
|
800
|
-
const getCurrentMachine = () => {
|
|
801
|
-
const context = store.getContext();
|
|
802
|
-
const currentStateName = getDiscriminant(context);
|
|
803
|
-
const factory = factories[currentStateName];
|
|
804
|
-
if (!factory) {
|
|
805
|
-
throw new Error(
|
|
806
|
-
`[Ensemble] Invalid state: No factory found for state "${String(currentStateName)}".`
|
|
807
|
-
);
|
|
808
|
-
}
|
|
809
|
-
return factory(context);
|
|
810
|
-
};
|
|
811
|
-
const actions = new Proxy({}, {
|
|
812
|
-
get(_target, prop) {
|
|
813
|
-
const currentMachine = getCurrentMachine();
|
|
814
|
-
const action2 = currentMachine[prop];
|
|
815
|
-
if (typeof action2 !== "function") {
|
|
816
|
-
throw new Error(
|
|
817
|
-
`[Ensemble] Transition "${prop}" is not valid in the current state.`
|
|
818
|
-
);
|
|
819
|
-
}
|
|
820
|
-
return (...args) => {
|
|
821
|
-
return action2.apply(currentMachine.context, args);
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
});
|
|
825
|
-
return {
|
|
826
|
-
get context() {
|
|
827
|
-
return store.getContext();
|
|
828
|
-
},
|
|
829
|
-
get state() {
|
|
830
|
-
return getCurrentMachine();
|
|
831
|
-
},
|
|
832
|
-
actions
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
function createEnsembleFactory(store, getDiscriminant) {
|
|
836
|
-
return function withFactories(factories) {
|
|
837
|
-
return createEnsemble(store, factories, getDiscriminant);
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
function runWithRunner(flow, initialMachine) {
|
|
841
|
-
const runner = createRunner(initialMachine);
|
|
842
|
-
const generator = flow(runner);
|
|
843
|
-
let result = generator.next();
|
|
844
|
-
while (!result.done) {
|
|
845
|
-
result = generator.next();
|
|
768
|
+
if (!Node.isCallExpression(initializer)) {
|
|
769
|
+
if (verbose) console.error(` ⚠️ Initializer is not a call expression`);
|
|
770
|
+
return null;
|
|
846
771
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const generator = flow(ensemble);
|
|
851
|
-
let result = generator.next();
|
|
852
|
-
while (!result.done) {
|
|
853
|
-
result = generator.next();
|
|
772
|
+
const metadata2 = extractFromCallExpression(initializer, verbose);
|
|
773
|
+
if (metadata2 && verbose) {
|
|
774
|
+
console.error(` ✅ Extracted metadata:`, JSON.stringify(metadata2, null, 2));
|
|
854
775
|
}
|
|
855
|
-
return
|
|
776
|
+
return metadata2;
|
|
856
777
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
* Read-only access to the current context from the external store.
|
|
866
|
-
* This getter always returns the latest context from the store.
|
|
867
|
-
*
|
|
868
|
-
* @protected
|
|
869
|
-
*
|
|
870
|
-
* @example
|
|
871
|
-
* const currentStatus = this.context.status;
|
|
872
|
-
* const currentData = this.context.data;
|
|
873
|
-
*/
|
|
874
|
-
get context() {
|
|
875
|
-
return this.store.getContext();
|
|
778
|
+
function analyzeStateNode(classSymbol, verbose = false) {
|
|
779
|
+
const chartNode = { on: {} };
|
|
780
|
+
const classDeclaration = classSymbol.getDeclarations()[0];
|
|
781
|
+
if (!classDeclaration || !Node.isClassDeclaration(classDeclaration)) {
|
|
782
|
+
if (verbose) {
|
|
783
|
+
console.error(`⚠️ Warning: Could not get class declaration for ${classSymbol.getName()}`);
|
|
784
|
+
}
|
|
785
|
+
return chartNode;
|
|
876
786
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
*
|
|
881
|
-
* @protected
|
|
882
|
-
* @param newContext - The new context object. Should typically be a shallow
|
|
883
|
-
* copy with only the properties you're changing, merged with the current
|
|
884
|
-
* context using spread operators.
|
|
885
|
-
*
|
|
886
|
-
* @example
|
|
887
|
-
* // In a transition method:
|
|
888
|
-
* this.setContext({ ...this.context, status: 'loading' });
|
|
889
|
-
*
|
|
890
|
-
* @example
|
|
891
|
-
* // Updating nested properties:
|
|
892
|
-
* this.setContext({
|
|
893
|
-
* ...this.context,
|
|
894
|
-
* user: { ...this.context.user, name: 'Alice' }
|
|
895
|
-
* });
|
|
896
|
-
*/
|
|
897
|
-
setContext(newContext) {
|
|
898
|
-
this.store.setContext(newContext);
|
|
787
|
+
const className = classSymbol.getName();
|
|
788
|
+
if (verbose) {
|
|
789
|
+
console.error(` Analyzing state: ${className}`);
|
|
899
790
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
if (
|
|
920
|
-
|
|
921
|
-
store.setContext(newContext);
|
|
922
|
-
return true;
|
|
923
|
-
}
|
|
924
|
-
return false;
|
|
925
|
-
},
|
|
926
|
-
has(_target, prop) {
|
|
927
|
-
const context = store.getContext();
|
|
928
|
-
return prop in context || typeof instance[prop] === "function";
|
|
929
|
-
},
|
|
930
|
-
ownKeys(_target) {
|
|
931
|
-
const context = store.getContext();
|
|
932
|
-
const contextKeys = Object.keys(context);
|
|
933
|
-
const methodKeys = Object.getOwnPropertyNames(
|
|
934
|
-
Object.getPrototypeOf(instance)
|
|
935
|
-
).filter((key) => key !== "constructor" && typeof instance[key] === "function");
|
|
936
|
-
return Array.from(/* @__PURE__ */ new Set([...contextKeys, ...methodKeys]));
|
|
937
|
-
},
|
|
938
|
-
getOwnPropertyDescriptor(_target, prop) {
|
|
939
|
-
const context = store.getContext();
|
|
940
|
-
if (prop in context || typeof instance[prop] === "function") {
|
|
941
|
-
return {
|
|
942
|
-
value: void 0,
|
|
943
|
-
writable: true,
|
|
944
|
-
enumerable: true,
|
|
945
|
-
configurable: true
|
|
946
|
-
};
|
|
791
|
+
for (const member of classDeclaration.getInstanceMembers()) {
|
|
792
|
+
const memberName = member.getName();
|
|
793
|
+
if (verbose) {
|
|
794
|
+
console.error(` Checking member: ${memberName}`);
|
|
795
|
+
}
|
|
796
|
+
const meta = extractMetaFromMember(member, verbose);
|
|
797
|
+
if (!meta) continue;
|
|
798
|
+
if (verbose) {
|
|
799
|
+
console.error(` Found transition: ${memberName}`);
|
|
800
|
+
}
|
|
801
|
+
const { invoke: invoke2, actions, guards, ...onEntry } = meta;
|
|
802
|
+
if (invoke2) {
|
|
803
|
+
if (!chartNode.invoke) chartNode.invoke = [];
|
|
804
|
+
chartNode.invoke.push({
|
|
805
|
+
src: invoke2.src,
|
|
806
|
+
onDone: { target: invoke2.onDone },
|
|
807
|
+
onError: { target: invoke2.onError },
|
|
808
|
+
description: invoke2.description
|
|
809
|
+
});
|
|
810
|
+
if (verbose) {
|
|
811
|
+
console.error(` → Invoke: ${invoke2.src}`);
|
|
947
812
|
}
|
|
948
|
-
return void 0;
|
|
949
|
-
}
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
function createMutableMachine(sharedContext, factories, getDiscriminant) {
|
|
953
|
-
const getCurrentMachine = () => {
|
|
954
|
-
const currentStateName = getDiscriminant(sharedContext);
|
|
955
|
-
const factory = factories[currentStateName];
|
|
956
|
-
if (!factory) {
|
|
957
|
-
throw new Error(
|
|
958
|
-
`[MutableMachine] Invalid state: No factory for state "${String(currentStateName)}".`
|
|
959
|
-
);
|
|
960
813
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
if (prop in target) {
|
|
966
|
-
return target[prop];
|
|
814
|
+
if (onEntry.target) {
|
|
815
|
+
const transition = { target: onEntry.target };
|
|
816
|
+
if (onEntry.description) {
|
|
817
|
+
transition.description = onEntry.description;
|
|
967
818
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
819
|
+
if (guards) {
|
|
820
|
+
transition.cond = guards.map((g) => g.name).join(" && ");
|
|
821
|
+
if (verbose) {
|
|
822
|
+
console.error(` → Guard: ${transition.cond}`);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (actions && actions.length > 0) {
|
|
826
|
+
transition.actions = actions.map((a) => a.name);
|
|
827
|
+
if (verbose) {
|
|
828
|
+
console.error(` → Actions: ${transition.actions.join(", ")}`);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
chartNode.on[memberName] = transition;
|
|
832
|
+
if (verbose) {
|
|
833
|
+
console.error(` → Target: ${onEntry.target}`);
|
|
980
834
|
}
|
|
981
|
-
return void 0;
|
|
982
|
-
},
|
|
983
|
-
set(target, prop, value, _receiver) {
|
|
984
|
-
target[prop] = value;
|
|
985
|
-
return true;
|
|
986
|
-
},
|
|
987
|
-
has(target, prop) {
|
|
988
|
-
const currentMachine = getCurrentMachine();
|
|
989
|
-
return prop in target || typeof currentMachine[prop] === "function";
|
|
990
835
|
}
|
|
991
|
-
}
|
|
836
|
+
}
|
|
837
|
+
return chartNode;
|
|
992
838
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
if (typeof child[actionName] === "function") {
|
|
999
|
-
const newChildState = child[actionName](...args);
|
|
1000
|
-
return setContext(this, { ...this.context, child: newChildState });
|
|
839
|
+
function analyzeStateNodeWithNesting(className, classSymbol, sourceFile, childConfig, verbose = false) {
|
|
840
|
+
const stateNode = analyzeStateNode(classSymbol, verbose);
|
|
841
|
+
if (childConfig) {
|
|
842
|
+
if (verbose) {
|
|
843
|
+
console.error(` 👪 Analyzing children for state: ${className}`);
|
|
1001
844
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
845
|
+
stateNode.initial = childConfig.initialState;
|
|
846
|
+
stateNode.states = {};
|
|
847
|
+
for (const childClassName of childConfig.classes) {
|
|
848
|
+
const childClassDeclaration = sourceFile.getClass(childClassName);
|
|
849
|
+
if (childClassDeclaration) {
|
|
850
|
+
const childSymbol = childClassDeclaration.getSymbolOrThrow();
|
|
851
|
+
stateNode.states[childClassName] = analyzeStateNode(childSymbol, verbose);
|
|
852
|
+
} else {
|
|
853
|
+
console.warn(`⚠️ Warning: Child class '${childClassName}' not found.`);
|
|
854
|
+
}
|
|
1009
855
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
[prop]: !this.context[prop]
|
|
1013
|
-
});
|
|
1014
|
-
};
|
|
856
|
+
}
|
|
857
|
+
return stateNode;
|
|
1015
858
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
859
|
+
function extractMachine(config, project, verbose = false) {
|
|
860
|
+
if (verbose) {
|
|
861
|
+
console.error(`
|
|
862
|
+
🔍 Analyzing machine: ${config.id}`);
|
|
863
|
+
console.error(` Source: ${config.input}`);
|
|
1021
864
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
865
|
+
const sourceFile = project.getSourceFile(config.input);
|
|
866
|
+
if (!sourceFile) {
|
|
867
|
+
throw new Error(`Source file not found: ${config.input}`);
|
|
868
|
+
}
|
|
869
|
+
if (config.parallel) {
|
|
870
|
+
if (verbose) {
|
|
871
|
+
console.error(` ⏹️ Parallel machine detected. Analyzing regions.`);
|
|
872
|
+
}
|
|
873
|
+
const parallelChart = {
|
|
874
|
+
id: config.id,
|
|
875
|
+
type: "parallel",
|
|
876
|
+
states: {}
|
|
1032
877
|
};
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
878
|
+
if (config.description) {
|
|
879
|
+
parallelChart.description = config.description;
|
|
880
|
+
}
|
|
881
|
+
for (const region of config.parallel.regions) {
|
|
882
|
+
if (verbose) {
|
|
883
|
+
console.error(` 📍 Analyzing region: ${region.name}`);
|
|
1038
884
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
885
|
+
const regionStates = {};
|
|
886
|
+
for (const className of region.classes) {
|
|
887
|
+
const classDeclaration = sourceFile.getClass(className);
|
|
888
|
+
if (classDeclaration) {
|
|
889
|
+
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
890
|
+
regionStates[className] = analyzeStateNode(classSymbol, verbose);
|
|
891
|
+
} else {
|
|
892
|
+
console.warn(`⚠️ Warning: Class '${className}' not found for region '${region.name}'.`);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
parallelChart.states[region.name] = {
|
|
896
|
+
initial: region.initialState,
|
|
897
|
+
states: regionStates
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
if (verbose) {
|
|
901
|
+
console.error(` ✅ Extracted ${config.parallel.regions.length} parallel regions`);
|
|
902
|
+
}
|
|
903
|
+
return parallelChart;
|
|
1047
904
|
}
|
|
1048
|
-
|
|
905
|
+
if (!config.initialState || !config.classes) {
|
|
906
|
+
throw new Error(`Machine config for '${config.id}' must have either 'parallel' or 'initialState'/'classes'.`);
|
|
1049
907
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.params, this.context.attempts + 1);
|
|
908
|
+
const fullChart = {
|
|
909
|
+
id: config.id,
|
|
910
|
+
initial: config.initialState,
|
|
911
|
+
states: {}
|
|
912
|
+
};
|
|
913
|
+
if (config.description) {
|
|
914
|
+
fullChart.description = config.description;
|
|
1058
915
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
916
|
+
for (const className of config.classes) {
|
|
917
|
+
const classDeclaration = sourceFile.getClass(className);
|
|
918
|
+
if (!classDeclaration) {
|
|
919
|
+
console.warn(`⚠️ Warning: Class '${className}' not found in '${config.input}'. Skipping.`);
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
const classSymbol = classDeclaration.getSymbolOrThrow();
|
|
923
|
+
const hasChildren = className === config.initialState && config.children;
|
|
924
|
+
const stateNode = analyzeStateNodeWithNesting(
|
|
925
|
+
className,
|
|
926
|
+
classSymbol,
|
|
927
|
+
sourceFile,
|
|
928
|
+
hasChildren ? config.children : void 0,
|
|
929
|
+
verbose
|
|
930
|
+
);
|
|
931
|
+
fullChart.states[className] = stateNode;
|
|
1065
932
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
constructor(config, context) {
|
|
1069
|
-
super(context);
|
|
1070
|
-
this.config = config;
|
|
1071
|
-
this.retry = (params) => new LoadingMachine(this.config, params != null ? params : this.config.initialParams, 1);
|
|
933
|
+
if (verbose) {
|
|
934
|
+
console.error(` ✅ Extracted ${config.classes.length} states`);
|
|
1072
935
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
936
|
+
return fullChart;
|
|
937
|
+
}
|
|
938
|
+
function extractMachines(config) {
|
|
939
|
+
var _a;
|
|
940
|
+
const verbose = (_a = config.verbose) != null ? _a : false;
|
|
941
|
+
if (verbose) {
|
|
942
|
+
console.error(`
|
|
943
|
+
📊 Starting statechart extraction`);
|
|
944
|
+
console.error(` Machines to extract: ${config.machines.length}`);
|
|
945
|
+
}
|
|
946
|
+
const project = new Project();
|
|
947
|
+
project.addSourceFilesAtPaths("src/**/*.ts");
|
|
948
|
+
project.addSourceFilesAtPaths("examples/**/*.ts");
|
|
949
|
+
const results = [];
|
|
950
|
+
for (const machineConfig of config.machines) {
|
|
951
|
+
try {
|
|
952
|
+
const chart = extractMachine(machineConfig, project, verbose);
|
|
953
|
+
results.push(chart);
|
|
954
|
+
} catch (error) {
|
|
955
|
+
console.error(`❌ Error extracting machine '${machineConfig.id}':`, error);
|
|
956
|
+
if (!verbose) {
|
|
957
|
+
console.error(` Run with --verbose for more details`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
if (verbose) {
|
|
962
|
+
console.error(`
|
|
963
|
+
✅ Extraction complete: ${results.length}/${config.machines.length} machines extracted`);
|
|
1079
964
|
}
|
|
1080
|
-
|
|
1081
|
-
function createFetchMachine(config) {
|
|
1082
|
-
return new IdleMachine(config);
|
|
965
|
+
return results;
|
|
1083
966
|
}
|
|
1084
|
-
function
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
967
|
+
function generateChart() {
|
|
968
|
+
const config = {
|
|
969
|
+
input: "examples/authMachine.ts",
|
|
970
|
+
classes: [
|
|
971
|
+
"LoggedOutMachine",
|
|
972
|
+
"LoggingInMachine",
|
|
973
|
+
"LoggedInMachine",
|
|
974
|
+
"SessionExpiredMachine",
|
|
975
|
+
"ErrorMachine"
|
|
976
|
+
],
|
|
977
|
+
id: "auth",
|
|
978
|
+
initialState: "LoggedOutMachine",
|
|
979
|
+
description: "Authentication state machine"
|
|
980
|
+
};
|
|
981
|
+
console.error("🔍 Using legacy generateChart function");
|
|
982
|
+
console.error("⚠️ Consider using extractMachines() with a config file instead\n");
|
|
983
|
+
const project = new Project();
|
|
984
|
+
project.addSourceFilesAtPaths("src/**/*.ts");
|
|
985
|
+
project.addSourceFilesAtPaths("examples/**/*.ts");
|
|
986
|
+
try {
|
|
987
|
+
const chart = extractMachine(config, project, true);
|
|
988
|
+
console.log(JSON.stringify(chart, null, 2));
|
|
989
|
+
} catch (error) {
|
|
990
|
+
console.error(`❌ Error:`, error);
|
|
991
|
+
process.exit(1);
|
|
1097
992
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
993
|
+
}
|
|
994
|
+
var ADVANCED_CONFIG_EXAMPLES = {
|
|
995
|
+
hierarchical: {
|
|
996
|
+
input: "examples/dashboardMachine.ts",
|
|
997
|
+
id: "dashboard",
|
|
998
|
+
classes: ["DashboardMachine", "LoggedOutMachine"],
|
|
999
|
+
initialState: "DashboardMachine",
|
|
1000
|
+
children: {
|
|
1001
|
+
contextProperty: "child",
|
|
1002
|
+
initialState: "ViewingChildMachine",
|
|
1003
|
+
classes: ["ViewingChildMachine", "EditingChildMachine"]
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
parallel: {
|
|
1007
|
+
input: "examples/editorMachine.ts",
|
|
1008
|
+
id: "editor",
|
|
1009
|
+
parallel: {
|
|
1010
|
+
regions: [
|
|
1011
|
+
{
|
|
1012
|
+
name: "fontWeight",
|
|
1013
|
+
initialState: "NormalWeight",
|
|
1014
|
+
classes: ["NormalWeight", "BoldWeight"]
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
name: "textDecoration",
|
|
1018
|
+
initialState: "NoDecoration",
|
|
1019
|
+
classes: ["NoDecoration", "UnderlineState"]
|
|
1020
|
+
}
|
|
1021
|
+
]
|
|
1022
|
+
}
|
|
1104
1023
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
};
|
|
1024
|
+
};
|
|
1025
|
+
if (__require.main === module) {
|
|
1026
|
+
generateChart();
|
|
1109
1027
|
}
|
|
1110
1028
|
|
|
1111
|
-
// src/middleware.ts
|
|
1029
|
+
// src/middleware/core.ts
|
|
1112
1030
|
var CANCEL = Symbol("CANCEL");
|
|
1113
1031
|
function createMiddleware(machine, hooks, options = {}) {
|
|
1114
|
-
const {
|
|
1115
|
-
const
|
|
1032
|
+
const { continueOnError = false, logErrors = true, onError } = options;
|
|
1033
|
+
const wrappedMachine = { ...machine };
|
|
1116
1034
|
for (const prop in machine) {
|
|
1117
1035
|
if (!Object.prototype.hasOwnProperty.call(machine, prop)) continue;
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
wrapped.context = value;
|
|
1121
|
-
continue;
|
|
1122
|
-
}
|
|
1123
|
-
if (exclude.includes(prop)) {
|
|
1124
|
-
wrapped[prop] = value;
|
|
1125
|
-
continue;
|
|
1036
|
+
if (prop !== "context" && typeof machine[prop] !== "function") {
|
|
1037
|
+
wrappedMachine[prop] = machine[prop];
|
|
1126
1038
|
}
|
|
1127
|
-
if (typeof value !== "function" || prop.startsWith("_")) {
|
|
1128
|
-
wrapped[prop] = value;
|
|
1129
|
-
continue;
|
|
1130
|
-
}
|
|
1131
|
-
wrapped[prop] = createTransitionWrapper(
|
|
1132
|
-
prop,
|
|
1133
|
-
value,
|
|
1134
|
-
machine,
|
|
1135
|
-
hooks,
|
|
1136
|
-
mode
|
|
1137
|
-
);
|
|
1138
1039
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
if (hooks.after) {
|
|
1167
|
-
const middlewareResult = {
|
|
1168
|
-
transitionName,
|
|
1169
|
-
prevContext: context,
|
|
1170
|
-
nextContext: result.context,
|
|
1171
|
-
args
|
|
1172
|
-
};
|
|
1173
|
-
const afterResult = hooks.after(middlewareResult);
|
|
1174
|
-
if (afterResult instanceof Promise) {
|
|
1175
|
-
throw new Error(
|
|
1176
|
-
`Middleware mode is 'sync' but after hook returned Promise for transition: ${transitionName}`
|
|
1177
|
-
);
|
|
1040
|
+
for (const prop in machine) {
|
|
1041
|
+
if (!Object.prototype.hasOwnProperty.call(machine, prop)) continue;
|
|
1042
|
+
const value = machine[prop];
|
|
1043
|
+
if (typeof value === "function" && prop !== "context") {
|
|
1044
|
+
wrappedMachine[prop] = function(...args) {
|
|
1045
|
+
const transitionName = prop;
|
|
1046
|
+
const context = wrappedMachine.context;
|
|
1047
|
+
const executeTransition = () => {
|
|
1048
|
+
let nextMachine;
|
|
1049
|
+
try {
|
|
1050
|
+
nextMachine = value.apply(this, args);
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
if (hooks.error) {
|
|
1053
|
+
try {
|
|
1054
|
+
hooks.error({
|
|
1055
|
+
transitionName,
|
|
1056
|
+
context,
|
|
1057
|
+
args: [...args],
|
|
1058
|
+
error
|
|
1059
|
+
});
|
|
1060
|
+
} catch (hookError) {
|
|
1061
|
+
if (!continueOnError) throw hookError;
|
|
1062
|
+
if (logErrors) console.error(`Middleware error hook error for ${transitionName}:`, hookError);
|
|
1063
|
+
onError == null ? void 0 : onError(hookError, "error", { transitionName, context, args, error });
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
throw error;
|
|
1178
1067
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1068
|
+
const ensureMiddlewareProperties = (machine2) => {
|
|
1069
|
+
if (machine2 && typeof machine2 === "object" && machine2.context) {
|
|
1070
|
+
for (const prop2 in wrappedMachine) {
|
|
1071
|
+
if (!Object.prototype.hasOwnProperty.call(wrappedMachine, prop2)) continue;
|
|
1072
|
+
if (prop2 !== "context" && !(prop2 in machine2)) {
|
|
1073
|
+
machine2[prop2] = wrappedMachine[prop2];
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
for (const prop2 in machine2) {
|
|
1077
|
+
if (!Object.prototype.hasOwnProperty.call(machine2, prop2)) continue;
|
|
1078
|
+
const value2 = machine2[prop2];
|
|
1079
|
+
if (typeof value2 === "function" && prop2 !== "context" && wrappedMachine[prop2]) {
|
|
1080
|
+
machine2[prop2] = wrappedMachine[prop2];
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
return machine2;
|
|
1188
1085
|
};
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1086
|
+
if (nextMachine && typeof nextMachine.then === "function") {
|
|
1087
|
+
const asyncResult = nextMachine.then((resolvedMachine) => {
|
|
1088
|
+
ensureMiddlewareProperties(resolvedMachine);
|
|
1089
|
+
if (hooks.after) {
|
|
1090
|
+
try {
|
|
1091
|
+
const result = hooks.after({
|
|
1092
|
+
transitionName,
|
|
1093
|
+
prevContext: context,
|
|
1094
|
+
nextContext: resolvedMachine.context,
|
|
1095
|
+
args: [...args]
|
|
1096
|
+
});
|
|
1097
|
+
if (result && typeof result.then === "function") {
|
|
1098
|
+
return result.then(() => resolvedMachine);
|
|
1099
|
+
}
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
if (!continueOnError) throw error;
|
|
1102
|
+
if (logErrors) console.error(`Middleware after hook error for ${transitionName}:`, error);
|
|
1103
|
+
onError == null ? void 0 : onError(error, "after", {
|
|
1104
|
+
transitionName,
|
|
1105
|
+
prevContext: context,
|
|
1106
|
+
nextContext: resolvedMachine.context,
|
|
1107
|
+
args
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return resolvedMachine;
|
|
1192
1112
|
});
|
|
1193
|
-
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1113
|
+
return asyncResult;
|
|
1114
|
+
} else {
|
|
1115
|
+
ensureMiddlewareProperties(nextMachine);
|
|
1116
|
+
if (hooks.after) {
|
|
1117
|
+
try {
|
|
1118
|
+
const result = hooks.after({
|
|
1119
|
+
transitionName,
|
|
1120
|
+
prevContext: context,
|
|
1121
|
+
nextContext: nextMachine.context,
|
|
1122
|
+
args: [...args]
|
|
1123
|
+
});
|
|
1124
|
+
if (result && typeof result === "object" && result && "then" in result) {
|
|
1125
|
+
return result.then(() => nextMachine).catch((error) => {
|
|
1126
|
+
if (!continueOnError) throw error;
|
|
1127
|
+
if (logErrors) console.error(`Middleware after hook error for ${transitionName}:`, error);
|
|
1128
|
+
onError == null ? void 0 : onError(error, "after", {
|
|
1129
|
+
transitionName,
|
|
1130
|
+
prevContext: context,
|
|
1131
|
+
nextContext: nextMachine.context,
|
|
1132
|
+
args
|
|
1133
|
+
});
|
|
1134
|
+
return nextMachine;
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
if (!continueOnError) throw error;
|
|
1139
|
+
if (logErrors) console.error(`Middleware after hook error for ${transitionName}:`, error);
|
|
1140
|
+
onError == null ? void 0 : onError(error, "after", {
|
|
1141
|
+
transitionName,
|
|
1142
|
+
prevContext: context,
|
|
1143
|
+
nextContext: nextMachine.context,
|
|
1144
|
+
args
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
return nextMachine;
|
|
1226
1149
|
}
|
|
1227
|
-
}
|
|
1228
|
-
throw err;
|
|
1229
|
-
}
|
|
1230
|
-
};
|
|
1231
|
-
const executeAsyncTransition = async () => {
|
|
1232
|
-
try {
|
|
1150
|
+
};
|
|
1233
1151
|
if (hooks.before) {
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
return errorResult;
|
|
1152
|
+
try {
|
|
1153
|
+
const result = hooks.before({
|
|
1154
|
+
transitionName,
|
|
1155
|
+
context,
|
|
1156
|
+
args: [...args]
|
|
1157
|
+
});
|
|
1158
|
+
if (result && typeof result === "object" && result && "then" in result) {
|
|
1159
|
+
return result.then((hookResult) => {
|
|
1160
|
+
if (hookResult === CANCEL) {
|
|
1161
|
+
return wrappedMachine;
|
|
1162
|
+
}
|
|
1163
|
+
return executeTransition();
|
|
1164
|
+
}).catch((error) => {
|
|
1165
|
+
if (!continueOnError) throw error;
|
|
1166
|
+
if (logErrors) console.error(`Middleware before hook error for ${transitionName}:`, error);
|
|
1167
|
+
onError == null ? void 0 : onError(error, "before", { transitionName, context, args });
|
|
1168
|
+
return executeTransition();
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
if (result === CANCEL) {
|
|
1172
|
+
return wrappedMachine;
|
|
1173
|
+
}
|
|
1174
|
+
} catch (error) {
|
|
1175
|
+
if (!continueOnError) throw error;
|
|
1176
|
+
if (logErrors) console.error(`Middleware before hook error for ${transitionName}:`, error);
|
|
1177
|
+
onError == null ? void 0 : onError(error, "before", { transitionName, context, args });
|
|
1261
1178
|
}
|
|
1262
1179
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
if (mode === "async") {
|
|
1267
|
-
return executeAsyncTransition();
|
|
1268
|
-
} else if (mode === "sync") {
|
|
1269
|
-
return executeSyncTransition();
|
|
1270
|
-
} else {
|
|
1271
|
-
return executeSyncTransition();
|
|
1180
|
+
;
|
|
1181
|
+
return executeTransition();
|
|
1182
|
+
};
|
|
1272
1183
|
}
|
|
1273
|
-
}
|
|
1184
|
+
}
|
|
1185
|
+
return wrappedMachine;
|
|
1274
1186
|
}
|
|
1275
1187
|
function withLogging(machine, options = {}) {
|
|
1276
|
-
const {
|
|
1277
|
-
logger = console.log,
|
|
1278
|
-
includeContext = true,
|
|
1279
|
-
includeArgs = true
|
|
1280
|
-
} = options;
|
|
1188
|
+
const { logger = console.log, includeArgs = false, includeContext = true } = options;
|
|
1281
1189
|
return createMiddleware(machine, {
|
|
1282
1190
|
before: ({ transitionName, args }) => {
|
|
1283
|
-
const
|
|
1284
|
-
logger(
|
|
1191
|
+
const message = includeArgs ? `→ ${transitionName} [${args.join(", ")}]` : `→ ${transitionName}`;
|
|
1192
|
+
logger(message);
|
|
1285
1193
|
},
|
|
1286
1194
|
after: ({ transitionName, nextContext }) => {
|
|
1287
1195
|
const contextStr = includeContext ? ` ${JSON.stringify(nextContext)}` : "";
|
|
1288
1196
|
logger(`✓ ${transitionName}${contextStr}`);
|
|
1197
|
+
},
|
|
1198
|
+
error: ({ transitionName, error }) => {
|
|
1199
|
+
console.error(`[Machine] ${transitionName} failed:`, error);
|
|
1289
1200
|
}
|
|
1290
1201
|
});
|
|
1291
1202
|
}
|
|
1292
1203
|
function withAnalytics(machine, track, options = {}) {
|
|
1293
|
-
const {
|
|
1294
|
-
eventPrefix = "state_transition",
|
|
1295
|
-
includePrevContext = false,
|
|
1296
|
-
includeArgs = true
|
|
1297
|
-
} = options;
|
|
1204
|
+
const { eventPrefix = "state_transition", includePrevContext = false, includeArgs = false } = options;
|
|
1298
1205
|
return createMiddleware(machine, {
|
|
1299
|
-
after:
|
|
1300
|
-
const
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
if (
|
|
1305
|
-
|
|
1306
|
-
}
|
|
1307
|
-
if (includeArgs && args.length > 0) {
|
|
1308
|
-
properties.args = args;
|
|
1309
|
-
}
|
|
1310
|
-
await track(`${eventPrefix}.${transitionName}`, properties);
|
|
1206
|
+
after: ({ transitionName, prevContext, nextContext, args }) => {
|
|
1207
|
+
const event = `${eventPrefix}.${transitionName}`;
|
|
1208
|
+
const data = { transition: transitionName };
|
|
1209
|
+
if (includePrevContext) data.from = prevContext;
|
|
1210
|
+
data.to = nextContext;
|
|
1211
|
+
if (includeArgs) data.args = args;
|
|
1212
|
+
track(event, data);
|
|
1311
1213
|
}
|
|
1312
|
-
}
|
|
1214
|
+
});
|
|
1313
1215
|
}
|
|
1314
|
-
function withValidation(machine,
|
|
1216
|
+
function withValidation(machine, validator) {
|
|
1315
1217
|
return createMiddleware(machine, {
|
|
1316
1218
|
before: (ctx) => {
|
|
1317
|
-
const result =
|
|
1318
|
-
if (result instanceof Promise) {
|
|
1319
|
-
return result.then((r) => {
|
|
1320
|
-
if (r === false) {
|
|
1321
|
-
throw new Error(`Validation failed for transition: ${ctx.transitionName}`);
|
|
1322
|
-
}
|
|
1323
|
-
return void 0;
|
|
1324
|
-
});
|
|
1325
|
-
}
|
|
1219
|
+
const result = validator(ctx);
|
|
1326
1220
|
if (result === false) {
|
|
1327
1221
|
throw new Error(`Validation failed for transition: ${ctx.transitionName}`);
|
|
1328
1222
|
}
|
|
1329
|
-
return void 0;
|
|
1330
1223
|
}
|
|
1331
|
-
}
|
|
1224
|
+
});
|
|
1332
1225
|
}
|
|
1333
|
-
function withPermissions(machine,
|
|
1226
|
+
function withPermissions(machine, checker) {
|
|
1334
1227
|
return createMiddleware(machine, {
|
|
1335
1228
|
before: (ctx) => {
|
|
1336
|
-
|
|
1337
|
-
if (result instanceof Promise) {
|
|
1338
|
-
return result.then((allowed) => {
|
|
1339
|
-
if (!allowed) {
|
|
1340
|
-
throw new Error(`Unauthorized transition: ${ctx.transitionName}`);
|
|
1341
|
-
}
|
|
1342
|
-
return void 0;
|
|
1343
|
-
});
|
|
1344
|
-
}
|
|
1345
|
-
if (!result) {
|
|
1229
|
+
if (!checker(ctx)) {
|
|
1346
1230
|
throw new Error(`Unauthorized transition: ${ctx.transitionName}`);
|
|
1347
1231
|
}
|
|
1348
|
-
return void 0;
|
|
1349
1232
|
}
|
|
1350
|
-
}
|
|
1233
|
+
});
|
|
1351
1234
|
}
|
|
1352
|
-
function withErrorReporting(machine,
|
|
1353
|
-
const {
|
|
1235
|
+
function withErrorReporting(machine, reporter, options = {}) {
|
|
1236
|
+
const { includeArgs = false } = options;
|
|
1354
1237
|
return createMiddleware(machine, {
|
|
1355
|
-
error:
|
|
1356
|
-
const
|
|
1357
|
-
transition: transitionName
|
|
1238
|
+
error: (errorCtx) => {
|
|
1239
|
+
const formattedCtx = {
|
|
1240
|
+
transition: errorCtx.transitionName,
|
|
1241
|
+
context: errorCtx.context,
|
|
1242
|
+
...includeArgs && { args: errorCtx.args }
|
|
1358
1243
|
};
|
|
1359
|
-
|
|
1360
|
-
errorContext.context = context;
|
|
1361
|
-
}
|
|
1362
|
-
if (includeArgs && args.length > 0) {
|
|
1363
|
-
errorContext.args = args;
|
|
1364
|
-
}
|
|
1365
|
-
await Promise.resolve(captureError(error, errorContext));
|
|
1244
|
+
reporter(errorCtx.error, formattedCtx);
|
|
1366
1245
|
}
|
|
1367
|
-
}
|
|
1246
|
+
});
|
|
1368
1247
|
}
|
|
1369
|
-
function withPerformanceMonitoring(machine,
|
|
1370
|
-
const
|
|
1248
|
+
function withPerformanceMonitoring(machine, tracker) {
|
|
1249
|
+
const startTimes = /* @__PURE__ */ new Map();
|
|
1371
1250
|
return createMiddleware(machine, {
|
|
1372
|
-
before: (
|
|
1373
|
-
|
|
1374
|
-
return void 0;
|
|
1251
|
+
before: (ctx) => {
|
|
1252
|
+
startTimes.set(ctx.transitionName, Date.now());
|
|
1375
1253
|
},
|
|
1376
|
-
after: (
|
|
1377
|
-
const startTime =
|
|
1254
|
+
after: (result) => {
|
|
1255
|
+
const startTime = startTimes.get(result.transitionName);
|
|
1378
1256
|
if (startTime) {
|
|
1379
|
-
const duration =
|
|
1380
|
-
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1257
|
+
const duration = Date.now() - startTime;
|
|
1258
|
+
startTimes.delete(result.transitionName);
|
|
1259
|
+
const testResult = {
|
|
1260
|
+
transitionName: result.transitionName,
|
|
1261
|
+
duration,
|
|
1262
|
+
context: result.nextContext || result.prevContext
|
|
1263
|
+
};
|
|
1264
|
+
tracker(testResult);
|
|
1385
1265
|
}
|
|
1386
|
-
return void 0;
|
|
1387
1266
|
}
|
|
1388
|
-
}
|
|
1267
|
+
});
|
|
1389
1268
|
}
|
|
1390
1269
|
function withRetry(machine, options = {}) {
|
|
1270
|
+
var _a, _b;
|
|
1391
1271
|
const {
|
|
1392
|
-
|
|
1393
|
-
delay = 1e3,
|
|
1394
|
-
backoffMultiplier = 1,
|
|
1272
|
+
maxAttempts = (_a = options.maxRetries) != null ? _a : 3,
|
|
1395
1273
|
shouldRetry = () => true,
|
|
1274
|
+
backoffMs = (_b = options.delay) != null ? _b : 100,
|
|
1275
|
+
backoffMultiplier = 2,
|
|
1396
1276
|
onRetry
|
|
1397
1277
|
} = options;
|
|
1398
|
-
const
|
|
1278
|
+
const wrappedMachine = { ...machine };
|
|
1399
1279
|
for (const prop in machine) {
|
|
1400
1280
|
if (!Object.prototype.hasOwnProperty.call(machine, prop)) continue;
|
|
1401
1281
|
const value = machine[prop];
|
|
1402
|
-
if (
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1282
|
+
if (typeof value === "function" && prop !== "context") {
|
|
1283
|
+
wrappedMachine[prop] = async function(...args) {
|
|
1284
|
+
let lastError;
|
|
1285
|
+
let attempt = 0;
|
|
1286
|
+
while (attempt < maxAttempts) {
|
|
1287
|
+
try {
|
|
1288
|
+
return await value.apply(this, args);
|
|
1289
|
+
} catch (error) {
|
|
1290
|
+
lastError = error;
|
|
1291
|
+
attempt++;
|
|
1292
|
+
if (attempt < maxAttempts && shouldRetry(lastError, attempt)) {
|
|
1293
|
+
onRetry == null ? void 0 : onRetry(lastError, attempt);
|
|
1294
|
+
const baseDelay = typeof backoffMs === "function" ? backoffMs(attempt) : backoffMs;
|
|
1295
|
+
const delay = baseDelay * Math.pow(backoffMultiplier, attempt - 1);
|
|
1296
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1297
|
+
} else {
|
|
1298
|
+
throw lastError;
|
|
1299
|
+
}
|
|
1418
1300
|
}
|
|
1419
|
-
onRetry == null ? void 0 : onRetry(attempt + 1, lastError);
|
|
1420
|
-
const currentDelay = delay * Math.pow(backoffMultiplier, attempt);
|
|
1421
|
-
await new Promise((resolve) => setTimeout(resolve, currentDelay));
|
|
1422
1301
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
}
|
|
1302
|
+
throw lastError;
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1426
1305
|
}
|
|
1427
|
-
return
|
|
1306
|
+
return wrappedMachine;
|
|
1428
1307
|
}
|
|
1308
|
+
function createCustomMiddleware(hooks, options) {
|
|
1309
|
+
return (machine) => createMiddleware(machine, hooks, options);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// src/middleware/history.ts
|
|
1429
1313
|
function withHistory(machine, options = {}) {
|
|
1430
|
-
const {
|
|
1431
|
-
maxSize,
|
|
1432
|
-
serializer,
|
|
1433
|
-
filter,
|
|
1434
|
-
onEntry,
|
|
1435
|
-
_isRewrap = false
|
|
1436
|
-
} = options;
|
|
1314
|
+
const { maxSize, serializer, onEntry } = options;
|
|
1437
1315
|
const history = [];
|
|
1438
1316
|
let entryId = 0;
|
|
1439
1317
|
const instrumentedMachine = createMiddleware(machine, {
|
|
1440
1318
|
before: ({ transitionName, args }) => {
|
|
1441
|
-
if (filter && !filter(transitionName, args)) {
|
|
1442
|
-
return;
|
|
1443
|
-
}
|
|
1444
1319
|
const entry = {
|
|
1445
1320
|
id: `entry-${entryId++}`,
|
|
1446
1321
|
transitionName,
|
|
1447
1322
|
args: [...args],
|
|
1448
|
-
// Shallow clone args (fast, works with any type)
|
|
1449
1323
|
timestamp: Date.now()
|
|
1450
1324
|
};
|
|
1451
1325
|
if (serializer) {
|
|
@@ -1461,54 +1335,7 @@ function withHistory(machine, options = {}) {
|
|
|
1461
1335
|
}
|
|
1462
1336
|
onEntry == null ? void 0 : onEntry(entry);
|
|
1463
1337
|
}
|
|
1464
|
-
}
|
|
1465
|
-
if (!_isRewrap) {
|
|
1466
|
-
for (const prop in instrumentedMachine) {
|
|
1467
|
-
if (!Object.prototype.hasOwnProperty.call(instrumentedMachine, prop)) continue;
|
|
1468
|
-
const value = instrumentedMachine[prop];
|
|
1469
|
-
if (typeof value === "function" && !prop.startsWith("_") && prop !== "context" && !["history", "clearHistory"].includes(prop)) {
|
|
1470
|
-
const originalFn = value;
|
|
1471
|
-
instrumentedMachine[prop] = function(...args) {
|
|
1472
|
-
const result = originalFn.apply(this, args);
|
|
1473
|
-
if (result && typeof result === "object" && "context" in result && !("history" in result)) {
|
|
1474
|
-
const rewrappedResult = createMiddleware(result, {
|
|
1475
|
-
before: ({ transitionName, args: transArgs }) => {
|
|
1476
|
-
if (filter && !filter(transitionName, transArgs)) {
|
|
1477
|
-
return;
|
|
1478
|
-
}
|
|
1479
|
-
const entry = {
|
|
1480
|
-
id: `entry-${entryId++}`,
|
|
1481
|
-
transitionName,
|
|
1482
|
-
args: [...transArgs],
|
|
1483
|
-
timestamp: Date.now()
|
|
1484
|
-
};
|
|
1485
|
-
if (serializer) {
|
|
1486
|
-
try {
|
|
1487
|
-
entry.serializedArgs = serializer.serialize(transArgs);
|
|
1488
|
-
} catch (err) {
|
|
1489
|
-
console.error("Failed to serialize history args:", err);
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
history.push(entry);
|
|
1493
|
-
if (maxSize && history.length > maxSize) {
|
|
1494
|
-
history.shift();
|
|
1495
|
-
}
|
|
1496
|
-
onEntry == null ? void 0 : onEntry(entry);
|
|
1497
|
-
}
|
|
1498
|
-
}, { exclude: ["context", "history", "clearHistory"] });
|
|
1499
|
-
return Object.assign(rewrappedResult, {
|
|
1500
|
-
history,
|
|
1501
|
-
clearHistory: () => {
|
|
1502
|
-
history.length = 0;
|
|
1503
|
-
entryId = 0;
|
|
1504
|
-
}
|
|
1505
|
-
});
|
|
1506
|
-
}
|
|
1507
|
-
return result;
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1338
|
+
});
|
|
1512
1339
|
return Object.assign(instrumentedMachine, {
|
|
1513
1340
|
history,
|
|
1514
1341
|
clearHistory: () => {
|
|
@@ -1517,37 +1344,27 @@ function withHistory(machine, options = {}) {
|
|
|
1517
1344
|
}
|
|
1518
1345
|
});
|
|
1519
1346
|
}
|
|
1347
|
+
|
|
1348
|
+
// src/middleware/snapshot.ts
|
|
1520
1349
|
function withSnapshot(machine, options = {}) {
|
|
1521
1350
|
const {
|
|
1522
1351
|
maxSize,
|
|
1523
1352
|
serializer,
|
|
1524
1353
|
captureSnapshot,
|
|
1525
|
-
|
|
1526
|
-
filter,
|
|
1527
|
-
onSnapshot,
|
|
1528
|
-
_extraExclusions = [],
|
|
1529
|
-
_isRewrap = false
|
|
1354
|
+
onlyOnChange = false
|
|
1530
1355
|
} = options;
|
|
1531
1356
|
const snapshots = [];
|
|
1532
1357
|
let snapshotId = 0;
|
|
1533
1358
|
const instrumentedMachine = createMiddleware(machine, {
|
|
1534
1359
|
after: ({ transitionName, prevContext, nextContext }) => {
|
|
1535
|
-
if (
|
|
1360
|
+
if (onlyOnChange && JSON.stringify(prevContext) === JSON.stringify(nextContext)) {
|
|
1536
1361
|
return;
|
|
1537
1362
|
}
|
|
1538
|
-
if (onlyIfChanged) {
|
|
1539
|
-
const changed = JSON.stringify(prevContext) !== JSON.stringify(nextContext);
|
|
1540
|
-
if (!changed) {
|
|
1541
|
-
return;
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
1363
|
const snapshot = {
|
|
1545
1364
|
id: `snapshot-${snapshotId++}`,
|
|
1546
1365
|
transitionName,
|
|
1547
1366
|
before: { ...prevContext },
|
|
1548
|
-
// Clone
|
|
1549
1367
|
after: { ...nextContext },
|
|
1550
|
-
// Clone
|
|
1551
1368
|
timestamp: Date.now()
|
|
1552
1369
|
};
|
|
1553
1370
|
if (serializer) {
|
|
@@ -1569,92 +1386,16 @@ function withSnapshot(machine, options = {}) {
|
|
|
1569
1386
|
if (maxSize && snapshots.length > maxSize) {
|
|
1570
1387
|
snapshots.shift();
|
|
1571
1388
|
}
|
|
1572
|
-
onSnapshot == null ? void 0 : onSnapshot(snapshot);
|
|
1573
1389
|
}
|
|
1574
|
-
}
|
|
1390
|
+
});
|
|
1575
1391
|
const restoreSnapshot = (context) => {
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1392
|
+
const transitions = Object.fromEntries(
|
|
1393
|
+
Object.entries(machine).filter(
|
|
1394
|
+
([key]) => key !== "context" && key !== "snapshots" && key !== "clearSnapshots" && key !== "restoreSnapshot" && typeof machine[key] === "function"
|
|
1395
|
+
)
|
|
1396
|
+
);
|
|
1397
|
+
return Object.assign({ context }, transitions);
|
|
1578
1398
|
};
|
|
1579
|
-
if (!_isRewrap) {
|
|
1580
|
-
for (const prop in instrumentedMachine) {
|
|
1581
|
-
if (!Object.prototype.hasOwnProperty.call(instrumentedMachine, prop)) continue;
|
|
1582
|
-
const value = instrumentedMachine[prop];
|
|
1583
|
-
if (typeof value === "function" && !prop.startsWith("_") && prop !== "context" && !["snapshots", "clearSnapshots", "restoreSnapshot", "history", "clearHistory"].includes(prop)) {
|
|
1584
|
-
const originalWrappedFn = value;
|
|
1585
|
-
instrumentedMachine[prop] = function(...args) {
|
|
1586
|
-
const result = originalWrappedFn.apply(this, args);
|
|
1587
|
-
if (result && typeof result === "object" && "context" in result && !("snapshots" in result)) {
|
|
1588
|
-
for (const transProp in result) {
|
|
1589
|
-
if (!Object.prototype.hasOwnProperty.call(result, transProp)) continue;
|
|
1590
|
-
const transValue = result[transProp];
|
|
1591
|
-
if (typeof transValue === "function" && !transProp.startsWith("_") && transProp !== "context" && !["snapshots", "clearSnapshots", "restoreSnapshot", "history", "clearHistory"].includes(transProp)) {
|
|
1592
|
-
const origTransFn = transValue;
|
|
1593
|
-
result[transProp] = function(...transArgs) {
|
|
1594
|
-
const prevCtx = result.context;
|
|
1595
|
-
const transResult = origTransFn.apply(this, transArgs);
|
|
1596
|
-
if (transResult && typeof transResult === "object" && "context" in transResult) {
|
|
1597
|
-
const nextCtx = transResult.context;
|
|
1598
|
-
if (!(filter && !filter(transProp))) {
|
|
1599
|
-
let shouldRecord = true;
|
|
1600
|
-
if (onlyIfChanged) {
|
|
1601
|
-
const changed = JSON.stringify(prevCtx) !== JSON.stringify(nextCtx);
|
|
1602
|
-
shouldRecord = changed;
|
|
1603
|
-
}
|
|
1604
|
-
if (shouldRecord) {
|
|
1605
|
-
const snapshot = {
|
|
1606
|
-
id: `snapshot-${snapshotId++}`,
|
|
1607
|
-
transitionName: transProp,
|
|
1608
|
-
before: { ...prevCtx },
|
|
1609
|
-
after: { ...nextCtx },
|
|
1610
|
-
timestamp: Date.now()
|
|
1611
|
-
};
|
|
1612
|
-
if (serializer) {
|
|
1613
|
-
try {
|
|
1614
|
-
snapshot.serializedBefore = serializer.serialize(prevCtx);
|
|
1615
|
-
snapshot.serializedAfter = serializer.serialize(nextCtx);
|
|
1616
|
-
} catch (err) {
|
|
1617
|
-
console.error("Failed to serialize snapshot:", err);
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
if (captureSnapshot) {
|
|
1621
|
-
try {
|
|
1622
|
-
snapshot.diff = captureSnapshot(prevCtx, nextCtx);
|
|
1623
|
-
} catch (err) {
|
|
1624
|
-
console.error("Failed to capture snapshot:", err);
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
snapshots.push(snapshot);
|
|
1628
|
-
if (maxSize && snapshots.length > maxSize) {
|
|
1629
|
-
snapshots.shift();
|
|
1630
|
-
}
|
|
1631
|
-
onSnapshot == null ? void 0 : onSnapshot(snapshot);
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
}
|
|
1635
|
-
return transResult;
|
|
1636
|
-
};
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
const resultWithTracking = Object.assign(result, {
|
|
1640
|
-
snapshots,
|
|
1641
|
-
clearSnapshots: () => {
|
|
1642
|
-
snapshots.length = 0;
|
|
1643
|
-
snapshotId = 0;
|
|
1644
|
-
},
|
|
1645
|
-
restoreSnapshot
|
|
1646
|
-
});
|
|
1647
|
-
if (machine.history) {
|
|
1648
|
-
resultWithTracking.history = machine.history;
|
|
1649
|
-
resultWithTracking.clearHistory = machine.clearHistory;
|
|
1650
|
-
}
|
|
1651
|
-
return resultWithTracking;
|
|
1652
|
-
}
|
|
1653
|
-
return result;
|
|
1654
|
-
};
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
1399
|
return Object.assign(instrumentedMachine, {
|
|
1659
1400
|
snapshots,
|
|
1660
1401
|
clearSnapshots: () => {
|
|
@@ -1664,139 +1405,122 @@ function withSnapshot(machine, options = {}) {
|
|
|
1664
1405
|
restoreSnapshot
|
|
1665
1406
|
});
|
|
1666
1407
|
}
|
|
1408
|
+
|
|
1409
|
+
// src/middleware/time-travel.ts
|
|
1667
1410
|
function withTimeTravel(machine, options = {}) {
|
|
1668
1411
|
const { maxSize, serializer, onRecord } = options;
|
|
1669
1412
|
const history = [];
|
|
1670
1413
|
const snapshots = [];
|
|
1671
|
-
let
|
|
1414
|
+
let historyId = 0;
|
|
1672
1415
|
let snapshotId = 0;
|
|
1673
|
-
const
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1416
|
+
const instrumentedMachine = createMiddleware(machine, {
|
|
1417
|
+
before: ({ transitionName, args }) => {
|
|
1418
|
+
const entry = {
|
|
1419
|
+
id: `entry-${historyId++}`,
|
|
1420
|
+
transitionName,
|
|
1421
|
+
args: [...args],
|
|
1422
|
+
timestamp: Date.now()
|
|
1423
|
+
};
|
|
1424
|
+
if (serializer) {
|
|
1425
|
+
try {
|
|
1426
|
+
entry.serializedArgs = serializer.serialize(args);
|
|
1427
|
+
} catch (err) {
|
|
1428
|
+
console.error("Failed to serialize history args:", err);
|
|
1429
|
+
}
|
|
1685
1430
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
history.shift();
|
|
1690
|
-
}
|
|
1691
|
-
onRecord == null ? void 0 : onRecord("history", entry);
|
|
1692
|
-
};
|
|
1693
|
-
const recordSnapshot = (transitionName, prevContext, nextContext) => {
|
|
1694
|
-
const snapshot = {
|
|
1695
|
-
id: `snapshot-${snapshotId++}`,
|
|
1696
|
-
transitionName,
|
|
1697
|
-
before: { ...prevContext },
|
|
1698
|
-
after: { ...nextContext },
|
|
1699
|
-
timestamp: Date.now()
|
|
1700
|
-
};
|
|
1701
|
-
if (serializer) {
|
|
1702
|
-
try {
|
|
1703
|
-
snapshot.serializedBefore = serializer.serialize(prevContext);
|
|
1704
|
-
snapshot.serializedAfter = serializer.serialize(nextContext);
|
|
1705
|
-
} catch (err) {
|
|
1706
|
-
console.error("Failed to serialize snapshot:", err);
|
|
1431
|
+
history.push(entry);
|
|
1432
|
+
if (maxSize && history.length > maxSize) {
|
|
1433
|
+
history.shift();
|
|
1707
1434
|
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
const replayFrom = (snapshotIndex = 0) => {
|
|
1720
|
-
if (snapshotIndex < 0 || snapshotIndex >= snapshots.length) {
|
|
1721
|
-
throw new Error(`Invalid snapshot index: ${snapshotIndex}`);
|
|
1722
|
-
}
|
|
1723
|
-
let current = restoreSnapshot(snapshots[snapshotIndex].before);
|
|
1724
|
-
const snapshot = snapshots[snapshotIndex];
|
|
1725
|
-
const historyStartIndex = history.findIndex(
|
|
1726
|
-
(entry) => entry.transitionName === snapshot.transitionName && entry.timestamp === snapshot.timestamp
|
|
1727
|
-
);
|
|
1728
|
-
if (historyStartIndex === -1) {
|
|
1729
|
-
throw new Error("Could not find matching history entry for snapshot");
|
|
1730
|
-
}
|
|
1731
|
-
for (let i = historyStartIndex; i < history.length; i++) {
|
|
1732
|
-
const entry = history[i];
|
|
1733
|
-
const transition = current[entry.transitionName];
|
|
1734
|
-
if (typeof transition === "function") {
|
|
1435
|
+
onRecord == null ? void 0 : onRecord("history", entry);
|
|
1436
|
+
},
|
|
1437
|
+
after: ({ transitionName, prevContext, nextContext }) => {
|
|
1438
|
+
const snapshot = {
|
|
1439
|
+
id: `snapshot-${snapshotId++}`,
|
|
1440
|
+
transitionName,
|
|
1441
|
+
before: { ...prevContext },
|
|
1442
|
+
after: { ...nextContext },
|
|
1443
|
+
timestamp: Date.now()
|
|
1444
|
+
};
|
|
1445
|
+
if (serializer) {
|
|
1735
1446
|
try {
|
|
1736
|
-
|
|
1447
|
+
snapshot.serializedBefore = serializer.serialize(prevContext);
|
|
1448
|
+
snapshot.serializedAfter = serializer.serialize(nextContext);
|
|
1737
1449
|
} catch (err) {
|
|
1738
|
-
console.error(
|
|
1739
|
-
throw err;
|
|
1450
|
+
console.error("Failed to serialize snapshot:", err);
|
|
1740
1451
|
}
|
|
1741
1452
|
}
|
|
1453
|
+
snapshots.push(snapshot);
|
|
1454
|
+
if (maxSize && snapshots.length > maxSize) {
|
|
1455
|
+
snapshots.shift();
|
|
1456
|
+
}
|
|
1457
|
+
onRecord == null ? void 0 : onRecord("snapshot", snapshot);
|
|
1742
1458
|
}
|
|
1743
|
-
|
|
1459
|
+
});
|
|
1460
|
+
const restoreSnapshot = (context) => {
|
|
1461
|
+
const transitions = Object.fromEntries(
|
|
1462
|
+
Object.entries(machine).filter(
|
|
1463
|
+
([key]) => key !== "context" && key !== "history" && key !== "snapshots" && key !== "clearHistory" && key !== "clearSnapshots" && key !== "restoreSnapshot" && key !== "clearTimeTravel" && key !== "replayFrom" && typeof machine[key] === "function"
|
|
1464
|
+
)
|
|
1465
|
+
);
|
|
1466
|
+
return Object.assign({ context }, transitions);
|
|
1744
1467
|
};
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1468
|
+
const replayFrom = (startIndex) => {
|
|
1469
|
+
var _a;
|
|
1470
|
+
if (startIndex < 0 || startIndex >= history.length) {
|
|
1471
|
+
throw new Error(`Invalid replay start index: ${startIndex}`);
|
|
1472
|
+
}
|
|
1473
|
+
let currentContext = (_a = snapshots[startIndex]) == null ? void 0 : _a.before;
|
|
1474
|
+
if (!currentContext) {
|
|
1475
|
+
throw new Error(`No snapshot available for index ${startIndex}`);
|
|
1476
|
+
}
|
|
1477
|
+
const transitionsToReplay = history.slice(startIndex);
|
|
1478
|
+
const freshMachine = Object.assign(
|
|
1479
|
+
{ context: currentContext },
|
|
1480
|
+
Object.fromEntries(
|
|
1481
|
+
Object.entries(machine).filter(
|
|
1482
|
+
([key]) => key !== "context" && typeof machine[key] === "function"
|
|
1483
|
+
)
|
|
1484
|
+
)
|
|
1485
|
+
);
|
|
1486
|
+
let replayedMachine = freshMachine;
|
|
1487
|
+
for (const entry of transitionsToReplay) {
|
|
1488
|
+
const transitionFn = replayedMachine[entry.transitionName];
|
|
1489
|
+
if (transitionFn) {
|
|
1490
|
+
replayedMachine = transitionFn.apply(replayedMachine.context, entry.args);
|
|
1763
1491
|
}
|
|
1764
1492
|
}
|
|
1765
|
-
return
|
|
1766
|
-
history,
|
|
1767
|
-
snapshots,
|
|
1768
|
-
clearHistory: () => {
|
|
1769
|
-
history.length = 0;
|
|
1770
|
-
entryId = 0;
|
|
1771
|
-
},
|
|
1772
|
-
clearSnapshots: () => {
|
|
1773
|
-
snapshots.length = 0;
|
|
1774
|
-
snapshotId = 0;
|
|
1775
|
-
},
|
|
1776
|
-
clearTimeTravel: () => {
|
|
1777
|
-
history.length = 0;
|
|
1778
|
-
snapshots.length = 0;
|
|
1779
|
-
entryId = 0;
|
|
1780
|
-
snapshotId = 0;
|
|
1781
|
-
},
|
|
1782
|
-
restoreSnapshot,
|
|
1783
|
-
replayFrom
|
|
1784
|
-
});
|
|
1493
|
+
return replayedMachine;
|
|
1785
1494
|
};
|
|
1786
|
-
return
|
|
1495
|
+
return Object.assign(instrumentedMachine, {
|
|
1496
|
+
history,
|
|
1497
|
+
snapshots,
|
|
1498
|
+
clearHistory: () => {
|
|
1499
|
+
history.length = 0;
|
|
1500
|
+
historyId = 0;
|
|
1501
|
+
},
|
|
1502
|
+
clearSnapshots: () => {
|
|
1503
|
+
snapshots.length = 0;
|
|
1504
|
+
snapshotId = 0;
|
|
1505
|
+
},
|
|
1506
|
+
clearTimeTravel: () => {
|
|
1507
|
+
history.length = 0;
|
|
1508
|
+
snapshots.length = 0;
|
|
1509
|
+
historyId = 0;
|
|
1510
|
+
snapshotId = 0;
|
|
1511
|
+
},
|
|
1512
|
+
restoreSnapshot,
|
|
1513
|
+
replayFrom
|
|
1514
|
+
});
|
|
1787
1515
|
}
|
|
1516
|
+
|
|
1517
|
+
// src/middleware/composition.ts
|
|
1788
1518
|
function compose(machine, ...middlewares) {
|
|
1789
1519
|
return middlewares.reduce((acc, middleware) => middleware(acc), machine);
|
|
1790
1520
|
}
|
|
1791
|
-
function createCustomMiddleware(hooks, options) {
|
|
1792
|
-
return (machine) => createMiddleware(machine, hooks, options);
|
|
1793
|
-
}
|
|
1794
1521
|
function composeTyped(machine, ...middlewares) {
|
|
1795
1522
|
return middlewares.reduce((acc, middleware) => middleware(acc), machine);
|
|
1796
1523
|
}
|
|
1797
|
-
function chain(machine) {
|
|
1798
|
-
return new MiddlewareChainBuilder(machine);
|
|
1799
|
-
}
|
|
1800
1524
|
var MiddlewareChainBuilder = class _MiddlewareChainBuilder {
|
|
1801
1525
|
constructor(machine) {
|
|
1802
1526
|
this.machine = machine;
|
|
@@ -1817,53 +1541,30 @@ var MiddlewareChainBuilder = class _MiddlewareChainBuilder {
|
|
|
1817
1541
|
return this.machine;
|
|
1818
1542
|
}
|
|
1819
1543
|
};
|
|
1820
|
-
function
|
|
1821
|
-
return
|
|
1544
|
+
function chain(machine) {
|
|
1545
|
+
return new MiddlewareChainBuilder(machine);
|
|
1822
1546
|
}
|
|
1823
|
-
function
|
|
1824
|
-
const {
|
|
1825
|
-
|
|
1826
|
-
logErrors = true,
|
|
1827
|
-
onError
|
|
1828
|
-
} = config;
|
|
1829
|
-
return (machine, ...middlewares) => {
|
|
1830
|
-
let currentMachine = machine;
|
|
1831
|
-
const errors = [];
|
|
1832
|
-
for (let i = 0; i < middlewares.length; i++) {
|
|
1833
|
-
const middleware = middlewares[i];
|
|
1834
|
-
try {
|
|
1835
|
-
if ("middleware" in middleware && "when" in middleware) {
|
|
1836
|
-
if (!middleware.when(currentMachine)) {
|
|
1837
|
-
continue;
|
|
1838
|
-
}
|
|
1839
|
-
currentMachine = middleware.middleware(currentMachine);
|
|
1840
|
-
} else {
|
|
1841
|
-
currentMachine = middleware(currentMachine);
|
|
1842
|
-
}
|
|
1843
|
-
} catch (error) {
|
|
1844
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
1845
|
-
errors.push({ error: err, middlewareIndex: i });
|
|
1846
|
-
if (logErrors) {
|
|
1847
|
-
console.error(`Middleware pipeline error at index ${i}:`, err);
|
|
1848
|
-
}
|
|
1849
|
-
onError == null ? void 0 : onError(err, `middleware-${i}`);
|
|
1850
|
-
if (!continueOnError) {
|
|
1851
|
-
break;
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
return {
|
|
1856
|
-
machine: currentMachine,
|
|
1857
|
-
errors,
|
|
1858
|
-
success: errors.length === 0
|
|
1859
|
-
};
|
|
1547
|
+
function when(middleware, predicate) {
|
|
1548
|
+
const conditional = function(machine) {
|
|
1549
|
+
return predicate(machine) ? middleware(machine) : machine;
|
|
1860
1550
|
};
|
|
1551
|
+
conditional.middleware = middleware;
|
|
1552
|
+
conditional.when = predicate;
|
|
1553
|
+
return conditional;
|
|
1554
|
+
}
|
|
1555
|
+
function inDevelopment(middleware) {
|
|
1556
|
+
return when(middleware, () => {
|
|
1557
|
+
return typeof process !== "undefined" ? true : typeof window !== "undefined" ? !window.location.hostname.includes("production") : false;
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
function whenContext(key, value, middleware) {
|
|
1561
|
+
return when(middleware, (machine) => machine.context[key] === value);
|
|
1861
1562
|
}
|
|
1862
1563
|
function createMiddlewareRegistry() {
|
|
1863
1564
|
const registry = /* @__PURE__ */ new Map();
|
|
1864
1565
|
return {
|
|
1865
1566
|
/**
|
|
1866
|
-
* Register a middleware
|
|
1567
|
+
* Register a middleware by name.
|
|
1867
1568
|
*/
|
|
1868
1569
|
register(name, middleware, description, priority) {
|
|
1869
1570
|
if (registry.has(name)) {
|
|
@@ -1925,21 +1626,45 @@ function createMiddlewareRegistry() {
|
|
|
1925
1626
|
}
|
|
1926
1627
|
};
|
|
1927
1628
|
}
|
|
1928
|
-
function
|
|
1929
|
-
const
|
|
1930
|
-
|
|
1629
|
+
function createPipeline(config = {}) {
|
|
1630
|
+
const {
|
|
1631
|
+
continueOnError = false,
|
|
1632
|
+
logErrors = true,
|
|
1633
|
+
onError
|
|
1634
|
+
} = config;
|
|
1635
|
+
return (machine, ...middlewares) => {
|
|
1636
|
+
let currentMachine = machine;
|
|
1637
|
+
const errors = [];
|
|
1638
|
+
let success = true;
|
|
1639
|
+
for (let i = 0; i < middlewares.length; i++) {
|
|
1640
|
+
const middleware = middlewares[i];
|
|
1641
|
+
try {
|
|
1642
|
+
if ("middleware" in middleware && "when" in middleware) {
|
|
1643
|
+
if (!middleware.when(currentMachine)) {
|
|
1644
|
+
continue;
|
|
1645
|
+
}
|
|
1646
|
+
currentMachine = middleware.middleware(currentMachine);
|
|
1647
|
+
} else {
|
|
1648
|
+
currentMachine = middleware(currentMachine);
|
|
1649
|
+
}
|
|
1650
|
+
} catch (error) {
|
|
1651
|
+
success = false;
|
|
1652
|
+
if (!continueOnError) {
|
|
1653
|
+
throw error;
|
|
1654
|
+
}
|
|
1655
|
+
errors.push({
|
|
1656
|
+
error,
|
|
1657
|
+
middlewareIndex: i,
|
|
1658
|
+
middlewareName: middleware.name
|
|
1659
|
+
});
|
|
1660
|
+
if (logErrors) {
|
|
1661
|
+
console.error(`Pipeline middleware error at index ${i}:`, error);
|
|
1662
|
+
}
|
|
1663
|
+
onError == null ? void 0 : onError(error, i, middleware.name);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
return { machine: currentMachine, errors, success };
|
|
1931
1667
|
};
|
|
1932
|
-
conditional.middleware = middleware;
|
|
1933
|
-
conditional.when = predicate;
|
|
1934
|
-
return conditional;
|
|
1935
|
-
}
|
|
1936
|
-
function inDevelopment(middleware) {
|
|
1937
|
-
return when(middleware, () => {
|
|
1938
|
-
return typeof process !== "undefined" ? true : typeof window !== "undefined" ? !window.location.hostname.includes("production") : false;
|
|
1939
|
-
});
|
|
1940
|
-
}
|
|
1941
|
-
function whenContext(key, value, middleware) {
|
|
1942
|
-
return when(middleware, (machine) => machine.context[key] === value);
|
|
1943
1668
|
}
|
|
1944
1669
|
function combine(...middlewares) {
|
|
1945
1670
|
return (machine) => composeTyped(machine, ...middlewares);
|
|
@@ -1958,7 +1683,197 @@ function isMiddlewareFn(value) {
|
|
|
1958
1683
|
return typeof value === "function" && value.length === 1;
|
|
1959
1684
|
}
|
|
1960
1685
|
function isConditionalMiddleware(value) {
|
|
1961
|
-
return value !== null && "middleware" in value && "when" in value && isMiddlewareFn(value.middleware) && typeof value.when === "function";
|
|
1686
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && "middleware" in value && "when" in value && isMiddlewareFn(value.middleware) && typeof value.when === "function";
|
|
1687
|
+
}
|
|
1688
|
+
function isMiddlewareResult(value, contextType) {
|
|
1689
|
+
return value !== null && typeof value === "object" && "transitionName" in value && "prevContext" in value && "nextContext" in value && "args" in value && typeof value.transitionName === "string" && Array.isArray(value.args) && (!contextType || isValidContext(value.prevContext, contextType) && isValidContext(value.nextContext, contextType));
|
|
1690
|
+
}
|
|
1691
|
+
function isMiddlewareContext(value, contextType) {
|
|
1692
|
+
return value !== null && typeof value === "object" && "transitionName" in value && "context" in value && "args" in value && typeof value.transitionName === "string" && Array.isArray(value.args) && (!contextType || isValidContext(value.context, contextType));
|
|
1693
|
+
}
|
|
1694
|
+
function isMiddlewareError(value, contextType) {
|
|
1695
|
+
return value !== null && typeof value === "object" && "transitionName" in value && "context" in value && "args" in value && "error" in value && typeof value.transitionName === "string" && Array.isArray(value.args) && value.error instanceof Error && (!contextType || isValidContext(value.context, contextType));
|
|
1696
|
+
}
|
|
1697
|
+
function isMiddlewareHooks(value, _contextType) {
|
|
1698
|
+
if (value === null || typeof value !== "object") return false;
|
|
1699
|
+
const hooks = value;
|
|
1700
|
+
if ("before" in hooks && hooks.before !== void 0) {
|
|
1701
|
+
if (typeof hooks.before !== "function") return false;
|
|
1702
|
+
}
|
|
1703
|
+
if ("after" in hooks && hooks.after !== void 0) {
|
|
1704
|
+
if (typeof hooks.after !== "function") return false;
|
|
1705
|
+
}
|
|
1706
|
+
if ("error" in hooks && hooks.error !== void 0) {
|
|
1707
|
+
if (typeof hooks.error !== "function") return false;
|
|
1708
|
+
}
|
|
1709
|
+
return true;
|
|
1710
|
+
}
|
|
1711
|
+
function isMiddlewareOptions(value) {
|
|
1712
|
+
return value === void 0 || value !== null && typeof value === "object" && ("continueOnError" in value ? typeof value.continueOnError === "boolean" : true) && ("logErrors" in value ? typeof value.logErrors === "boolean" : true) && ("onError" in value ? typeof value.onError === "function" || value.onError === void 0 : true);
|
|
1713
|
+
}
|
|
1714
|
+
function isValidContext(value, _contextType) {
|
|
1715
|
+
return value !== null && typeof value === "object";
|
|
1716
|
+
}
|
|
1717
|
+
function isNamedMiddleware(value) {
|
|
1718
|
+
return value !== null && typeof value === "object" && "name" in value && "middleware" in value && typeof value.name === "string" && isMiddlewareFn(value.middleware) && ("description" in value ? typeof value.description === "string" || value.description === void 0 : true) && ("priority" in value ? typeof value.priority === "number" || value.priority === void 0 : true);
|
|
1719
|
+
}
|
|
1720
|
+
function isPipelineConfig(value) {
|
|
1721
|
+
return value === void 0 || value !== null && typeof value === "object" && ("continueOnError" in value ? typeof value.continueOnError === "boolean" : true) && ("logErrors" in value ? typeof value.logErrors === "boolean" : true) && ("onError" in value ? typeof value.onError === "function" || value.onError === void 0 : true);
|
|
1722
|
+
}
|
|
1723
|
+
var MiddlewareBuilder = class {
|
|
1724
|
+
constructor(machine) {
|
|
1725
|
+
this.machine = machine;
|
|
1726
|
+
this.middlewares = [];
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Add logging middleware with type-safe configuration.
|
|
1730
|
+
*/
|
|
1731
|
+
withLogging(options) {
|
|
1732
|
+
this.middlewares.push((machine) => withLogging(machine, options));
|
|
1733
|
+
return this;
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1736
|
+
* Add analytics middleware with type-safe configuration.
|
|
1737
|
+
*/
|
|
1738
|
+
withAnalytics(track, options) {
|
|
1739
|
+
this.middlewares.push((machine) => withAnalytics(machine, track, options));
|
|
1740
|
+
return this;
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* Add validation middleware with type-safe configuration.
|
|
1744
|
+
*/
|
|
1745
|
+
withValidation(validator, _options) {
|
|
1746
|
+
this.middlewares.push((machine) => withValidation(machine, validator));
|
|
1747
|
+
return this;
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* Add permission checking middleware with type-safe configuration.
|
|
1751
|
+
*/
|
|
1752
|
+
withPermissions(checker) {
|
|
1753
|
+
this.middlewares.push((machine) => withPermissions(machine, checker));
|
|
1754
|
+
return this;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Add error reporting middleware with type-safe configuration.
|
|
1758
|
+
*/
|
|
1759
|
+
withErrorReporting(reporter, options) {
|
|
1760
|
+
this.middlewares.push((machine) => withErrorReporting(machine, reporter, options));
|
|
1761
|
+
return this;
|
|
1762
|
+
}
|
|
1763
|
+
/**
|
|
1764
|
+
* Add performance monitoring middleware with type-safe configuration.
|
|
1765
|
+
*/
|
|
1766
|
+
withPerformanceMonitoring(tracker, _options) {
|
|
1767
|
+
this.middlewares.push((machine) => withPerformanceMonitoring(machine, tracker));
|
|
1768
|
+
return this;
|
|
1769
|
+
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Add retry middleware with type-safe configuration.
|
|
1772
|
+
*/
|
|
1773
|
+
withRetry(options) {
|
|
1774
|
+
this.middlewares.push((machine) => withRetry(machine, options));
|
|
1775
|
+
return this;
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Add history tracking middleware with type-safe configuration.
|
|
1779
|
+
*/
|
|
1780
|
+
withHistory(options) {
|
|
1781
|
+
this.middlewares.push((machine) => withHistory(machine, options));
|
|
1782
|
+
return this;
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* Add snapshot tracking middleware with type-safe configuration.
|
|
1786
|
+
*/
|
|
1787
|
+
withSnapshot(options) {
|
|
1788
|
+
this.middlewares.push((machine) => withSnapshot(machine, options));
|
|
1789
|
+
return this;
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Add time travel middleware with type-safe configuration.
|
|
1793
|
+
*/
|
|
1794
|
+
withTimeTravel(options) {
|
|
1795
|
+
this.middlewares.push((machine) => withTimeTravel(machine, options));
|
|
1796
|
+
return this;
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Add debugging middleware (combination of history, snapshot, and time travel).
|
|
1800
|
+
*/
|
|
1801
|
+
withDebugging() {
|
|
1802
|
+
this.middlewares.push((machine) => withDebugging(machine));
|
|
1803
|
+
return this;
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Add a custom middleware function.
|
|
1807
|
+
*/
|
|
1808
|
+
withCustom(middleware) {
|
|
1809
|
+
this.middlewares.push(middleware);
|
|
1810
|
+
return this;
|
|
1811
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Add a conditional middleware.
|
|
1814
|
+
*/
|
|
1815
|
+
withConditional(middleware, predicate) {
|
|
1816
|
+
this.middlewares.push(when(middleware, predicate));
|
|
1817
|
+
return this;
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Build the final machine with all configured middleware applied.
|
|
1821
|
+
*/
|
|
1822
|
+
build() {
|
|
1823
|
+
let result = this.machine;
|
|
1824
|
+
for (const middleware of this.middlewares) {
|
|
1825
|
+
result = middleware(result);
|
|
1826
|
+
}
|
|
1827
|
+
return result;
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Get the middleware chain without building (for inspection or further composition).
|
|
1831
|
+
*/
|
|
1832
|
+
getChain() {
|
|
1833
|
+
return [...this.middlewares];
|
|
1834
|
+
}
|
|
1835
|
+
/**
|
|
1836
|
+
* Clear all configured middleware.
|
|
1837
|
+
*/
|
|
1838
|
+
clear() {
|
|
1839
|
+
this.middlewares = [];
|
|
1840
|
+
return this;
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
function middlewareBuilder(machine) {
|
|
1844
|
+
return new MiddlewareBuilder(machine);
|
|
1845
|
+
}
|
|
1846
|
+
function createMiddlewareFactory(defaultOptions = {}) {
|
|
1847
|
+
return {
|
|
1848
|
+
create: (machine) => {
|
|
1849
|
+
const builder = middlewareBuilder(machine);
|
|
1850
|
+
if (defaultOptions.logging) {
|
|
1851
|
+
builder.withLogging(defaultOptions.logging);
|
|
1852
|
+
}
|
|
1853
|
+
if (defaultOptions.analytics) {
|
|
1854
|
+
builder.withAnalytics(
|
|
1855
|
+
defaultOptions.analytics.track,
|
|
1856
|
+
defaultOptions.analytics.options
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1859
|
+
if (defaultOptions.history) {
|
|
1860
|
+
builder.withHistory(defaultOptions.history);
|
|
1861
|
+
}
|
|
1862
|
+
if (defaultOptions.snapshot) {
|
|
1863
|
+
builder.withSnapshot(defaultOptions.snapshot);
|
|
1864
|
+
}
|
|
1865
|
+
if (defaultOptions.timeTravel) {
|
|
1866
|
+
builder.withTimeTravel(defaultOptions.timeTravel);
|
|
1867
|
+
}
|
|
1868
|
+
if (defaultOptions.retry) {
|
|
1869
|
+
builder.withRetry(defaultOptions.retry);
|
|
1870
|
+
}
|
|
1871
|
+
return builder;
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
function withDebugging(machine) {
|
|
1876
|
+
return withTimeTravel(withSnapshot(withHistory(machine)));
|
|
1962
1877
|
}
|
|
1963
1878
|
|
|
1964
1879
|
// src/utils.ts
|
|
@@ -2085,15 +2000,61 @@ function state(context, transitions) {
|
|
|
2085
2000
|
}
|
|
2086
2001
|
|
|
2087
2002
|
// src/index.ts
|
|
2088
|
-
function createMachine(context,
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2003
|
+
function createMachine(context, fnsOrFactory) {
|
|
2004
|
+
if (typeof fnsOrFactory === "function") {
|
|
2005
|
+
let transitions2;
|
|
2006
|
+
const transition = (newContext) => {
|
|
2007
|
+
const machine2 = createMachine(newContext, transitions2);
|
|
2008
|
+
const boundTransitions2 = Object.fromEntries(
|
|
2009
|
+
Object.entries(transitions2).map(([key, fn]) => [
|
|
2010
|
+
key,
|
|
2011
|
+
fn.bind(newContext)
|
|
2012
|
+
])
|
|
2013
|
+
);
|
|
2014
|
+
return Object.assign(machine2, boundTransitions2);
|
|
2015
|
+
};
|
|
2016
|
+
transitions2 = fnsOrFactory(transition);
|
|
2017
|
+
const boundTransitions = Object.fromEntries(
|
|
2018
|
+
Object.entries(transitions2).map(([key, fn]) => [
|
|
2019
|
+
key,
|
|
2020
|
+
fn.bind(context)
|
|
2021
|
+
])
|
|
2022
|
+
);
|
|
2023
|
+
return Object.assign({ context }, boundTransitions);
|
|
2024
|
+
}
|
|
2025
|
+
const transitions = "context" in fnsOrFactory ? Object.fromEntries(
|
|
2026
|
+
Object.entries(fnsOrFactory).filter(([key]) => key !== "context")
|
|
2027
|
+
) : fnsOrFactory;
|
|
2092
2028
|
const machine = Object.assign({ context }, transitions);
|
|
2093
2029
|
return machine;
|
|
2094
2030
|
}
|
|
2095
|
-
function createAsyncMachine(context,
|
|
2096
|
-
|
|
2031
|
+
function createAsyncMachine(context, fnsOrFactory) {
|
|
2032
|
+
if (typeof fnsOrFactory === "function") {
|
|
2033
|
+
let transitions2;
|
|
2034
|
+
const transition = (newContext) => {
|
|
2035
|
+
const machine2 = createAsyncMachine(newContext, transitions2);
|
|
2036
|
+
const boundTransitions2 = Object.fromEntries(
|
|
2037
|
+
Object.entries(transitions2).map(([key, fn]) => [
|
|
2038
|
+
key,
|
|
2039
|
+
fn.bind(newContext)
|
|
2040
|
+
])
|
|
2041
|
+
);
|
|
2042
|
+
return Object.assign(machine2, boundTransitions2);
|
|
2043
|
+
};
|
|
2044
|
+
transitions2 = fnsOrFactory(transition);
|
|
2045
|
+
const boundTransitions = Object.fromEntries(
|
|
2046
|
+
Object.entries(transitions2).map(([key, fn]) => [
|
|
2047
|
+
key,
|
|
2048
|
+
fn.bind(context)
|
|
2049
|
+
])
|
|
2050
|
+
);
|
|
2051
|
+
return Object.assign({ context }, boundTransitions);
|
|
2052
|
+
}
|
|
2053
|
+
const transitions = "context" in fnsOrFactory ? Object.fromEntries(
|
|
2054
|
+
Object.entries(fnsOrFactory).filter(([key]) => key !== "context")
|
|
2055
|
+
) : fnsOrFactory;
|
|
2056
|
+
const machine = Object.assign({ context }, transitions);
|
|
2057
|
+
return machine;
|
|
2097
2058
|
}
|
|
2098
2059
|
function createMachineFactory() {
|
|
2099
2060
|
return (transformers) => {
|
|
@@ -2101,7 +2062,7 @@ function createMachineFactory() {
|
|
|
2101
2062
|
Object.entries(transformers).map(([key, transform]) => [
|
|
2102
2063
|
key,
|
|
2103
2064
|
function(...args) {
|
|
2104
|
-
const newContext = transform(this, ...args);
|
|
2065
|
+
const newContext = transform(this.context, ...args);
|
|
2105
2066
|
return createMachine(newContext, fns);
|
|
2106
2067
|
}
|
|
2107
2068
|
])
|
|
@@ -2215,10 +2176,11 @@ function next(m, update) {
|
|
|
2215
2176
|
export {
|
|
2216
2177
|
ADVANCED_CONFIG_EXAMPLES,
|
|
2217
2178
|
BoundMachine,
|
|
2179
|
+
CANCEL,
|
|
2218
2180
|
META_KEY,
|
|
2219
2181
|
MachineBase,
|
|
2182
|
+
MiddlewareBuilder,
|
|
2220
2183
|
MultiMachineBase,
|
|
2221
|
-
RUNTIME_META,
|
|
2222
2184
|
action,
|
|
2223
2185
|
bindTransitions,
|
|
2224
2186
|
branch,
|
|
@@ -2240,6 +2202,7 @@ export {
|
|
|
2240
2202
|
createMachineBuilder,
|
|
2241
2203
|
createMachineFactory,
|
|
2242
2204
|
createMiddleware,
|
|
2205
|
+
createMiddlewareFactory,
|
|
2243
2206
|
createMiddlewareRegistry,
|
|
2244
2207
|
createMultiMachine,
|
|
2245
2208
|
createMutableMachine,
|
|
@@ -2252,13 +2215,9 @@ export {
|
|
|
2252
2215
|
delegateToChild,
|
|
2253
2216
|
describe,
|
|
2254
2217
|
extendTransitions,
|
|
2255
|
-
extractFromInstance,
|
|
2256
|
-
extractFunctionMetadata,
|
|
2257
2218
|
extractMachine,
|
|
2258
2219
|
extractMachines,
|
|
2259
|
-
extractStateNode,
|
|
2260
2220
|
generateChart,
|
|
2261
|
-
generateStatechart,
|
|
2262
2221
|
guard,
|
|
2263
2222
|
guardAsync,
|
|
2264
2223
|
guarded,
|
|
@@ -2266,12 +2225,20 @@ export {
|
|
|
2266
2225
|
inDevelopment,
|
|
2267
2226
|
invoke,
|
|
2268
2227
|
isConditionalMiddleware,
|
|
2228
|
+
isMiddlewareContext,
|
|
2229
|
+
isMiddlewareError,
|
|
2269
2230
|
isMiddlewareFn,
|
|
2231
|
+
isMiddlewareHooks,
|
|
2232
|
+
isMiddlewareOptions,
|
|
2233
|
+
isMiddlewareResult,
|
|
2234
|
+
isNamedMiddleware,
|
|
2235
|
+
isPipelineConfig,
|
|
2270
2236
|
isState,
|
|
2271
2237
|
logState,
|
|
2272
2238
|
matchMachine,
|
|
2273
2239
|
mergeContext,
|
|
2274
2240
|
metadata,
|
|
2241
|
+
middlewareBuilder,
|
|
2275
2242
|
next,
|
|
2276
2243
|
overrideTransitions,
|
|
2277
2244
|
pipeTransitions,
|