@doeixd/machine 0.0.7 → 0.0.9

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.
@@ -104,9 +104,9 @@ function describe(_text, transition) {
104
104
  });
105
105
  return transition;
106
106
  }
107
- function guarded(guard, transition) {
107
+ function guarded(guard2, transition) {
108
108
  attachRuntimeMeta(transition, {
109
- guards: [guard]
109
+ guards: [guard2]
110
110
  });
111
111
  return transition;
112
112
  }
@@ -127,6 +127,118 @@ function action(action2, transition) {
127
127
  });
128
128
  return transition;
129
129
  }
130
+ function guard(condition, transition, options = {}) {
131
+ const { onFail = "throw", errorMessage, description } = options;
132
+ const fullOptions = { ...options, onFail, errorMessage, description };
133
+ const guardedTransition = function(...args) {
134
+ const isMachine = typeof this === "object" && "context" in this;
135
+ const ctx = isMachine ? this.context : this;
136
+ const conditionResult = condition(ctx, ...args);
137
+ if (conditionResult) {
138
+ const contextForTransition = isMachine ? this.context : this;
139
+ return transition.apply(contextForTransition, args);
140
+ } else {
141
+ if (onFail === "throw") {
142
+ const message = errorMessage || "Guard condition failed";
143
+ throw new Error(message);
144
+ } else if (onFail === "ignore") {
145
+ if (isMachine) {
146
+ return this;
147
+ } else {
148
+ throw new Error('Cannot use "ignore" mode with context-only binding. Use full machine binding or provide fallback.');
149
+ }
150
+ } else if (typeof onFail === "function") {
151
+ if (isMachine) {
152
+ return onFail.apply(this, args);
153
+ } else {
154
+ throw new Error("Cannot use function fallback with context-only binding. Use full machine binding.");
155
+ }
156
+ } else {
157
+ return onFail;
158
+ }
159
+ }
160
+ };
161
+ Object.defineProperty(guardedTransition, "__guard", { value: true, enumerable: false });
162
+ Object.defineProperty(guardedTransition, "condition", { value: condition, enumerable: false });
163
+ Object.defineProperty(guardedTransition, "transition", { value: transition, enumerable: false });
164
+ Object.defineProperty(guardedTransition, "options", { value: fullOptions, enumerable: false });
165
+ attachRuntimeMeta(guardedTransition, {
166
+ description: description || "Synchronous guarded transition",
167
+ guards: [{ name: "runtime_guard", description: description || "Synchronous condition check" }]
168
+ });
169
+ return guardedTransition;
170
+ }
171
+ function guardAsync(condition, transition, options = {}) {
172
+ const { onFail = "throw", errorMessage, description } = options;
173
+ const fullOptions = { ...options, onFail, errorMessage, description };
174
+ const guardedTransition = async function(...args) {
175
+ const isMachine = typeof this === "object" && "context" in this;
176
+ const ctx = isMachine ? this.context : this;
177
+ const conditionResult = await Promise.resolve(condition(ctx, ...args));
178
+ if (conditionResult) {
179
+ const contextForTransition = isMachine ? this.context : this;
180
+ return transition.apply(contextForTransition, args);
181
+ } else {
182
+ if (onFail === "throw") {
183
+ const message = errorMessage || "Guard condition failed";
184
+ throw new Error(message);
185
+ } else if (onFail === "ignore") {
186
+ if (isMachine) {
187
+ return this;
188
+ } else {
189
+ throw new Error('Cannot use "ignore" mode with context-only binding. Use full machine binding or provide fallback.');
190
+ }
191
+ } else if (typeof onFail === "function") {
192
+ if (isMachine) {
193
+ return onFail.apply(this, args);
194
+ } else {
195
+ throw new Error("Cannot use function fallback with context-only binding. Use full machine binding.");
196
+ }
197
+ } else {
198
+ return onFail;
199
+ }
200
+ }
201
+ };
202
+ Object.defineProperty(guardedTransition, "__guard", { value: true, enumerable: false });
203
+ Object.defineProperty(guardedTransition, "condition", { value: condition, enumerable: false });
204
+ Object.defineProperty(guardedTransition, "transition", { value: transition, enumerable: false });
205
+ Object.defineProperty(guardedTransition, "options", { value: fullOptions, enumerable: false });
206
+ attachRuntimeMeta(guardedTransition, {
207
+ description: description || "Runtime guarded transition",
208
+ guards: [{ name: "runtime_guard", description: description || "Runtime condition check" }]
209
+ });
210
+ return guardedTransition;
211
+ }
212
+ function whenGuard(condition) {
213
+ return {
214
+ /**
215
+ * Define the transition to execute when the condition passes.
216
+ * Returns a guarded transition that can optionally have an else clause.
217
+ */
218
+ do(transition) {
219
+ const guarded2 = guard(condition, transition);
220
+ guarded2.else = function(fallback) {
221
+ return guard(condition, transition, { onFail: fallback });
222
+ };
223
+ return guarded2;
224
+ }
225
+ };
226
+ }
227
+ function whenGuardAsync(condition) {
228
+ return {
229
+ /**
230
+ * Define the transition to execute when the condition passes.
231
+ * Returns a guarded transition that can optionally have an else clause.
232
+ */
233
+ do(transition) {
234
+ const guarded2 = guardAsync(condition, transition);
235
+ guarded2.else = function(fallback) {
236
+ return guardAsync(condition, transition, { onFail: fallback });
237
+ };
238
+ return guarded2;
239
+ }
240
+ };
241
+ }
130
242
  function metadata(_meta, value) {
131
243
  return value;
132
244
  }
@@ -226,9 +338,9 @@ function extractFromCallExpression(call2, verbose = false) {
226
338
  break;
227
339
  case "guarded":
228
340
  if (args[0]) {
229
- const guard = parseObjectLiteral(args[0]);
230
- if (Object.keys(guard).length > 0) {
231
- metadata2.guards = [guard];
341
+ const guard2 = parseObjectLiteral(args[0]);
342
+ if (Object.keys(guard2).length > 0) {
343
+ metadata2.guards = [guard2];
232
344
  }
233
345
  }
234
346
  if (args[1] && Node.isCallExpression(args[1])) {
@@ -260,6 +372,36 @@ function extractFromCallExpression(call2, verbose = false) {
260
372
  }
261
373
  }
262
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;
263
405
  default:
264
406
  return null;
265
407
  }
@@ -501,6 +643,37 @@ function generateChart() {
501
643
  process.exit(1);
502
644
  }
503
645
  }
646
+ var ADVANCED_CONFIG_EXAMPLES = {
647
+ hierarchical: {
648
+ input: "examples/dashboardMachine.ts",
649
+ id: "dashboard",
650
+ classes: ["DashboardMachine", "LoggedOutMachine"],
651
+ initialState: "DashboardMachine",
652
+ children: {
653
+ contextProperty: "child",
654
+ initialState: "ViewingChildMachine",
655
+ classes: ["ViewingChildMachine", "EditingChildMachine"]
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
+ ]
674
+ }
675
+ }
676
+ };
504
677
  if (__require.main === module) {
505
678
  generateChart();
506
679
  }
@@ -545,6 +718,16 @@ function extractStateNode(stateInstance) {
545
718
  transition.actions = meta.actions.map((a) => a.name);
546
719
  }
547
720
  stateNode.on[key] = transition;
721
+ } else if (meta.guards && meta.guards.length > 0) {
722
+ const transition = {
723
+ target: "GuardedTransition",
724
+ // Placeholder - actual target determined at runtime
725
+ cond: meta.guards.map((g) => g.name).join(" && ")
726
+ };
727
+ if (meta.description) {
728
+ transition.description = meta.description;
729
+ }
730
+ stateNode.on[key] = transition;
548
731
  }
549
732
  }
550
733
  if (invoke2.length > 0) {
@@ -920,6 +1103,859 @@ function createParallelMachine(m1, m2) {
920
1103
  };
921
1104
  }
922
1105
 
1106
+ // src/middleware.ts
1107
+ var CANCEL = Symbol("CANCEL");
1108
+ function createMiddleware(machine, hooks, options = {}) {
1109
+ const { mode = "auto", exclude = ["context"] } = options;
1110
+ const wrapped = {};
1111
+ for (const prop in machine) {
1112
+ if (!Object.prototype.hasOwnProperty.call(machine, prop)) continue;
1113
+ const value = machine[prop];
1114
+ if (prop === "context") {
1115
+ wrapped.context = value;
1116
+ continue;
1117
+ }
1118
+ if (exclude.includes(prop)) {
1119
+ wrapped[prop] = value;
1120
+ continue;
1121
+ }
1122
+ if (typeof value !== "function" || prop.startsWith("_")) {
1123
+ wrapped[prop] = value;
1124
+ continue;
1125
+ }
1126
+ wrapped[prop] = createTransitionWrapper(
1127
+ prop,
1128
+ value,
1129
+ machine,
1130
+ hooks,
1131
+ mode
1132
+ );
1133
+ }
1134
+ return wrapped;
1135
+ }
1136
+ function createTransitionWrapper(transitionName, originalFn, machine, hooks, mode) {
1137
+ return function wrappedTransition(...args) {
1138
+ const context = machine.context;
1139
+ const middlewareCtx = {
1140
+ transitionName,
1141
+ context,
1142
+ args
1143
+ };
1144
+ const executeSyncTransition = () => {
1145
+ try {
1146
+ if (hooks.before) {
1147
+ const beforeResult = hooks.before(middlewareCtx);
1148
+ if (beforeResult === CANCEL) {
1149
+ return machine;
1150
+ }
1151
+ if (beforeResult instanceof Promise) {
1152
+ throw new Error(
1153
+ `Middleware mode is 'sync' but before hook returned Promise for transition: ${transitionName}`
1154
+ );
1155
+ }
1156
+ }
1157
+ const result = originalFn.call(this, ...args);
1158
+ if (result instanceof Promise) {
1159
+ return handleAsyncResult(result, context);
1160
+ }
1161
+ if (hooks.after) {
1162
+ const middlewareResult = {
1163
+ transitionName,
1164
+ prevContext: context,
1165
+ nextContext: result.context,
1166
+ args
1167
+ };
1168
+ const afterResult = hooks.after(middlewareResult);
1169
+ if (afterResult instanceof Promise) {
1170
+ throw new Error(
1171
+ `Middleware mode is 'sync' but after hook returned Promise for transition: ${transitionName}`
1172
+ );
1173
+ }
1174
+ }
1175
+ return result;
1176
+ } catch (err) {
1177
+ if (hooks.error) {
1178
+ const middlewareError = {
1179
+ transitionName,
1180
+ context,
1181
+ args,
1182
+ error: err
1183
+ };
1184
+ const errorResult = hooks.error(middlewareError);
1185
+ if (errorResult instanceof Promise) {
1186
+ errorResult.catch(() => {
1187
+ });
1188
+ throw err;
1189
+ }
1190
+ if (errorResult && typeof errorResult === "object" && "context" in errorResult) {
1191
+ return errorResult;
1192
+ }
1193
+ }
1194
+ throw err;
1195
+ }
1196
+ };
1197
+ const handleAsyncResult = async (resultPromise, ctx) => {
1198
+ try {
1199
+ const result = await resultPromise;
1200
+ if (hooks.after) {
1201
+ const middlewareResult = {
1202
+ transitionName,
1203
+ prevContext: ctx,
1204
+ nextContext: result.context,
1205
+ args
1206
+ };
1207
+ await hooks.after(middlewareResult);
1208
+ }
1209
+ return result;
1210
+ } catch (err) {
1211
+ if (hooks.error) {
1212
+ const middlewareError = {
1213
+ transitionName,
1214
+ context: ctx,
1215
+ args,
1216
+ error: err
1217
+ };
1218
+ const errorResult = await hooks.error(middlewareError);
1219
+ if (errorResult && typeof errorResult === "object" && "context" in errorResult) {
1220
+ return errorResult;
1221
+ }
1222
+ }
1223
+ throw err;
1224
+ }
1225
+ };
1226
+ const executeAsyncTransition = async () => {
1227
+ try {
1228
+ if (hooks.before) {
1229
+ const beforeResult = await hooks.before(middlewareCtx);
1230
+ if (beforeResult === CANCEL) {
1231
+ return machine;
1232
+ }
1233
+ }
1234
+ const result = await originalFn.call(this, ...args);
1235
+ if (hooks.after) {
1236
+ const middlewareResult = {
1237
+ transitionName,
1238
+ prevContext: context,
1239
+ nextContext: result.context,
1240
+ args
1241
+ };
1242
+ await hooks.after(middlewareResult);
1243
+ }
1244
+ return result;
1245
+ } catch (err) {
1246
+ if (hooks.error) {
1247
+ const middlewareError = {
1248
+ transitionName,
1249
+ context,
1250
+ args,
1251
+ error: err
1252
+ };
1253
+ const errorResult = await hooks.error(middlewareError);
1254
+ if (errorResult && typeof errorResult === "object" && "context" in errorResult) {
1255
+ return errorResult;
1256
+ }
1257
+ }
1258
+ throw err;
1259
+ }
1260
+ };
1261
+ if (mode === "async") {
1262
+ return executeAsyncTransition();
1263
+ } else if (mode === "sync") {
1264
+ return executeSyncTransition();
1265
+ } else {
1266
+ return executeSyncTransition();
1267
+ }
1268
+ };
1269
+ }
1270
+ function withLogging(machine, options = {}) {
1271
+ const {
1272
+ logger = console.log,
1273
+ includeContext = true,
1274
+ includeArgs = true
1275
+ } = options;
1276
+ return createMiddleware(machine, {
1277
+ before: ({ transitionName, args }) => {
1278
+ const argsStr = includeArgs && args.length > 0 ? ` ${JSON.stringify(args)}` : "";
1279
+ logger(`→ ${transitionName}${argsStr}`);
1280
+ },
1281
+ after: ({ transitionName, nextContext }) => {
1282
+ const contextStr = includeContext ? ` ${JSON.stringify(nextContext)}` : "";
1283
+ logger(`✓ ${transitionName}${contextStr}`);
1284
+ }
1285
+ });
1286
+ }
1287
+ function withAnalytics(machine, track, options = {}) {
1288
+ const {
1289
+ eventPrefix = "state_transition",
1290
+ includePrevContext = false,
1291
+ includeArgs = true
1292
+ } = options;
1293
+ return createMiddleware(machine, {
1294
+ after: async ({ transitionName, prevContext, nextContext, args }) => {
1295
+ const properties = {
1296
+ transition: transitionName,
1297
+ to: nextContext
1298
+ };
1299
+ if (includePrevContext) {
1300
+ properties.from = prevContext;
1301
+ }
1302
+ if (includeArgs && args.length > 0) {
1303
+ properties.args = args;
1304
+ }
1305
+ await track(`${eventPrefix}.${transitionName}`, properties);
1306
+ }
1307
+ }, { mode: "async" });
1308
+ }
1309
+ function withValidation(machine, validate, options) {
1310
+ return createMiddleware(machine, {
1311
+ before: (ctx) => {
1312
+ const result = validate(ctx);
1313
+ if (result instanceof Promise) {
1314
+ return result.then((r) => {
1315
+ if (r === false) {
1316
+ throw new Error(`Validation failed for transition: ${ctx.transitionName}`);
1317
+ }
1318
+ return void 0;
1319
+ });
1320
+ }
1321
+ if (result === false) {
1322
+ throw new Error(`Validation failed for transition: ${ctx.transitionName}`);
1323
+ }
1324
+ return void 0;
1325
+ }
1326
+ }, { mode: "auto", ...options });
1327
+ }
1328
+ function withPermissions(machine, canPerform, options) {
1329
+ return createMiddleware(machine, {
1330
+ before: (ctx) => {
1331
+ const result = canPerform(ctx);
1332
+ if (result instanceof Promise) {
1333
+ return result.then((allowed) => {
1334
+ if (!allowed) {
1335
+ throw new Error(`Unauthorized transition: ${ctx.transitionName}`);
1336
+ }
1337
+ return void 0;
1338
+ });
1339
+ }
1340
+ if (!result) {
1341
+ throw new Error(`Unauthorized transition: ${ctx.transitionName}`);
1342
+ }
1343
+ return void 0;
1344
+ }
1345
+ }, { mode: "auto", ...options });
1346
+ }
1347
+ function withErrorReporting(machine, captureError, options = {}) {
1348
+ const { includeContext = true, includeArgs = true, mode } = options;
1349
+ return createMiddleware(machine, {
1350
+ error: async ({ transitionName, context, args, error }) => {
1351
+ const errorContext = {
1352
+ transition: transitionName
1353
+ };
1354
+ if (includeContext) {
1355
+ errorContext.context = context;
1356
+ }
1357
+ if (includeArgs && args.length > 0) {
1358
+ errorContext.args = args;
1359
+ }
1360
+ await Promise.resolve(captureError(error, errorContext));
1361
+ }
1362
+ }, { mode });
1363
+ }
1364
+ function withPerformanceMonitoring(machine, onMetric) {
1365
+ const timings = /* @__PURE__ */ new Map();
1366
+ return createMiddleware(machine, {
1367
+ before: ({ transitionName }) => {
1368
+ timings.set(transitionName, performance.now());
1369
+ return void 0;
1370
+ },
1371
+ after: ({ transitionName, nextContext }) => {
1372
+ const startTime = timings.get(transitionName);
1373
+ if (startTime) {
1374
+ const duration = performance.now() - startTime;
1375
+ timings.delete(transitionName);
1376
+ const result = onMetric({ transitionName, duration, context: nextContext });
1377
+ if (result instanceof Promise) {
1378
+ return result;
1379
+ }
1380
+ }
1381
+ return void 0;
1382
+ }
1383
+ }, { mode: "auto" });
1384
+ }
1385
+ function withRetry(machine, options = {}) {
1386
+ const {
1387
+ maxRetries = 3,
1388
+ delay = 1e3,
1389
+ backoffMultiplier = 1,
1390
+ shouldRetry = () => true,
1391
+ onRetry
1392
+ } = options;
1393
+ const wrapped = {};
1394
+ for (const prop in machine) {
1395
+ if (!Object.prototype.hasOwnProperty.call(machine, prop)) continue;
1396
+ const value = machine[prop];
1397
+ if (prop === "context" || typeof value !== "function") {
1398
+ wrapped[prop] = value;
1399
+ continue;
1400
+ }
1401
+ wrapped[prop] = async function retriableTransition(...args) {
1402
+ let lastError;
1403
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1404
+ try {
1405
+ return await value.call(this, ...args);
1406
+ } catch (error) {
1407
+ lastError = error;
1408
+ if (attempt === maxRetries) {
1409
+ break;
1410
+ }
1411
+ if (!shouldRetry(lastError)) {
1412
+ break;
1413
+ }
1414
+ onRetry == null ? void 0 : onRetry(attempt + 1, lastError);
1415
+ const currentDelay = delay * Math.pow(backoffMultiplier, attempt);
1416
+ await new Promise((resolve) => setTimeout(resolve, currentDelay));
1417
+ }
1418
+ }
1419
+ throw lastError;
1420
+ };
1421
+ }
1422
+ return wrapped;
1423
+ }
1424
+ function withHistory(machine, options = {}) {
1425
+ const {
1426
+ maxSize,
1427
+ serializer,
1428
+ filter,
1429
+ onEntry,
1430
+ _isRewrap = false
1431
+ } = options;
1432
+ const history = [];
1433
+ let entryId = 0;
1434
+ const instrumentedMachine = createMiddleware(machine, {
1435
+ before: ({ transitionName, args }) => {
1436
+ if (filter && !filter(transitionName, args)) {
1437
+ return;
1438
+ }
1439
+ const entry = {
1440
+ id: `entry-${entryId++}`,
1441
+ transitionName,
1442
+ args: [...args],
1443
+ // Shallow clone args (fast, works with any type)
1444
+ timestamp: Date.now()
1445
+ };
1446
+ if (serializer) {
1447
+ try {
1448
+ entry.serializedArgs = serializer.serialize(args);
1449
+ } catch (err) {
1450
+ console.error("Failed to serialize history args:", err);
1451
+ }
1452
+ }
1453
+ history.push(entry);
1454
+ if (maxSize && history.length > maxSize) {
1455
+ history.shift();
1456
+ }
1457
+ onEntry == null ? void 0 : onEntry(entry);
1458
+ }
1459
+ }, { exclude: ["context", "history", "clearHistory"] });
1460
+ if (!_isRewrap) {
1461
+ for (const prop in instrumentedMachine) {
1462
+ if (!Object.prototype.hasOwnProperty.call(instrumentedMachine, prop)) continue;
1463
+ const value = instrumentedMachine[prop];
1464
+ if (typeof value === "function" && !prop.startsWith("_") && prop !== "context" && !["history", "clearHistory"].includes(prop)) {
1465
+ const originalFn = value;
1466
+ instrumentedMachine[prop] = function(...args) {
1467
+ const result = originalFn.apply(this, args);
1468
+ if (result && typeof result === "object" && "context" in result && !("history" in result)) {
1469
+ const rewrappedResult = createMiddleware(result, {
1470
+ before: ({ transitionName, args: transArgs }) => {
1471
+ if (filter && !filter(transitionName, transArgs)) {
1472
+ return;
1473
+ }
1474
+ const entry = {
1475
+ id: `entry-${entryId++}`,
1476
+ transitionName,
1477
+ args: [...transArgs],
1478
+ timestamp: Date.now()
1479
+ };
1480
+ if (serializer) {
1481
+ try {
1482
+ entry.serializedArgs = serializer.serialize(transArgs);
1483
+ } catch (err) {
1484
+ console.error("Failed to serialize history args:", err);
1485
+ }
1486
+ }
1487
+ history.push(entry);
1488
+ if (maxSize && history.length > maxSize) {
1489
+ history.shift();
1490
+ }
1491
+ onEntry == null ? void 0 : onEntry(entry);
1492
+ }
1493
+ }, { exclude: ["context", "history", "clearHistory"] });
1494
+ return Object.assign(rewrappedResult, {
1495
+ history,
1496
+ clearHistory: () => {
1497
+ history.length = 0;
1498
+ entryId = 0;
1499
+ }
1500
+ });
1501
+ }
1502
+ return result;
1503
+ };
1504
+ }
1505
+ }
1506
+ }
1507
+ return Object.assign(instrumentedMachine, {
1508
+ history,
1509
+ clearHistory: () => {
1510
+ history.length = 0;
1511
+ entryId = 0;
1512
+ }
1513
+ });
1514
+ }
1515
+ function withSnapshot(machine, options = {}) {
1516
+ const {
1517
+ maxSize,
1518
+ serializer,
1519
+ captureSnapshot,
1520
+ onlyIfChanged = false,
1521
+ filter,
1522
+ onSnapshot,
1523
+ _extraExclusions = [],
1524
+ _isRewrap = false
1525
+ } = options;
1526
+ const snapshots = [];
1527
+ let snapshotId = 0;
1528
+ const instrumentedMachine = createMiddleware(machine, {
1529
+ after: ({ transitionName, prevContext, nextContext }) => {
1530
+ if (filter && !filter(transitionName)) {
1531
+ return;
1532
+ }
1533
+ if (onlyIfChanged) {
1534
+ const changed = JSON.stringify(prevContext) !== JSON.stringify(nextContext);
1535
+ if (!changed) {
1536
+ return;
1537
+ }
1538
+ }
1539
+ const snapshot = {
1540
+ id: `snapshot-${snapshotId++}`,
1541
+ transitionName,
1542
+ before: { ...prevContext },
1543
+ // Clone
1544
+ after: { ...nextContext },
1545
+ // Clone
1546
+ timestamp: Date.now()
1547
+ };
1548
+ if (serializer) {
1549
+ try {
1550
+ snapshot.serializedBefore = serializer.serialize(prevContext);
1551
+ snapshot.serializedAfter = serializer.serialize(nextContext);
1552
+ } catch (err) {
1553
+ console.error("Failed to serialize snapshot:", err);
1554
+ }
1555
+ }
1556
+ if (captureSnapshot) {
1557
+ try {
1558
+ snapshot.diff = captureSnapshot(prevContext, nextContext);
1559
+ } catch (err) {
1560
+ console.error("Failed to capture snapshot:", err);
1561
+ }
1562
+ }
1563
+ snapshots.push(snapshot);
1564
+ if (maxSize && snapshots.length > maxSize) {
1565
+ snapshots.shift();
1566
+ }
1567
+ onSnapshot == null ? void 0 : onSnapshot(snapshot);
1568
+ }
1569
+ }, { exclude: ["context", "snapshots", "clearSnapshots", "restoreSnapshot", ..._extraExclusions] });
1570
+ const restoreSnapshot = (context) => {
1571
+ const { context: _, ...transitions } = machine;
1572
+ return { context, ...transitions };
1573
+ };
1574
+ if (!_isRewrap) {
1575
+ for (const prop in instrumentedMachine) {
1576
+ if (!Object.prototype.hasOwnProperty.call(instrumentedMachine, prop)) continue;
1577
+ const value = instrumentedMachine[prop];
1578
+ if (typeof value === "function" && !prop.startsWith("_") && prop !== "context" && !["snapshots", "clearSnapshots", "restoreSnapshot", "history", "clearHistory"].includes(prop)) {
1579
+ const originalWrappedFn = value;
1580
+ instrumentedMachine[prop] = function(...args) {
1581
+ const result = originalWrappedFn.apply(this, args);
1582
+ if (result && typeof result === "object" && "context" in result && !("snapshots" in result)) {
1583
+ for (const transProp in result) {
1584
+ if (!Object.prototype.hasOwnProperty.call(result, transProp)) continue;
1585
+ const transValue = result[transProp];
1586
+ if (typeof transValue === "function" && !transProp.startsWith("_") && transProp !== "context" && !["snapshots", "clearSnapshots", "restoreSnapshot", "history", "clearHistory"].includes(transProp)) {
1587
+ const origTransFn = transValue;
1588
+ result[transProp] = function(...transArgs) {
1589
+ const prevCtx = result.context;
1590
+ const transResult = origTransFn.apply(this, transArgs);
1591
+ if (transResult && typeof transResult === "object" && "context" in transResult) {
1592
+ const nextCtx = transResult.context;
1593
+ if (!(filter && !filter(transProp))) {
1594
+ let shouldRecord = true;
1595
+ if (onlyIfChanged) {
1596
+ const changed = JSON.stringify(prevCtx) !== JSON.stringify(nextCtx);
1597
+ shouldRecord = changed;
1598
+ }
1599
+ if (shouldRecord) {
1600
+ const snapshot = {
1601
+ id: `snapshot-${snapshotId++}`,
1602
+ transitionName: transProp,
1603
+ before: { ...prevCtx },
1604
+ after: { ...nextCtx },
1605
+ timestamp: Date.now()
1606
+ };
1607
+ if (serializer) {
1608
+ try {
1609
+ snapshot.serializedBefore = serializer.serialize(prevCtx);
1610
+ snapshot.serializedAfter = serializer.serialize(nextCtx);
1611
+ } catch (err) {
1612
+ console.error("Failed to serialize snapshot:", err);
1613
+ }
1614
+ }
1615
+ if (captureSnapshot) {
1616
+ try {
1617
+ snapshot.diff = captureSnapshot(prevCtx, nextCtx);
1618
+ } catch (err) {
1619
+ console.error("Failed to capture snapshot:", err);
1620
+ }
1621
+ }
1622
+ snapshots.push(snapshot);
1623
+ if (maxSize && snapshots.length > maxSize) {
1624
+ snapshots.shift();
1625
+ }
1626
+ onSnapshot == null ? void 0 : onSnapshot(snapshot);
1627
+ }
1628
+ }
1629
+ }
1630
+ return transResult;
1631
+ };
1632
+ }
1633
+ }
1634
+ const resultWithTracking = Object.assign(result, {
1635
+ snapshots,
1636
+ clearSnapshots: () => {
1637
+ snapshots.length = 0;
1638
+ snapshotId = 0;
1639
+ },
1640
+ restoreSnapshot
1641
+ });
1642
+ if (machine.history) {
1643
+ resultWithTracking.history = machine.history;
1644
+ resultWithTracking.clearHistory = machine.clearHistory;
1645
+ }
1646
+ return resultWithTracking;
1647
+ }
1648
+ return result;
1649
+ };
1650
+ }
1651
+ }
1652
+ }
1653
+ return Object.assign(instrumentedMachine, {
1654
+ snapshots,
1655
+ clearSnapshots: () => {
1656
+ snapshots.length = 0;
1657
+ snapshotId = 0;
1658
+ },
1659
+ restoreSnapshot
1660
+ });
1661
+ }
1662
+ function withTimeTravel(machine, options = {}) {
1663
+ const { maxSize, serializer, onRecord } = options;
1664
+ const history = [];
1665
+ const snapshots = [];
1666
+ let entryId = 0;
1667
+ let snapshotId = 0;
1668
+ const recordHistory = (transitionName, args) => {
1669
+ const entry = {
1670
+ id: `entry-${entryId++}`,
1671
+ transitionName,
1672
+ args: [...args],
1673
+ timestamp: Date.now()
1674
+ };
1675
+ if (serializer) {
1676
+ try {
1677
+ entry.serializedArgs = serializer.serialize(args);
1678
+ } catch (err) {
1679
+ console.error("Failed to serialize history args:", err);
1680
+ }
1681
+ }
1682
+ history.push(entry);
1683
+ if (maxSize && history.length > maxSize) {
1684
+ history.shift();
1685
+ }
1686
+ onRecord == null ? void 0 : onRecord("history", entry);
1687
+ };
1688
+ const recordSnapshot = (transitionName, prevContext, nextContext) => {
1689
+ const snapshot = {
1690
+ id: `snapshot-${snapshotId++}`,
1691
+ transitionName,
1692
+ before: { ...prevContext },
1693
+ after: { ...nextContext },
1694
+ timestamp: Date.now()
1695
+ };
1696
+ if (serializer) {
1697
+ try {
1698
+ snapshot.serializedBefore = serializer.serialize(prevContext);
1699
+ snapshot.serializedAfter = serializer.serialize(nextContext);
1700
+ } catch (err) {
1701
+ console.error("Failed to serialize snapshot:", err);
1702
+ }
1703
+ }
1704
+ snapshots.push(snapshot);
1705
+ if (maxSize && snapshots.length > maxSize) {
1706
+ snapshots.shift();
1707
+ }
1708
+ onRecord == null ? void 0 : onRecord("snapshot", snapshot);
1709
+ };
1710
+ const restoreSnapshot = (context) => {
1711
+ const { context: _, ...transitions } = machine;
1712
+ return Object.assign({ context }, context, transitions);
1713
+ };
1714
+ const replayFrom = (snapshotIndex = 0) => {
1715
+ if (snapshotIndex < 0 || snapshotIndex >= snapshots.length) {
1716
+ throw new Error(`Invalid snapshot index: ${snapshotIndex}`);
1717
+ }
1718
+ let current = restoreSnapshot(snapshots[snapshotIndex].before);
1719
+ const snapshot = snapshots[snapshotIndex];
1720
+ const historyStartIndex = history.findIndex(
1721
+ (entry) => entry.transitionName === snapshot.transitionName && entry.timestamp === snapshot.timestamp
1722
+ );
1723
+ if (historyStartIndex === -1) {
1724
+ throw new Error("Could not find matching history entry for snapshot");
1725
+ }
1726
+ for (let i = historyStartIndex; i < history.length; i++) {
1727
+ const entry = history[i];
1728
+ const transition = current[entry.transitionName];
1729
+ if (typeof transition === "function") {
1730
+ try {
1731
+ current = transition.apply(current.context, entry.args);
1732
+ } catch (err) {
1733
+ console.error(`Replay failed at step ${i}:`, err);
1734
+ throw err;
1735
+ }
1736
+ }
1737
+ }
1738
+ return current;
1739
+ };
1740
+ const wrapMachine = (machine2) => {
1741
+ const wrapped = { ...machine2 };
1742
+ for (const prop in machine2) {
1743
+ if (!Object.prototype.hasOwnProperty.call(machine2, prop)) continue;
1744
+ const value = machine2[prop];
1745
+ if (typeof value === "function" && !prop.startsWith("_") && prop !== "context" && !["history", "snapshots", "clearHistory", "clearSnapshots", "clearTimeTravel", "restoreSnapshot", "replayFrom"].includes(prop)) {
1746
+ wrapped[prop] = function(...args) {
1747
+ recordHistory(prop, args);
1748
+ const prevContext = wrapped.context;
1749
+ const result = value.apply(this, args);
1750
+ if (result && typeof result === "object" && "context" in result) {
1751
+ recordSnapshot(prop, prevContext, result.context);
1752
+ }
1753
+ if (result && typeof result === "object" && "context" in result) {
1754
+ return wrapMachine(result);
1755
+ }
1756
+ return result;
1757
+ };
1758
+ }
1759
+ }
1760
+ return Object.assign(wrapped, {
1761
+ history,
1762
+ snapshots,
1763
+ clearHistory: () => {
1764
+ history.length = 0;
1765
+ entryId = 0;
1766
+ },
1767
+ clearSnapshots: () => {
1768
+ snapshots.length = 0;
1769
+ snapshotId = 0;
1770
+ },
1771
+ clearTimeTravel: () => {
1772
+ history.length = 0;
1773
+ snapshots.length = 0;
1774
+ entryId = 0;
1775
+ snapshotId = 0;
1776
+ },
1777
+ restoreSnapshot,
1778
+ replayFrom
1779
+ });
1780
+ };
1781
+ return wrapMachine(machine);
1782
+ }
1783
+ function compose(machine, ...middlewares) {
1784
+ return middlewares.reduce((acc, middleware) => middleware(acc), machine);
1785
+ }
1786
+ function createCustomMiddleware(hooks, options) {
1787
+ return (machine) => createMiddleware(machine, hooks, options);
1788
+ }
1789
+ function composeTyped(machine, ...middlewares) {
1790
+ return middlewares.reduce((acc, middleware) => middleware(acc), machine);
1791
+ }
1792
+ function chain(machine) {
1793
+ return new MiddlewareChainBuilder(machine);
1794
+ }
1795
+ var MiddlewareChainBuilder = class _MiddlewareChainBuilder {
1796
+ constructor(machine) {
1797
+ this.machine = machine;
1798
+ }
1799
+ /**
1800
+ * Add a middleware to the composition chain.
1801
+ * @param middleware - The middleware function to add
1802
+ * @returns A new composer with the middleware applied
1803
+ */
1804
+ with(middleware) {
1805
+ const result = middleware(this.machine);
1806
+ return new _MiddlewareChainBuilder(result);
1807
+ }
1808
+ /**
1809
+ * Build the final machine with all middlewares applied.
1810
+ */
1811
+ build() {
1812
+ return this.machine;
1813
+ }
1814
+ };
1815
+ function withDebugging(machine) {
1816
+ return withTimeTravel(withSnapshot(withHistory(machine)));
1817
+ }
1818
+ function createPipeline(config = {}) {
1819
+ const {
1820
+ continueOnError = false,
1821
+ logErrors = true,
1822
+ onError
1823
+ } = config;
1824
+ return (machine, ...middlewares) => {
1825
+ let currentMachine = machine;
1826
+ const errors = [];
1827
+ for (let i = 0; i < middlewares.length; i++) {
1828
+ const middleware = middlewares[i];
1829
+ try {
1830
+ if ("middleware" in middleware && "when" in middleware) {
1831
+ if (!middleware.when(currentMachine)) {
1832
+ continue;
1833
+ }
1834
+ currentMachine = middleware.middleware(currentMachine);
1835
+ } else {
1836
+ currentMachine = middleware(currentMachine);
1837
+ }
1838
+ } catch (error) {
1839
+ const err = error instanceof Error ? error : new Error(String(error));
1840
+ errors.push({ error: err, middlewareIndex: i });
1841
+ if (logErrors) {
1842
+ console.error(`Middleware pipeline error at index ${i}:`, err);
1843
+ }
1844
+ onError == null ? void 0 : onError(err, `middleware-${i}`);
1845
+ if (!continueOnError) {
1846
+ break;
1847
+ }
1848
+ }
1849
+ }
1850
+ return {
1851
+ machine: currentMachine,
1852
+ errors,
1853
+ success: errors.length === 0
1854
+ };
1855
+ };
1856
+ }
1857
+ function createMiddlewareRegistry() {
1858
+ const registry = /* @__PURE__ */ new Map();
1859
+ return {
1860
+ /**
1861
+ * Register a middleware with a name and optional metadata.
1862
+ */
1863
+ register(name, middleware, description, priority) {
1864
+ if (registry.has(name)) {
1865
+ throw new Error(`Middleware '${name}' is already registered`);
1866
+ }
1867
+ registry.set(name, { name, middleware, description, priority });
1868
+ return this;
1869
+ },
1870
+ /**
1871
+ * Unregister a middleware by name.
1872
+ */
1873
+ unregister(name) {
1874
+ return registry.delete(name);
1875
+ },
1876
+ /**
1877
+ * Check if a middleware is registered.
1878
+ */
1879
+ has(name) {
1880
+ return registry.has(name);
1881
+ },
1882
+ /**
1883
+ * Get a registered middleware by name.
1884
+ */
1885
+ get(name) {
1886
+ return registry.get(name);
1887
+ },
1888
+ /**
1889
+ * List all registered middlewares.
1890
+ */
1891
+ list() {
1892
+ return Array.from(registry.values()).sort((a, b) => {
1893
+ var _a, _b;
1894
+ return ((_a = a.priority) != null ? _a : 0) - ((_b = b.priority) != null ? _b : 0);
1895
+ });
1896
+ },
1897
+ /**
1898
+ * Apply a selection of registered middlewares to a machine.
1899
+ * Middlewares are applied in priority order (lowest to highest).
1900
+ */
1901
+ apply(machine, middlewareNames) {
1902
+ const middlewares = middlewareNames.map((name) => {
1903
+ const entry = registry.get(name);
1904
+ if (!entry) {
1905
+ throw new Error(`Middleware '${name}' is not registered`);
1906
+ }
1907
+ return entry;
1908
+ }).sort((a, b) => {
1909
+ var _a, _b;
1910
+ return ((_a = a.priority) != null ? _a : 0) - ((_b = b.priority) != null ? _b : 0);
1911
+ });
1912
+ return composeTyped(machine, ...middlewares.map((m) => m.middleware));
1913
+ },
1914
+ /**
1915
+ * Apply all registered middlewares to a machine in priority order.
1916
+ */
1917
+ applyAll(machine) {
1918
+ const middlewares = this.list();
1919
+ return composeTyped(machine, ...middlewares.map((m) => m.middleware));
1920
+ }
1921
+ };
1922
+ }
1923
+ function when(middleware, predicate) {
1924
+ const conditional = function(machine) {
1925
+ return predicate(machine) ? middleware(machine) : machine;
1926
+ };
1927
+ conditional.middleware = middleware;
1928
+ conditional.when = predicate;
1929
+ return conditional;
1930
+ }
1931
+ function inDevelopment(middleware) {
1932
+ return when(middleware, () => {
1933
+ return typeof process !== "undefined" ? true : typeof window !== "undefined" ? !window.location.hostname.includes("production") : false;
1934
+ });
1935
+ }
1936
+ function whenContext(key, value, middleware) {
1937
+ return when(middleware, (machine) => machine.context[key] === value);
1938
+ }
1939
+ function combine(...middlewares) {
1940
+ return (machine) => composeTyped(machine, ...middlewares);
1941
+ }
1942
+ function branch(branches, fallback) {
1943
+ return (machine) => {
1944
+ for (const [predicate, middleware] of branches) {
1945
+ if (predicate(machine)) {
1946
+ return middleware(machine);
1947
+ }
1948
+ }
1949
+ return fallback ? fallback(machine) : machine;
1950
+ };
1951
+ }
1952
+ function isMiddlewareFn(value) {
1953
+ return typeof value === "function" && value.length === 1;
1954
+ }
1955
+ function isConditionalMiddleware(value) {
1956
+ return value !== null && "middleware" in value && "when" in value && isMiddlewareFn(value.middleware) && typeof value.when === "function";
1957
+ }
1958
+
923
1959
  // src/utils.ts
924
1960
  function isState(machine, machineClass) {
925
1961
  return machine instanceof machineClass;
@@ -970,9 +2006,12 @@ var BoundMachine = class _BoundMachine {
970
2006
  this.wrappedMachine = machine;
971
2007
  return new Proxy(this, {
972
2008
  get: (target, prop) => {
973
- if (prop === "wrappedMachine" || prop === "context") {
2009
+ if (prop === "wrappedMachine") {
974
2010
  return Reflect.get(target, prop);
975
2011
  }
2012
+ if (prop === "context") {
2013
+ return this.wrappedMachine.context;
2014
+ }
976
2015
  const value = this.wrappedMachine[prop];
977
2016
  if (typeof value === "function") {
978
2017
  return (...args) => {
@@ -987,17 +2026,15 @@ var BoundMachine = class _BoundMachine {
987
2026
  }
988
2027
  });
989
2028
  }
990
- /**
991
- * Access the underlying machine's context directly.
992
- */
993
- get context() {
994
- return this.wrappedMachine.context;
995
- }
996
2029
  };
997
2030
 
998
2031
  // src/index.ts
999
2032
  function createMachine(context, fns) {
1000
- return Object.assign({ context }, fns);
2033
+ const transitions = "context" in fns ? Object.fromEntries(
2034
+ Object.entries(fns).filter(([key]) => key !== "context")
2035
+ ) : fns;
2036
+ const machine = Object.assign({ context }, transitions);
2037
+ return machine;
1001
2038
  }
1002
2039
  function createAsyncMachine(context, fns) {
1003
2040
  return Object.assign({ context }, fns);
@@ -1033,6 +2070,17 @@ function extendTransitions(machine, newTransitions) {
1033
2070
  const combinedTransitions = { ...originalTransitions, ...newTransitions };
1034
2071
  return createMachine(context, combinedTransitions);
1035
2072
  }
2073
+ function combineFactories(factory1, factory2) {
2074
+ return (...args) => {
2075
+ const machine1 = factory1(...args);
2076
+ const machine2 = factory2();
2077
+ const combinedContext = { ...machine1.context, ...machine2.context };
2078
+ const { context: _, ...transitions1 } = machine1;
2079
+ const { context: __, ...transitions2 } = machine2;
2080
+ const combinedTransitions = { ...transitions1, ...transitions2 };
2081
+ return createMachine(combinedContext, combinedTransitions);
2082
+ };
2083
+ }
1036
2084
  function createMachineBuilder(templateMachine) {
1037
2085
  const { context, ...transitions } = templateMachine;
1038
2086
  return (newContext) => {
@@ -1052,15 +2100,32 @@ function hasState(machine, key, value) {
1052
2100
  }
1053
2101
  function runMachine(initial, onChange) {
1054
2102
  let current = initial;
2103
+ let activeController = null;
1055
2104
  async function dispatch(event) {
2105
+ if (activeController) {
2106
+ activeController.abort();
2107
+ activeController = null;
2108
+ }
1056
2109
  const fn = current[event.type];
1057
2110
  if (typeof fn !== "function") {
1058
2111
  throw new Error(`[Machine] Unknown event type '${String(event.type)}' on current state.`);
1059
2112
  }
1060
- const nextState = await fn.apply(current.context, event.args);
1061
- current = nextState;
1062
- onChange == null ? void 0 : onChange(current);
1063
- return current;
2113
+ const controller = new AbortController();
2114
+ activeController = controller;
2115
+ try {
2116
+ const nextStatePromise = fn.apply(current.context, [...event.args, { signal: controller.signal }]);
2117
+ const nextState = await nextStatePromise;
2118
+ if (controller.signal.aborted) {
2119
+ return current;
2120
+ }
2121
+ current = nextState;
2122
+ onChange == null ? void 0 : onChange(current);
2123
+ return current;
2124
+ } finally {
2125
+ if (activeController === controller) {
2126
+ activeController = null;
2127
+ }
2128
+ }
1064
2129
  }
1065
2130
  return {
1066
2131
  /** Gets the context of the current state of the machine. */
@@ -1068,7 +2133,14 @@ function runMachine(initial, onChange) {
1068
2133
  return current.context;
1069
2134
  },
1070
2135
  /** Dispatches a type-safe event to the machine, triggering a transition. */
1071
- dispatch
2136
+ dispatch,
2137
+ /** Stops any pending async operation and cleans up resources. */
2138
+ stop: () => {
2139
+ if (activeController) {
2140
+ activeController.abort();
2141
+ activeController = null;
2142
+ }
2143
+ }
1072
2144
  };
1073
2145
  }
1074
2146
  var MachineBase = class {
@@ -1085,6 +2157,7 @@ function next(m, update) {
1085
2157
  return createMachine(update(context), transitions);
1086
2158
  }
1087
2159
  export {
2160
+ ADVANCED_CONFIG_EXAMPLES,
1088
2161
  BoundMachine,
1089
2162
  META_KEY,
1090
2163
  MachineBase,
@@ -1092,8 +2165,15 @@ export {
1092
2165
  RUNTIME_META,
1093
2166
  action,
1094
2167
  bindTransitions,
2168
+ branch,
1095
2169
  call,
2170
+ chain,
2171
+ combine,
2172
+ combineFactories,
2173
+ compose,
2174
+ composeTyped,
1096
2175
  createAsyncMachine,
2176
+ createCustomMiddleware,
1097
2177
  createEnsemble,
1098
2178
  createEvent,
1099
2179
  createFetchMachine,
@@ -1101,9 +2181,12 @@ export {
1101
2181
  createMachine,
1102
2182
  createMachineBuilder,
1103
2183
  createMachineFactory,
2184
+ createMiddleware,
2185
+ createMiddlewareRegistry,
1104
2186
  createMultiMachine,
1105
2187
  createMutableMachine,
1106
2188
  createParallelMachine,
2189
+ createPipeline,
1107
2190
  createRunner,
1108
2191
  delegateToChild,
1109
2192
  describe,
@@ -1115,9 +2198,14 @@ export {
1115
2198
  extractStateNode,
1116
2199
  generateChart,
1117
2200
  generateStatechart,
2201
+ guard,
2202
+ guardAsync,
1118
2203
  guarded,
1119
2204
  hasState,
2205
+ inDevelopment,
1120
2206
  invoke,
2207
+ isConditionalMiddleware,
2208
+ isMiddlewareFn,
1121
2209
  isState,
1122
2210
  logState,
1123
2211
  matchMachine,
@@ -1138,6 +2226,21 @@ export {
1138
2226
  stepAsync,
1139
2227
  toggle,
1140
2228
  transitionTo,
2229
+ when,
2230
+ whenContext,
2231
+ whenGuard,
2232
+ whenGuardAsync,
2233
+ withAnalytics,
2234
+ withDebugging,
2235
+ withErrorReporting,
2236
+ withHistory,
2237
+ withLogging,
2238
+ withPerformanceMonitoring,
2239
+ withPermissions,
2240
+ withRetry,
2241
+ withSnapshot,
2242
+ withTimeTravel,
2243
+ withValidation,
1141
2244
  yieldMachine
1142
2245
  };
1143
2246
  //# sourceMappingURL=index.js.map