@aws/durable-execution-sdk-js 1.1.2 → 2.0.0-alpha.1

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.
Files changed (63) hide show
  1. package/dist/index.mjs +669 -103
  2. package/dist/index.mjs.map +1 -1
  3. package/dist-cjs/index.js +666 -102
  4. package/dist-cjs/index.js.map +1 -1
  5. package/dist-types/context/durable-context/durable-context.d.ts +5 -0
  6. package/dist-types/context/durable-context/durable-context.d.ts.map +1 -1
  7. package/dist-types/errors/durable-error/durable-error.d.ts +24 -0
  8. package/dist-types/errors/durable-error/durable-error.d.ts.map +1 -1
  9. package/dist-types/errors/non-retryable-errors.d.ts +5 -0
  10. package/dist-types/errors/non-retryable-errors.d.ts.map +1 -0
  11. package/dist-types/handlers/callback-handler/callback.d.ts +2 -2
  12. package/dist-types/handlers/callback-handler/callback.d.ts.map +1 -1
  13. package/dist-types/handlers/concurrent-execution-handler/concurrent-execution-handler.d.ts +4 -2
  14. package/dist-types/handlers/concurrent-execution-handler/concurrent-execution-handler.d.ts.map +1 -1
  15. package/dist-types/handlers/invoke-handler/invoke-handler.d.ts +2 -1
  16. package/dist-types/handlers/invoke-handler/invoke-handler.d.ts.map +1 -1
  17. package/dist-types/handlers/map-handler/map-handler.d.ts.map +1 -1
  18. package/dist-types/handlers/parallel-handler/parallel-handler.d.ts.map +1 -1
  19. package/dist-types/handlers/promise-handler/promise-handler.d.ts +1 -1
  20. package/dist-types/handlers/promise-handler/promise-handler.d.ts.map +1 -1
  21. package/dist-types/handlers/run-in-child-context-handler/run-in-child-context-handler.d.ts +4 -3
  22. package/dist-types/handlers/run-in-child-context-handler/run-in-child-context-handler.d.ts.map +1 -1
  23. package/dist-types/handlers/step-handler/step-handler.d.ts +2 -1
  24. package/dist-types/handlers/step-handler/step-handler.d.ts.map +1 -1
  25. package/dist-types/handlers/wait-for-callback-handler/wait-for-callback-handler.d.ts +2 -1
  26. package/dist-types/handlers/wait-for-callback-handler/wait-for-callback-handler.d.ts.map +1 -1
  27. package/dist-types/handlers/wait-for-condition-handler/wait-for-condition-handler.d.ts +2 -1
  28. package/dist-types/handlers/wait-for-condition-handler/wait-for-condition-handler.d.ts.map +1 -1
  29. package/dist-types/index.d.ts +7 -3
  30. package/dist-types/index.d.ts.map +1 -1
  31. package/dist-types/testing/test-constants.d.ts +6 -0
  32. package/dist-types/testing/test-constants.d.ts.map +1 -1
  33. package/dist-types/types/batch.d.ts +50 -0
  34. package/dist-types/types/batch.d.ts.map +1 -1
  35. package/dist-types/types/child-context.d.ts +5 -0
  36. package/dist-types/types/child-context.d.ts.map +1 -1
  37. package/dist-types/types/durable-context.d.ts +23 -0
  38. package/dist-types/types/durable-context.d.ts.map +1 -1
  39. package/dist-types/utils/checkpoint/checkpoint-manager.d.ts.map +1 -1
  40. package/dist-types/utils/constants/constants.d.ts +6 -0
  41. package/dist-types/utils/constants/constants.d.ts.map +1 -1
  42. package/dist-types/utils/constants/version.d.ts +1 -12
  43. package/dist-types/utils/constants/version.d.ts.map +1 -1
  44. package/dist-types/utils/retry/linear-retry-strategy/linear-retry-strategy.d.ts +10 -0
  45. package/dist-types/utils/retry/linear-retry-strategy/linear-retry-strategy.d.ts.map +1 -0
  46. package/dist-types/utils/retry/linear-retry-strategy/linear-retry-strategy.test.d.ts +2 -0
  47. package/dist-types/utils/retry/linear-retry-strategy/linear-retry-strategy.test.d.ts.map +1 -0
  48. package/dist-types/utils/retry/retry-presets/retry-presets.d.ts +13 -1
  49. package/dist-types/utils/retry/retry-presets/retry-presets.d.ts.map +1 -1
  50. package/dist-types/utils/serdes/filesystem-serdes.d.ts +119 -0
  51. package/dist-types/utils/serdes/filesystem-serdes.d.ts.map +1 -0
  52. package/dist-types/utils/serdes/filesystem-serdes.test.d.ts +2 -0
  53. package/dist-types/utils/serdes/filesystem-serdes.test.d.ts.map +1 -0
  54. package/dist-types/utils/serdes/preview.d.ts +96 -0
  55. package/dist-types/utils/serdes/preview.d.ts.map +1 -0
  56. package/dist-types/utils/serdes/serdes.d.ts +29 -0
  57. package/dist-types/utils/serdes/serdes.d.ts.map +1 -1
  58. package/dist-types/utils/with-retry/index.d.ts +188 -0
  59. package/dist-types/utils/with-retry/index.d.ts.map +1 -0
  60. package/dist-types/utils/with-retry/index.test.d.ts +2 -0
  61. package/dist-types/utils/with-retry/index.test.d.ts.map +1 -0
  62. package/dist-types/with-durable-execution.d.ts.map +1 -1
  63. package/package.json +3 -3
package/dist-cjs/index.js CHANGED
@@ -6,6 +6,8 @@ var async_hooks = require('async_hooks');
6
6
  var crypto = require('crypto');
7
7
  var node_console = require('node:console');
8
8
  var util = require('node:util');
9
+ var promises = require('node:fs/promises');
10
+ var node_path = require('node:path');
9
11
 
10
12
  /**
11
13
  * @internal
@@ -235,6 +237,35 @@ exports.JitterStrategy = void 0;
235
237
  JitterStrategy["HALF"] = "HALF";
236
238
  })(exports.JitterStrategy || (exports.JitterStrategy = {}));
237
239
 
240
+ /**
241
+ * Nesting type for batch operations (map and parallel)
242
+ *
243
+ * Controls how child contexts are created for each branch/iteration, affecting
244
+ * observability, cost, and scale limits.
245
+ *
246
+ * @public
247
+ */
248
+ exports.NestingType = void 0;
249
+ (function (NestingType) {
250
+ /**
251
+ * Create CONTEXT operations for each branch/iteration with full checkpointing.
252
+ * Operations within each branch/iteration are wrapped in their own context.
253
+ *
254
+ * - **Observability**: High - each branch/iteration appears as separate operation in execution history
255
+ * - **Cost**: Higher - consumes more operations due to CONTEXT creation overhead
256
+ * - **Scale**: Lower maximum iterations due to operation limits
257
+ */
258
+ NestingType["NESTED"] = "NESTED";
259
+ /**
260
+ * Skip CONTEXT operations for branches/iterations using virtual contexts.
261
+ * Operations execute directly without individual context wrapping.
262
+ *
263
+ * - **Observability**: Lower - branches/iterations don't appear as separate operations
264
+ * - **Cost**: ~30% lower - reduces operation consumption by skipping CONTEXT overhead
265
+ * - **Scale**: Higher maximum iterations possible within operation limits
266
+ */
267
+ NestingType["FLAT"] = "FLAT";
268
+ })(exports.NestingType || (exports.NestingType = {}));
238
269
  /**
239
270
  * The status of a batch item
240
271
  * @public
@@ -559,15 +590,39 @@ const createRetryStrategy = (config = {}) => {
559
590
  };
560
591
  };
561
592
 
593
+ /**
594
+ * Creates a linear backoff retry strategy
595
+ * @param maxAttempts - Maximum number of attempts (default: 6)
596
+ * @param initialDelay - Initial delay in seconds (default: 1)
597
+ * @param increment - Linear increment per attempt in seconds (default: 1)
598
+ * @returns Retry strategy with linear backoff
599
+ */
600
+ const createLinearRetryStrategy = (maxAttempts = 6, initialDelay = 1, increment = 1) => {
601
+ return (error, attemptsMade) => {
602
+ if (attemptsMade >= maxAttempts) {
603
+ return { shouldRetry: false };
604
+ }
605
+ return {
606
+ shouldRetry: true,
607
+ delay: { seconds: initialDelay + increment * (attemptsMade - 1) },
608
+ };
609
+ };
610
+ };
611
+
562
612
  /**
563
613
  * Pre-configured retry strategies for common use cases
564
614
  * @example
565
615
  * ```typescript
566
- * // Use default retry preset (3 attempts with exponential backoff)
616
+ * // Use default retry preset (6 attempts with exponential backoff)
567
617
  * await context.step('my-step', async () => {
568
618
  * return await someOperation();
569
619
  * }, { retryStrategy: retryPresets.default });
570
620
  *
621
+ * // Use linear retry preset (1s, 2s, 3s, 4s, 5s delays)
622
+ * await context.step('linear-step', async () => {
623
+ * return await someOperation();
624
+ * }, { retryStrategy: retryPresets.linear });
625
+ *
571
626
  * // Use no-retry preset (fail immediately on error)
572
627
  * await context.step('critical-step', async () => {
573
628
  * return await criticalOperation();
@@ -593,6 +648,13 @@ const retryPresets = {
593
648
  backoffRate: 2,
594
649
  jitter: exports.JitterStrategy.FULL,
595
650
  }),
651
+ /**
652
+ * Linear retry strategy with fixed increment
653
+ * - 6 total attempts (1 initial + 5 retries)
654
+ * - Delays: 1s, 2s, 3s, 4s, 5s
655
+ * - Total max wait time: 15 seconds
656
+ */
657
+ linear: createLinearRetryStrategy(6, 1, 1),
596
658
  /**
597
659
  * No retry strategy - fails immediately on first error
598
660
  * - 1 total attempt (no retries)
@@ -633,6 +695,12 @@ const CHECKPOINT_TERMINATION_COOLDOWN_MS = 20;
633
695
  * and limit polling duration for long-running operations
634
696
  */
635
697
  const MAX_POLL_DURATION_MS = 15 * 60 * 1000;
698
+ /**
699
+ * Maximum checkpoint payload size in bytes (256KB).
700
+ * Payloads exceeding this limit trigger ReplayChildren mode in child contexts,
701
+ * and overflow-to-file behavior in FileSystemSerdes.
702
+ */
703
+ const CHECKPOINT_SIZE_LIMIT_BYTES = 256 * 1024;
636
704
 
637
705
  /**
638
706
  * Base class for all durable operation errors
@@ -661,6 +729,10 @@ class DurableOperationError extends Error {
661
729
  return new StepError(errorObject.ErrorMessage || "Step failed", cause, errorObject.ErrorData);
662
730
  case "CallbackError":
663
731
  return new CallbackError(errorObject.ErrorMessage || "Callback failed", cause, errorObject.ErrorData);
732
+ case "CallbackTimeoutError":
733
+ return new CallbackTimeoutError(errorObject.ErrorMessage || "Callback timed out", cause, errorObject.ErrorData);
734
+ case "CallbackSubmitterError":
735
+ return new CallbackSubmitterError(errorObject.ErrorMessage || "Callback submitter failed", cause, errorObject.ErrorData);
664
736
  case "InvokeError":
665
737
  return new InvokeError(errorObject.ErrorMessage || "Invoke failed", cause, errorObject.ErrorData);
666
738
  case "ChildContextError":
@@ -703,6 +775,26 @@ class CallbackError extends DurableOperationError {
703
775
  super(message || "Callback failed", cause, errorData);
704
776
  }
705
777
  }
778
+ /**
779
+ * Error thrown when a callback operation times out
780
+ * @public
781
+ */
782
+ class CallbackTimeoutError extends DurableOperationError {
783
+ errorType = "CallbackTimeoutError";
784
+ constructor(message, cause, errorData) {
785
+ super(message || "Callback timed out", cause, errorData);
786
+ }
787
+ }
788
+ /**
789
+ * Error thrown when a callback submitter fails
790
+ * @public
791
+ */
792
+ class CallbackSubmitterError extends DurableOperationError {
793
+ errorType = "CallbackSubmitterError";
794
+ constructor(message, cause, errorData) {
795
+ super(message || "Callback submitter failed", cause, errorData);
796
+ }
797
+ }
706
798
  /**
707
799
  * Error thrown when an invoke operation fails
708
800
  * @public
@@ -723,6 +815,16 @@ class ChildContextError extends DurableOperationError {
723
815
  super(message || "Child context failed", cause, errorData);
724
816
  }
725
817
  }
818
+ /**
819
+ * Error thrown when a promise combinator operation fails
820
+ * @public
821
+ */
822
+ class PromiseCombinatorError extends DurableOperationError {
823
+ errorType = "PromiseCombinatorError";
824
+ constructor(message, cause, errorData) {
825
+ super(message || "Promise combinator failed", cause, errorData);
826
+ }
827
+ }
726
828
  /**
727
829
  * Error thrown when a wait for condition operation fails
728
830
  * @public
@@ -1122,7 +1224,7 @@ const validateReplayConsistency = (stepId, currentOperation, checkpointData, con
1122
1224
  }
1123
1225
  };
1124
1226
 
1125
- const createStepHandler = (context, checkpoint, parentContext, createStepId, logger, parentId) => {
1227
+ const createStepHandler = (context, checkpoint, parentContext, createStepId, logger, parentId, getDefaultSerdes) => {
1126
1228
  return (nameOrFn, fnOrOptions, maybeOptions) => {
1127
1229
  let name;
1128
1230
  let fn;
@@ -1138,7 +1240,8 @@ const createStepHandler = (context, checkpoint, parentContext, createStepId, log
1138
1240
  }
1139
1241
  const stepId = createStepId();
1140
1242
  const semantics = options?.semantics || exports.StepSemantics.AtLeastOncePerRetry;
1141
- const serdes = options?.serdes || defaultSerdes;
1243
+ const serdes = options?.serdes ||
1244
+ (getDefaultSerdes ? getDefaultSerdes() : defaultSerdes);
1142
1245
  // Phase 1: Execute step
1143
1246
  const phase1Promise = (async () => {
1144
1247
  let stepData = context.getStepData(stepId);
@@ -1353,7 +1456,7 @@ const createStepHandler = (context, checkpoint, parentContext, createStepId, log
1353
1456
  };
1354
1457
  };
1355
1458
 
1356
- const createInvokeHandler = (context, checkpoint, createStepId, parentId, checkAndUpdateReplayMode) => {
1459
+ const createInvokeHandler = (context, checkpoint, createStepId, parentId, checkAndUpdateReplayMode, getDefaultSerdes) => {
1357
1460
  function invokeHandler(nameOrFuncId, funcIdOrInput, inputOrConfig, maybeConfig) {
1358
1461
  const isNameFirst = typeof funcIdOrInput === "string";
1359
1462
  const name = isNameFirst ? nameOrFuncId : undefined;
@@ -1411,7 +1514,8 @@ const createInvokeHandler = (context, checkpoint, createStepId, parentId, checkA
1411
1514
  }
1412
1515
  // Start invoke if not already started
1413
1516
  if (!stepData) {
1414
- const serializedPayload = await safeSerialize(config?.payloadSerdes || defaultSerdes, input, stepId, name, context.terminationManager, context.durableExecutionArn);
1517
+ const serializedPayload = await safeSerialize(config?.payloadSerdes ||
1518
+ (getDefaultSerdes ? getDefaultSerdes() : defaultSerdes), input, stepId, name, context.terminationManager, context.durableExecutionArn);
1415
1519
  await checkpoint.checkpoint(stepId, {
1416
1520
  Id: stepId,
1417
1521
  ParentId: parentId,
@@ -1446,7 +1550,8 @@ const createInvokeHandler = (context, checkpoint, createStepId, parentId, checkA
1446
1550
  const stepData = context.getStepData(stepId);
1447
1551
  if (stepData?.Status === clientLambda.OperationStatus.SUCCEEDED) {
1448
1552
  const invokeDetails = stepData.ChainedInvokeDetails;
1449
- return await safeDeserialize(config?.resultSerdes || defaultSerdes, invokeDetails?.Result, stepId, name, context.terminationManager, context.durableExecutionArn);
1553
+ return await safeDeserialize(config?.resultSerdes ||
1554
+ (getDefaultSerdes ? getDefaultSerdes() : defaultSerdes), invokeDetails?.Result, stepId, name, context.terminationManager, context.durableExecutionArn);
1450
1555
  }
1451
1556
  // Handle failure
1452
1557
  const invokeDetails = stepData?.ChainedInvokeDetails;
@@ -1468,7 +1573,8 @@ const createInvokeHandler = (context, checkpoint, createStepId, parentId, checkA
1468
1573
  checkAndUpdateReplayMode?.();
1469
1574
  checkpoint.markOperationState(stepId, OperationLifecycleState.COMPLETED);
1470
1575
  const invokeDetails = stepData.ChainedInvokeDetails;
1471
- return await safeDeserialize(config?.resultSerdes || defaultSerdes, invokeDetails?.Result, stepId, name, context.terminationManager, context.durableExecutionArn);
1576
+ return await safeDeserialize(config?.resultSerdes ||
1577
+ (getDefaultSerdes ? getDefaultSerdes() : defaultSerdes), invokeDetails?.Result, stepId, name, context.terminationManager, context.durableExecutionArn);
1472
1578
  }
1473
1579
  // Handle failure
1474
1580
  log("❌", "Invoke failed:", { stepId, status: stepData?.Status });
@@ -1487,8 +1593,6 @@ const createInvokeHandler = (context, checkpoint, createStepId, parentId, checkA
1487
1593
  return invokeHandler;
1488
1594
  };
1489
1595
 
1490
- // Checkpoint size limit in bytes (256KB)
1491
- const CHECKPOINT_SIZE_LIMIT = 256 * 1024;
1492
1596
  const determineChildReplayMode = (context, stepId) => {
1493
1597
  const stepData = context.getStepData(stepId);
1494
1598
  if (!stepData) {
@@ -1504,7 +1608,7 @@ const determineChildReplayMode = (context, stepId) => {
1504
1608
  }
1505
1609
  return DurableExecutionMode.ExecutionMode;
1506
1610
  };
1507
- const createRunInChildContextHandler = (context, checkpoint, parentContext, createStepId, getParentLogger, createChildContext, parentId) => {
1611
+ const createRunInChildContextHandler = (context, checkpoint, parentContext, createStepId, getParentLogger, createChildContext, parentId, getDefaultSerdes) => {
1508
1612
  return (nameOrFn, fnOrOptions, maybeOptions) => {
1509
1613
  let name;
1510
1614
  let fn;
@@ -1542,10 +1646,10 @@ const createRunInChildContextHandler = (context, checkpoint, parentContext, crea
1542
1646
  currentStepData?.Status === clientLambda.OperationStatus.FAILED) {
1543
1647
  // Mark this run-in-child-context as finished to prevent descendant operations
1544
1648
  checkpoint.markAncestorFinished(entityId);
1545
- return handleCompletedChildContext(context, parentContext, entityId, name, fn, options, getParentLogger, createChildContext);
1649
+ return handleCompletedChildContext(context, parentContext, entityId, name, fn, options, getParentLogger, createChildContext, getDefaultSerdes);
1546
1650
  }
1547
1651
  // Execute if not completed
1548
- return executeChildContext(context, checkpoint, parentContext, entityId, name, fn, options, getParentLogger, createChildContext, parentId);
1652
+ return executeChildContext(context, checkpoint, parentContext, entityId, name, fn, options, getParentLogger, createChildContext, parentId, getDefaultSerdes);
1549
1653
  })()
1550
1654
  .then((result) => {
1551
1655
  phase1Result = result;
@@ -1563,14 +1667,19 @@ const createRunInChildContextHandler = (context, checkpoint, parentContext, crea
1563
1667
  });
1564
1668
  };
1565
1669
  };
1566
- const handleCompletedChildContext = async (context, parentContext, entityId, stepName, fn, options, getParentLogger, createChildContext) => {
1567
- const serdes = options?.serdes || defaultSerdes;
1670
+ const handleCompletedChildContext = async (context, parentContext, entityId, stepName, fn, options, getParentLogger, createChildContext, getDefaultSerdes) => {
1671
+ const serdes = options?.serdes || (getDefaultSerdes ? getDefaultSerdes() : defaultSerdes);
1672
+ const errorMapper = options?.errorMapper;
1568
1673
  const stepData = context.getStepData(entityId);
1569
1674
  const result = stepData?.ContextDetails?.Result;
1570
1675
  // Handle failed child context
1571
1676
  if (stepData?.Status === clientLambda.OperationStatus.FAILED) {
1572
1677
  if (stepData.ContextDetails?.Error) {
1573
1678
  const originalError = DurableOperationError.fromErrorObject(stepData.ContextDetails.Error);
1679
+ // Use errorMapper if provided, otherwise wrap in ChildContextError
1680
+ if (errorMapper) {
1681
+ throw errorMapper(originalError);
1682
+ }
1574
1683
  throw new ChildContextError(originalError.message, originalError);
1575
1684
  }
1576
1685
  else {
@@ -1589,10 +1698,12 @@ const handleCompletedChildContext = async (context, parentContext, entityId, ste
1589
1698
  });
1590
1699
  return await safeDeserialize(serdes, result, entityId, stepName, context.terminationManager, context.durableExecutionArn);
1591
1700
  };
1592
- const executeChildContext = async (context, checkpoint, parentContext, entityId, name, fn, options, getParentLogger, createChildContext, parentId) => {
1593
- const serdes = options?.serdes || defaultSerdes;
1594
- // Checkpoint at start if not already started (fire-and-forget for performance)
1595
- if (context.getStepData(entityId) === undefined) {
1701
+ const executeChildContext = async (context, checkpoint, parentContext, entityId, name, fn, options, getParentLogger, createChildContext, parentId, getDefaultSerdes) => {
1702
+ const serdes = options?.serdes || (getDefaultSerdes ? getDefaultSerdes() : defaultSerdes);
1703
+ const errorMapper = options?.errorMapper;
1704
+ const isVirtual = options?.virtualContext === true;
1705
+ // Checkpoint at start if not already started and not virtual (fire-and-forget for performance)
1706
+ if (!isVirtual && context.getStepData(entityId) === undefined) {
1596
1707
  const subType = options?.subType || exports.OperationSubType.RUN_IN_CHILD_CONTEXT;
1597
1708
  checkpoint.checkpoint(entityId, {
1598
1709
  Id: entityId,
@@ -1604,8 +1715,14 @@ const executeChildContext = async (context, checkpoint, parentContext, entityId,
1604
1715
  });
1605
1716
  }
1606
1717
  const childReplayMode = determineChildReplayMode(context, entityId);
1607
- // Create a child context with the entity ID as prefix
1608
- const durableChildContext = createChildContext(context, parentContext, childReplayMode, getParentLogger(), entityId, undefined, entityId);
1718
+ // Create a child context with appropriate parentId and stepPrefix
1719
+ const durableChildContext = createChildContext(context, parentContext, childReplayMode, getParentLogger(), entityId, // stepPrefix: use entityId for unique step IDs
1720
+ undefined,
1721
+ // parentId: this parameter is used for checkpointing, and should point to
1722
+ // valid parentId tthat is already checkpointed.
1723
+ // If this runInChildContext is a virtual, then we will use the parentId (the ancestor)
1724
+ // But if this runInChildContext is a virtual, then it's entityId can be used
1725
+ isVirtual ? parentId : entityId);
1609
1726
  try {
1610
1727
  // Execute the child context function with context tracking
1611
1728
  const result = await runWithContext(entityId, parentId, () => fn(durableChildContext), undefined, childReplayMode);
@@ -1615,7 +1732,7 @@ const executeChildContext = async (context, checkpoint, parentContext, entityId,
1615
1732
  let payloadToCheckpoint = serializedResult;
1616
1733
  let replayChildren = false;
1617
1734
  if (serializedResult &&
1618
- Buffer.byteLength(serializedResult, "utf8") > CHECKPOINT_SIZE_LIMIT) {
1735
+ Buffer.byteLength(serializedResult, "utf8") > CHECKPOINT_SIZE_LIMIT_BYTES) {
1619
1736
  replayChildren = true;
1620
1737
  // Use summary generator if provided, otherwise use empty string
1621
1738
  if (options?.summaryGenerator) {
@@ -1628,50 +1745,63 @@ const executeChildContext = async (context, checkpoint, parentContext, entityId,
1628
1745
  entityId,
1629
1746
  name,
1630
1747
  payloadSize: Buffer.byteLength(serializedResult, "utf8"),
1631
- limit: CHECKPOINT_SIZE_LIMIT,
1748
+ limit: CHECKPOINT_SIZE_LIMIT_BYTES,
1749
+ });
1750
+ }
1751
+ // Mark this run-in-child-context as finished to prevent descendant operations (only for non-virtual)
1752
+ if (!isVirtual) {
1753
+ checkpoint.markAncestorFinished(entityId);
1754
+ const subType = options?.subType || exports.OperationSubType.RUN_IN_CHILD_CONTEXT;
1755
+ checkpoint.checkpoint(entityId, {
1756
+ Id: entityId,
1757
+ ParentId: parentId,
1758
+ Action: clientLambda.OperationAction.SUCCEED,
1759
+ SubType: subType,
1760
+ Type: clientLambda.OperationType.CONTEXT,
1761
+ Payload: payloadToCheckpoint,
1762
+ ContextOptions: replayChildren ? { ReplayChildren: true } : undefined,
1763
+ Name: name,
1764
+ });
1765
+ log("✅", "Child context completed successfully:", {
1766
+ entityId,
1767
+ name,
1768
+ });
1769
+ }
1770
+ else {
1771
+ log("✅", "Virtual child context completed successfully:", {
1772
+ entityId,
1773
+ name,
1632
1774
  });
1633
1775
  }
1634
- // Mark this run-in-child-context as finished to prevent descendant operations
1635
- checkpoint.markAncestorFinished(entityId);
1636
- const subType = options?.subType || exports.OperationSubType.RUN_IN_CHILD_CONTEXT;
1637
- checkpoint.checkpoint(entityId, {
1638
- Id: entityId,
1639
- ParentId: parentId,
1640
- Action: clientLambda.OperationAction.SUCCEED,
1641
- SubType: subType,
1642
- Type: clientLambda.OperationType.CONTEXT,
1643
- Payload: payloadToCheckpoint,
1644
- ContextOptions: replayChildren ? { ReplayChildren: true } : undefined,
1645
- Name: name,
1646
- });
1647
- log("✅", "Child context completed successfully:", {
1648
- entityId,
1649
- name,
1650
- });
1651
1776
  return result;
1652
1777
  }
1653
1778
  catch (error) {
1654
- log("❌", "Child context failed:", {
1779
+ log("❌", isVirtual ? "Virtual child context failed:" : "Child context failed:", {
1655
1780
  entityId,
1656
1781
  name,
1657
1782
  error,
1658
1783
  });
1659
- // Mark this run-in-child-context as finished to prevent descendant operations
1660
- checkpoint.markAncestorFinished(entityId);
1661
- // Always checkpoint failures
1662
- const subType = options?.subType || exports.OperationSubType.RUN_IN_CHILD_CONTEXT;
1663
- checkpoint.checkpoint(entityId, {
1664
- Id: entityId,
1665
- ParentId: parentId,
1666
- Action: clientLambda.OperationAction.FAIL,
1667
- SubType: subType,
1668
- Type: clientLambda.OperationType.CONTEXT,
1669
- Error: createErrorObjectFromError(error),
1670
- Name: name,
1671
- });
1672
- // Reconstruct error from ErrorObject for deterministic behavior
1784
+ // Mark this run-in-child-context as finished and checkpoint failure (only for non-virtual)
1785
+ if (!isVirtual) {
1786
+ checkpoint.markAncestorFinished(entityId);
1787
+ const subType = options?.subType || exports.OperationSubType.RUN_IN_CHILD_CONTEXT;
1788
+ checkpoint.checkpoint(entityId, {
1789
+ Id: entityId,
1790
+ ParentId: parentId,
1791
+ Action: clientLambda.OperationAction.FAIL,
1792
+ SubType: subType,
1793
+ Type: clientLambda.OperationType.CONTEXT,
1794
+ Error: createErrorObjectFromError(error),
1795
+ Name: name,
1796
+ });
1797
+ }
1798
+ // Always wrap in ChildContextError for consistent error handling
1673
1799
  const errorObject = createErrorObjectFromError(error);
1674
1800
  const reconstructedError = DurableOperationError.fromErrorObject(errorObject);
1801
+ // Use errorMapper if provided, otherwise wrap in ChildContextError
1802
+ if (errorMapper) {
1803
+ throw errorMapper(reconstructedError);
1804
+ }
1675
1805
  throw new ChildContextError(reconstructedError.message, reconstructedError);
1676
1806
  }
1677
1807
  };
@@ -1778,7 +1908,7 @@ const createWaitHandler = (context, checkpoint, createStepId, parentId, checkAnd
1778
1908
  return waitHandler;
1779
1909
  };
1780
1910
 
1781
- const createWaitForConditionHandler = (context, checkpoint, createStepId, logger, parentId) => {
1911
+ const createWaitForConditionHandler = (context, checkpoint, createStepId, logger, parentId, getDefaultSerdes) => {
1782
1912
  return (nameOrCheck, checkOrConfig, maybeConfig) => {
1783
1913
  let name;
1784
1914
  let check;
@@ -1796,7 +1926,7 @@ const createWaitForConditionHandler = (context, checkpoint, createStepId, logger
1796
1926
  throw new Error("waitForCondition requires config with waitStrategy and initialState");
1797
1927
  }
1798
1928
  const stepId = createStepId();
1799
- const serdes = config.serdes || defaultSerdes;
1929
+ const serdes = config.serdes || (getDefaultSerdes ? getDefaultSerdes() : defaultSerdes);
1800
1930
  const phase1Promise = (async () => {
1801
1931
  let stepData = context.getStepData(stepId);
1802
1932
  // Check if already completed
@@ -2001,7 +2131,7 @@ const createPassThroughSerdes = () => ({
2001
2131
  serialize: async (value) => value,
2002
2132
  deserialize: async (data) => data,
2003
2133
  });
2004
- const createCallback = (context, checkpoint, createStepId, checkAndUpdateReplayMode, parentId) => {
2134
+ const createCallback = (context, checkpoint, createStepId, checkAndUpdateReplayMode, parentId, getDefaultCallbackDeserializer) => {
2005
2135
  return (nameOrConfig, maybeConfig) => {
2006
2136
  let name;
2007
2137
  let config;
@@ -2013,7 +2143,10 @@ const createCallback = (context, checkpoint, createStepId, checkAndUpdateReplayM
2013
2143
  config = nameOrConfig;
2014
2144
  }
2015
2145
  const stepId = createStepId();
2016
- const serdes = config?.serdes || createPassThroughSerdes();
2146
+ const serdes = config?.serdes ||
2147
+ (getDefaultCallbackDeserializer
2148
+ ? getDefaultCallbackDeserializer()
2149
+ : createPassThroughSerdes());
2017
2150
  // Phase 1: Setup and checkpoint
2018
2151
  let isCompleted = false;
2019
2152
  const phase1Promise = (async () => {
@@ -2105,16 +2238,22 @@ const createCallback = (context, checkpoint, createStepId, checkAndUpdateReplayM
2105
2238
  const resolvedPromise = new DurablePromise(async () => deserializedResult);
2106
2239
  return [resolvedPromise, callbackData.CallbackId];
2107
2240
  }
2108
- // Handle failure
2241
+ // Handle failure or timeout
2109
2242
  const error = stepData?.CallbackDetails?.Error;
2243
+ const isTimeout = stepData?.Status === clientLambda.OperationStatus.TIMED_OUT;
2110
2244
  const callbackError = error
2111
2245
  ? (() => {
2112
2246
  const cause = new Error(error.ErrorMessage);
2113
2247
  cause.name = error.ErrorType || "Error";
2114
2248
  cause.stack = error.StackTrace?.join("\n");
2249
+ if (isTimeout) {
2250
+ return new CallbackTimeoutError(error.ErrorMessage || "Callback timed out", cause, error.ErrorData);
2251
+ }
2115
2252
  return new CallbackError(error.ErrorMessage || "Callback failed", cause, error.ErrorData);
2116
2253
  })()
2117
- : new CallbackError("Callback failed");
2254
+ : isTimeout
2255
+ ? new CallbackTimeoutError("Callback timed out")
2256
+ : new CallbackError("Callback failed");
2118
2257
  const rejectedPromise = new DurablePromise(async () => {
2119
2258
  throw callbackError;
2120
2259
  });
@@ -2134,7 +2273,7 @@ const createCallback = (context, checkpoint, createStepId, checkAndUpdateReplayM
2134
2273
  };
2135
2274
  };
2136
2275
 
2137
- const createWaitForCallbackHandler = (context, getNextStepId, runInChildContext) => {
2276
+ const createWaitForCallbackHandler = (context, getNextStepId, runInChildContext, getDefaultCallbackDeserializer) => {
2138
2277
  return (nameOrSubmitter, submitterOrConfig, maybeConfig) => {
2139
2278
  let name;
2140
2279
  let submitter;
@@ -2169,11 +2308,16 @@ const createWaitForCallbackHandler = (context, getNextStepId, runInChildContext)
2169
2308
  });
2170
2309
  // Use runInChildContext to ensure proper ID generation and isolation
2171
2310
  const childFunction = async (childCtx) => {
2172
- // Convert WaitForCallbackConfig to CreateCallbackConfig
2173
- const createCallbackConfig = config
2311
+ // Convert WaitForCallbackConfig to CreateCallbackConfig.
2312
+ // When a defaultCallbackDeserializer is configured, force passthrough serdes
2313
+ // on the inner createCallback so the raw string is preserved for phase 2.
2314
+ const createCallbackConfig = config || getDefaultCallbackDeserializer
2174
2315
  ? {
2175
- timeout: config.timeout,
2176
- heartbeatTimeout: config.heartbeatTimeout,
2316
+ timeout: config?.timeout,
2317
+ heartbeatTimeout: config?.heartbeatTimeout,
2318
+ ...(getDefaultCallbackDeserializer && {
2319
+ serdes: createPassThroughSerdes(),
2320
+ }),
2177
2321
  }
2178
2322
  : undefined;
2179
2323
  // Create callback and get the promise + callbackId
@@ -2211,6 +2355,26 @@ const createWaitForCallbackHandler = (context, getNextStepId, runInChildContext)
2211
2355
  return {
2212
2356
  result: await runInChildContext(name, childFunction, {
2213
2357
  subType: exports.OperationSubType.WAIT_FOR_CALLBACK,
2358
+ // When a defaultCallbackDeserializer is configured, use passthrough serdes
2359
+ // so the raw callback string is preserved through the runInChildContext
2360
+ // round-trip and phase 2 can apply the deserializer exactly once.
2361
+ // Without this, defaultSerdes (JSON) would add an extra encode/decode layer.
2362
+ ...(getDefaultCallbackDeserializer && {
2363
+ serdes: createPassThroughSerdes(),
2364
+ }),
2365
+ errorMapper: (originalError) => {
2366
+ // Pass through callback errors directly (both timeout and failure)
2367
+ if (originalError.errorType === "CallbackTimeoutError" ||
2368
+ originalError.errorType === "CallbackError") {
2369
+ return originalError;
2370
+ }
2371
+ // Map step errors to CallbackSubmitterError
2372
+ if (originalError.errorType === "StepError") {
2373
+ return new CallbackSubmitterError(originalError.message, originalError);
2374
+ }
2375
+ // Wrap other errors in ChildContextError
2376
+ return new ChildContextError(originalError.message, originalError);
2377
+ },
2214
2378
  }),
2215
2379
  stepId,
2216
2380
  };
@@ -2222,7 +2386,10 @@ const createWaitForCallbackHandler = (context, getNextStepId, runInChildContext)
2222
2386
  return new DurablePromise(async () => {
2223
2387
  const { result, stepId } = await phase1Promise;
2224
2388
  // Always deserialize the result since it's a string
2225
- return (await safeDeserialize(config?.serdes ?? createPassThroughSerdes(), result, stepId, name, context.terminationManager, context.durableExecutionArn));
2389
+ return (await safeDeserialize(config?.serdes ??
2390
+ (getDefaultCallbackDeserializer
2391
+ ? getDefaultCallbackDeserializer()
2392
+ : createPassThroughSerdes()), result, stepId, name, context.terminationManager, context.durableExecutionArn));
2226
2393
  });
2227
2394
  };
2228
2395
  };
@@ -2306,6 +2473,7 @@ const createMapHandler = (context, executeConcurrently) => {
2306
2473
  completionConfig: config?.completionConfig,
2307
2474
  serdes: config?.serdes,
2308
2475
  itemSerdes: config?.itemSerdes,
2476
+ nesting: config?.nesting,
2309
2477
  });
2310
2478
  log("🗺️", "Map operation completed successfully:", {
2311
2479
  resultCount: result.totalCount,
@@ -2386,6 +2554,7 @@ const createParallelHandler = (context, executeConcurrently) => {
2386
2554
  completionConfig: config?.completionConfig,
2387
2555
  serdes: config?.serdes,
2388
2556
  itemSerdes: config?.itemSerdes,
2557
+ nesting: config?.nesting,
2389
2558
  });
2390
2559
  log("🔀", "Parallel operation completed successfully:", {
2391
2560
  resultCount: result.totalCount,
@@ -2447,13 +2616,7 @@ function createErrorAwareSerdes() {
2447
2616
  : undefined,
2448
2617
  };
2449
2618
  }
2450
- // No-retry strategy for promise combinators
2451
- const stepConfig = {
2452
- retryStrategy: () => ({
2453
- shouldRetry: false,
2454
- }),
2455
- };
2456
- const createPromiseHandler = (step) => {
2619
+ const createPromiseHandler = (runInChildContext) => {
2457
2620
  const parseParams = (nameOrPromises, maybePromises) => {
2458
2621
  if (typeof nameOrPromises === "string" || nameOrPromises === undefined) {
2459
2622
  return { name: nameOrPromises, promises: maybePromises };
@@ -2463,32 +2626,38 @@ const createPromiseHandler = (step) => {
2463
2626
  const all = (nameOrPromises, maybePromises) => {
2464
2627
  return new DurablePromise(async () => {
2465
2628
  const { name, promises } = parseParams(nameOrPromises, maybePromises);
2466
- // Wrap Promise.all execution in a step for persistence
2467
- return await step(name, () => Promise.all(promises), stepConfig);
2629
+ // Wrap Promise.all execution in a child context for persistence
2630
+ return await runInChildContext(name, () => Promise.all(promises), {
2631
+ errorMapper: (error) => new PromiseCombinatorError(error.message, error),
2632
+ });
2468
2633
  });
2469
2634
  };
2470
2635
  const allSettled = (nameOrPromises, maybePromises) => {
2471
2636
  return new DurablePromise(async () => {
2472
2637
  const { name, promises } = parseParams(nameOrPromises, maybePromises);
2473
- // Wrap Promise.allSettled execution in a step for persistence
2474
- return await step(name, () => Promise.allSettled(promises), {
2475
- ...stepConfig,
2638
+ // Wrap Promise.allSettled execution in a child context for persistence
2639
+ return await runInChildContext(name, () => Promise.allSettled(promises), {
2476
2640
  serdes: createErrorAwareSerdes(),
2641
+ errorMapper: (error) => new PromiseCombinatorError(error.message, error),
2477
2642
  });
2478
2643
  });
2479
2644
  };
2480
2645
  const any = (nameOrPromises, maybePromises) => {
2481
2646
  return new DurablePromise(async () => {
2482
2647
  const { name, promises } = parseParams(nameOrPromises, maybePromises);
2483
- // Wrap Promise.any execution in a step for persistence
2484
- return await step(name, () => Promise.any(promises), stepConfig);
2648
+ // Wrap Promise.any execution in a child context for persistence
2649
+ return await runInChildContext(name, () => Promise.any(promises), {
2650
+ errorMapper: (error) => new PromiseCombinatorError(error.message, error),
2651
+ });
2485
2652
  });
2486
2653
  };
2487
2654
  const race = (nameOrPromises, maybePromises) => {
2488
2655
  return new DurablePromise(async () => {
2489
2656
  const { name, promises } = parseParams(nameOrPromises, maybePromises);
2490
- // Wrap Promise.race execution in a step for persistence
2491
- return await step(name, () => Promise.race(promises), stepConfig);
2657
+ // Wrap Promise.race execution in a child context for persistence
2658
+ return await runInChildContext(name, () => Promise.race(promises), {
2659
+ errorMapper: (error) => new PromiseCombinatorError(error.message, error),
2660
+ });
2492
2661
  });
2493
2662
  };
2494
2663
  return {
@@ -2578,9 +2747,11 @@ function restoreBatchResult(data) {
2578
2747
  class ConcurrencyController {
2579
2748
  operationName;
2580
2749
  skipNextOperation;
2581
- constructor(operationName, skipNextOperation) {
2750
+ getDefaultSerdes;
2751
+ constructor(operationName, skipNextOperation, getDefaultSerdes) {
2582
2752
  this.operationName = operationName;
2583
2753
  this.skipNextOperation = skipNextOperation;
2754
+ this.getDefaultSerdes = getDefaultSerdes;
2584
2755
  }
2585
2756
  isChildEntityCompleted(executionContext, parentEntityId, completedCount) {
2586
2757
  const childEntityId = `${parentEntityId}-${completedCount + 1}`;
@@ -2638,7 +2809,8 @@ class ConcurrencyController {
2638
2809
  const summaryPayload = stepData?.ContextDetails?.Result;
2639
2810
  if (summaryPayload) {
2640
2811
  try {
2641
- const serdes = config.serdes || defaultSerdes;
2812
+ const serdes = config.serdes ||
2813
+ (this.getDefaultSerdes ? this.getDefaultSerdes() : defaultSerdes);
2642
2814
  const parsedSummary = await serdes.deserialize(summaryPayload, {
2643
2815
  entityId: entityId,
2644
2816
  durableExecutionArn: executionContext.durableExecutionArn,
@@ -2701,7 +2873,11 @@ class ConcurrencyController {
2701
2873
  continue;
2702
2874
  }
2703
2875
  try {
2704
- const result = await parentContext.runInChildContext(item.name || item.id, (childContext) => executor(item, childContext), { subType: config.iterationSubType, serdes: config.itemSerdes });
2876
+ const result = await parentContext.runInChildContext(item.name || item.id, (childContext) => executor(item, childContext), {
2877
+ subType: config.iterationSubType,
2878
+ serdes: config.itemSerdes,
2879
+ virtualContext: config.nesting === exports.NestingType.FLAT,
2880
+ });
2705
2881
  resultItems.push({
2706
2882
  result,
2707
2883
  index: item.index,
@@ -2806,7 +2982,11 @@ class ConcurrencyController {
2806
2982
  itemName: item.name,
2807
2983
  });
2808
2984
  parentContext
2809
- .runInChildContext(item.name || item.id, (childContext) => executor(item, childContext), { subType: config.iterationSubType, serdes: config.itemSerdes })
2985
+ .runInChildContext(item.name || item.id, (childContext) => executor(item, childContext), {
2986
+ subType: config.iterationSubType,
2987
+ serdes: config.itemSerdes,
2988
+ virtualContext: config.nesting === exports.NestingType.FLAT,
2989
+ })
2810
2990
  .then((result) => {
2811
2991
  resultItems[index] = {
2812
2992
  result,
@@ -2876,7 +3056,7 @@ class ConcurrencyController {
2876
3056
  });
2877
3057
  }
2878
3058
  }
2879
- const createConcurrentExecutionHandler = (context, runInChildContext, skipNextOperation) => {
3059
+ const createConcurrentExecutionHandler = (context, runInChildContext, skipNextOperation, getDefaultSerdes) => {
2880
3060
  return (nameOrItems, itemsOrExecutor, executorOrConfig, maybeConfig) => {
2881
3061
  // Phase 1: Start execution immediately
2882
3062
  const phase1Promise = (async () => {
@@ -2912,7 +3092,7 @@ const createConcurrentExecutionHandler = (context, runInChildContext, skipNextOp
2912
3092
  throw new Error(`Invalid maxConcurrency: ${config.maxConcurrency}. Must be a positive number or undefined for unlimited concurrency.`);
2913
3093
  }
2914
3094
  const executeOperation = async (executionContext) => {
2915
- const concurrencyController = new ConcurrencyController("concurrent-execution", skipNextOperation);
3095
+ const concurrencyController = new ConcurrencyController("concurrent-execution", skipNextOperation, getDefaultSerdes);
2916
3096
  // Access durableExecutionMode from the context - it's set by runInChildContext
2917
3097
  // based on determineChildReplayMode logic
2918
3098
  const durableExecutionMode = executionContext.durableExecutionMode;
@@ -3035,6 +3215,9 @@ class DurableContextImpl {
3035
3215
  _parentId;
3036
3216
  modeManagement;
3037
3217
  durableExecution;
3218
+ _defaultSerdes = defaultSerdes;
3219
+ _defaultCallbackDeserializer = createPassThroughSerdes();
3220
+ _customCallbackDeserializerSet = false;
3038
3221
  logger;
3039
3222
  executionContext;
3040
3223
  constructor(_executionContext, lambdaContext, durableExecutionMode, inheritedLogger, stepPrefix, durableExecution, parentId) {
@@ -3175,14 +3358,14 @@ class DurableContextImpl {
3175
3358
  step(nameOrFn, fnOrOptions, maybeOptions) {
3176
3359
  validateContextUsage(this._stepPrefix, "step", this._executionContext.terminationManager);
3177
3360
  return this.withDurableModeManagement(() => {
3178
- const stepHandler = createStepHandler(this._executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), this.durableLogger, this._parentId);
3361
+ const stepHandler = createStepHandler(this._executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), this.durableLogger, this._parentId, () => this._defaultSerdes);
3179
3362
  return stepHandler(nameOrFn, fnOrOptions, maybeOptions);
3180
3363
  });
3181
3364
  }
3182
3365
  invoke(nameOrFuncId, funcIdOrInput, inputOrConfig, maybeConfig) {
3183
3366
  validateContextUsage(this._stepPrefix, "invoke", this._executionContext.terminationManager);
3184
3367
  return this.withDurableModeManagement(() => {
3185
- const invokeHandler = createInvokeHandler(this._executionContext, this.checkpoint, this.createStepId.bind(this), this._parentId, this.checkAndUpdateReplayMode.bind(this));
3368
+ const invokeHandler = createInvokeHandler(this._executionContext, this.checkpoint, this.createStepId.bind(this), this._parentId, this.checkAndUpdateReplayMode.bind(this), () => this._defaultSerdes);
3186
3369
  return invokeHandler(...[
3187
3370
  nameOrFuncId,
3188
3371
  funcIdOrInput,
@@ -3196,7 +3379,17 @@ class DurableContextImpl {
3196
3379
  return this.withDurableModeManagement(() => {
3197
3380
  const blockHandler = createRunInChildContextHandler(this._executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), () => this.durableLogger,
3198
3381
  // Adapter function to maintain compatibility
3199
- (executionContext, parentContext, durableExecutionMode, inheritedLogger, stepPrefix, _checkpointToken, parentId) => createDurableContext(executionContext, parentContext, durableExecutionMode, inheritedLogger, stepPrefix, this.durableExecution, parentId), this._parentId);
3382
+ (executionContext, parentContext, durableExecutionMode, inheritedLogger, stepPrefix, _checkpointToken, parentId) => {
3383
+ const childCtx = createDurableContext(executionContext, parentContext, durableExecutionMode, inheritedLogger, stepPrefix, this.durableExecution, parentId);
3384
+ // Propagate serdes config to child context
3385
+ childCtx.configureSerdes({
3386
+ defaultSerdes: this._defaultSerdes,
3387
+ ...(this._customCallbackDeserializerSet && {
3388
+ defaultCallbackDeserializer: this._defaultCallbackDeserializer,
3389
+ }),
3390
+ });
3391
+ return childCtx;
3392
+ }, this._parentId, () => this._defaultSerdes);
3200
3393
  return blockHandler(nameOrFn, fnOrOptions, maybeOptions);
3201
3394
  });
3202
3395
  }
@@ -3234,24 +3427,39 @@ class DurableContextImpl {
3234
3427
  this.modeAwareLoggingEnabled = config.modeAware;
3235
3428
  }
3236
3429
  }
3430
+ configureSerdes(config) {
3431
+ if (config.defaultSerdes !== undefined) {
3432
+ this._defaultSerdes = config.defaultSerdes;
3433
+ }
3434
+ if (config.defaultCallbackDeserializer !== undefined) {
3435
+ this._defaultCallbackDeserializer = config.defaultCallbackDeserializer;
3436
+ this._customCallbackDeserializerSet = true;
3437
+ }
3438
+ }
3237
3439
  createCallback(nameOrConfig, maybeConfig) {
3238
3440
  validateContextUsage(this._stepPrefix, "createCallback", this._executionContext.terminationManager);
3239
3441
  return this.withDurableModeManagement(() => {
3240
- const callbackFactory = createCallback(this._executionContext, this.checkpoint, this.createStepId.bind(this), this.checkAndUpdateReplayMode.bind(this), this._parentId);
3442
+ const callbackFactory = createCallback(this._executionContext, this.checkpoint, this.createStepId.bind(this), this.checkAndUpdateReplayMode.bind(this), this._parentId, () => this._defaultCallbackDeserializer);
3241
3443
  return callbackFactory(nameOrConfig, maybeConfig);
3242
3444
  });
3243
3445
  }
3244
3446
  waitForCallback(nameOrSubmitter, submitterOrConfig, maybeConfig) {
3245
3447
  validateContextUsage(this._stepPrefix, "waitForCallback", this._executionContext.terminationManager);
3246
3448
  return this.withDurableModeManagement(() => {
3247
- const waitForCallbackHandler = createWaitForCallbackHandler(this._executionContext, this.getNextStepId.bind(this), this.runInChildContext.bind(this));
3449
+ const waitForCallbackHandler = createWaitForCallbackHandler(this._executionContext, this.getNextStepId.bind(this), this.runInChildContext.bind(this),
3450
+ // Only pass the getter when the user has explicitly configured a custom
3451
+ // deserializer. The default is createPassThroughSerdes() which matches
3452
+ // the original behavior, so no injection is needed in that case.
3453
+ this._customCallbackDeserializerSet
3454
+ ? () => this._defaultCallbackDeserializer
3455
+ : undefined);
3248
3456
  return waitForCallbackHandler(nameOrSubmitter, submitterOrConfig, maybeConfig);
3249
3457
  });
3250
3458
  }
3251
3459
  waitForCondition(nameOrCheckFunc, checkFuncOrConfig, maybeConfig) {
3252
3460
  validateContextUsage(this._stepPrefix, "waitForCondition", this._executionContext.terminationManager);
3253
3461
  return this.withDurableModeManagement(() => {
3254
- const waitForConditionHandler = createWaitForConditionHandler(this._executionContext, this.checkpoint, this.createStepId.bind(this), this.durableLogger, this._parentId);
3462
+ const waitForConditionHandler = createWaitForConditionHandler(this._executionContext, this.checkpoint, this.createStepId.bind(this), this.durableLogger, this._parentId, () => this._defaultSerdes);
3255
3463
  return typeof nameOrCheckFunc === "string" ||
3256
3464
  nameOrCheckFunc === undefined
3257
3465
  ? waitForConditionHandler(nameOrCheckFunc, checkFuncOrConfig, maybeConfig)
@@ -3275,7 +3483,7 @@ class DurableContextImpl {
3275
3483
  _executeConcurrently(nameOrItems, itemsOrExecutor, executorOrConfig, maybeConfig) {
3276
3484
  validateContextUsage(this._stepPrefix, "_executeConcurrently", this._executionContext.terminationManager);
3277
3485
  return this.withDurableModeManagement(() => {
3278
- const concurrentExecutionHandler = createConcurrentExecutionHandler(this._executionContext, this.runInChildContext.bind(this), this.skipNextOperation.bind(this));
3486
+ const concurrentExecutionHandler = createConcurrentExecutionHandler(this._executionContext, this.runInChildContext.bind(this), this.skipNextOperation.bind(this), () => this._defaultSerdes);
3279
3487
  const promise = concurrentExecutionHandler(nameOrItems, itemsOrExecutor, executorOrConfig, maybeConfig);
3280
3488
  // Prevent unhandled promise rejections
3281
3489
  promise?.catch(() => { });
@@ -3283,7 +3491,7 @@ class DurableContextImpl {
3283
3491
  });
3284
3492
  }
3285
3493
  get promise() {
3286
- return createPromiseHandler(this.step.bind(this));
3494
+ return createPromiseHandler(this.runInChildContext.bind(this));
3287
3495
  }
3288
3496
  }
3289
3497
  const createDurableContext = (executionContext, parentContext, durableExecutionMode, inheritedLogger, stepPrefix, durableExecution, parentId) => {
@@ -3313,6 +3521,22 @@ class CheckpointUnrecoverableExecutionError extends UnrecoverableExecutionError
3313
3521
  }
3314
3522
  }
3315
3523
 
3524
+ // KMS errors from Lambda that indicate customer-caused key misconfiguration.
3525
+ // These arrive as 502 errors but are non-retryable.
3526
+ const NON_RETRYABLE_CUSTOMER_ERRORS = new Set([
3527
+ "KMSAccessDeniedException",
3528
+ "KMSDisabledException",
3529
+ "KMSInvalidStateException",
3530
+ "KMSNotFoundException",
3531
+ ]);
3532
+ /**
3533
+ * Returns true if the error is a non-retryable customer error (e.g., KMS key misconfiguration).
3534
+ */
3535
+ function isNonRetryableCustomerError(error) {
3536
+ const name = error?.name;
3537
+ return !!name && NON_RETRYABLE_CUSTOMER_ERRORS.has(name);
3538
+ }
3539
+
3316
3540
  const STEP_DATA_UPDATED_EVENT = "stepDataUpdated";
3317
3541
  const TERMINAL_STATUSES = [
3318
3542
  clientLambda.OperationStatus.SUCCEEDED,
@@ -3478,6 +3702,11 @@ class CheckpointManager {
3478
3702
  statusCode !== 429) {
3479
3703
  return new CheckpointUnrecoverableExecutionError(`Checkpoint failed: ${errorMessage}`, originalError);
3480
3704
  }
3705
+ // For example: KMS errors from Lambda arrive as 502 errors. These indicate customer-caused
3706
+ // KMS key misconfiguration and should not be retried — treat as execution error.
3707
+ if (isNonRetryableCustomerError(error)) {
3708
+ return new CheckpointUnrecoverableExecutionError(`Checkpoint failed: ${errorMessage}`, originalError);
3709
+ }
3481
3710
  return new CheckpointUnrecoverableInvocationError(`Checkpoint failed: ${errorMessage}`, originalError);
3482
3711
  }
3483
3712
  async processQueue() {
@@ -4245,17 +4474,27 @@ const createDefaultLogger = (executionContext) => {
4245
4474
 
4246
4475
  /**
4247
4476
  * SDK metadata injected by Rollup at build time from package.json.
4477
+ * These values are inserted into UserAgent headers.
4478
+ *
4479
+ * At build time, Rollup replaces "2.0.0-alpha.1"
4480
+ * with actual values from package.json.
4248
4481
  *
4249
- * At build time, Rollup replaces "@aws/durable-execution-sdk-js" and
4250
- * "1.1.2" with actual values from package.json.
4482
+ * SDK_NAME is a fixed string matching the cross-SDK convention:
4483
+ * aws-durable-execution-sdk-\{language\}
4484
+ * Alternate version if SDK path is within the Lambda bundled runtime.
4251
4485
  *
4252
4486
  * Defaults are provided for test environments where Rollup doesn't run
4253
4487
  * and process.env values are undefined.
4254
4488
  *
4255
4489
  * @internal
4256
4490
  */
4257
- const SDK_NAME = "@aws/durable-execution-sdk-js";
4258
- const SDK_VERSION = "1.1.2";
4491
+ const runtimeDir = process.env.LAMBDA_RUNTIME_DIR || "/var/runtime";
4492
+ const isRuntimeBundled = typeof __dirname !== "undefined" && __dirname.startsWith(runtimeDir);
4493
+ const SDK_NAME = "aws-durable-execution-sdk-js";
4494
+ const baseVersion = "2.0.0-alpha.1";
4495
+ const SDK_VERSION = isRuntimeBundled
4496
+ ? `${baseVersion}-bundled`
4497
+ : baseVersion;
4259
4498
 
4260
4499
  let defaultLambdaClient;
4261
4500
  /**
@@ -4699,11 +4938,293 @@ function validateDurableExecutionEvent(event) {
4699
4938
  const withDurableExecution = (handler, config) => {
4700
4939
  return async (event, context) => {
4701
4940
  validateDurableExecutionEvent(event);
4702
- const { executionContext, durableExecutionMode, checkpointToken } = await initializeExecutionContext(event, context, config?.client);
4703
- return runHandler(event, context, executionContext, durableExecutionMode, checkpointToken, handler);
4941
+ try {
4942
+ const { executionContext, durableExecutionMode, checkpointToken } = await initializeExecutionContext(event, context, config?.client);
4943
+ return await runHandler(event, context, executionContext, durableExecutionMode, checkpointToken, handler);
4944
+ }
4945
+ catch (error) {
4946
+ // Non-retryable customer errors (e.g., KMS key misconfiguration) should
4947
+ // fail the execution immediately rather than retrying the invocation.
4948
+ if (isNonRetryableCustomerError(error)) {
4949
+ return {
4950
+ Status: exports.InvocationStatus.FAILED,
4951
+ Error: createErrorObjectFromError(error),
4952
+ };
4953
+ }
4954
+ throw error;
4955
+ }
4704
4956
  };
4705
4957
  };
4706
4958
 
4959
+ /**
4960
+ * Controls whether a preview field is matched by name anywhere in the object
4961
+ * tree, or by exact dot-notation path from the root.
4962
+ *
4963
+ * @public
4964
+ */
4965
+ exports.FieldMatchMode = void 0;
4966
+ (function (FieldMatchMode) {
4967
+ /** Match the field name at any depth in the object tree (default). */
4968
+ FieldMatchMode["ANYWHERE"] = "ANYWHERE";
4969
+ /**
4970
+ * Match by exact dot-notation path from root.
4971
+ * A single segment (e.g. `"email"`) matches only the root-level field.
4972
+ * A dotted path (e.g. `"user.email"`) matches that exact nested location.
4973
+ */
4974
+ FieldMatchMode["PATH"] = "PATH";
4975
+ })(exports.FieldMatchMode || (exports.FieldMatchMode = {}));
4976
+ /**
4977
+ * Controls which fields are included in the preview by default.
4978
+ *
4979
+ * @public
4980
+ */
4981
+ exports.PreviewMode = void 0;
4982
+ (function (PreviewMode) {
4983
+ /** Include all fields, then apply `exclude` and `mask` rules. */
4984
+ PreviewMode["INCLUDE_ALL"] = "INCLUDE_ALL";
4985
+ /** Exclude all fields, then apply `include` and `mask` rules. */
4986
+ PreviewMode["EXCLUDE_ALL"] = "EXCLUDE_ALL";
4987
+ })(exports.PreviewMode || (exports.PreviewMode = {}));
4988
+ /** Returns true if the field at `path` (dot-notation) matches the given PreviewField rule. */
4989
+ function fieldMatches(path, field) {
4990
+ const mode = field.match ?? exports.FieldMatchMode.ANYWHERE;
4991
+ if (mode === exports.FieldMatchMode.PATH) {
4992
+ return path === field.name;
4993
+ }
4994
+ return path.split(".").includes(field.name);
4995
+ }
4996
+ function isMatched(path, fields) {
4997
+ return fields?.some((f) => fieldMatches(path, f)) ?? false;
4998
+ }
4999
+ /**
5000
+ * Builds a preview object from `value` according to `config`.
5001
+ *
5002
+ * Traverses the object tree and collects fields based on the include/exclude/mask
5003
+ * rules in `config`. The result is a nested object mirroring the original structure,
5004
+ * capped at `config.maxPreviewBytes` (default 4096 bytes).
5005
+ *
5006
+ * Priority rules:
5007
+ * - `exclude` always wins — excluded fields are never shown, even if in `mask`
5008
+ * - `mask` implies visibility — masked fields are shown (with `maskString`) unless excluded
5009
+ *
5010
+ * Limitations:
5011
+ * - Field names containing dots are not supported (indistinguishable from path separators)
5012
+ * - Array structure is not preserved — fields from array elements are merged into a plain object
5013
+ * - When array elements have heterogeneous shapes at the same field path, later elements
5014
+ * overwrite earlier primitives in the preview (e.g. `[{ user: "arb" }, { user: { email: "x" } }]`
5015
+ * produces `{ user: { email: "x" } }` — `"arb"` is lost)
5016
+ *
5017
+ * @example
5018
+ * ```typescript
5019
+ * const preview = buildPreview(order, {
5020
+ * mode: PreviewMode.EXCLUDE_ALL,
5021
+ * include: [{ name: "id" }, { name: "status" }],
5022
+ * mask: [{ name: "email" }],
5023
+ * });
5024
+ * // { id: "order-123", status: "pending", user: { email: "***" } }
5025
+ * ```
5026
+ *
5027
+ * @public
5028
+ */
5029
+ function buildPreview(value, config) {
5030
+ if (value === null || typeof value !== "object")
5031
+ return undefined;
5032
+ const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
5033
+ const maskString = config.maskString ?? "***";
5034
+ const maxBytes = config.maxPreviewBytes ?? 4096;
5035
+ const pairs = [];
5036
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5037
+ function collect(obj, pathPrefix) {
5038
+ if (obj === null || typeof obj !== "object")
5039
+ return;
5040
+ if (Array.isArray(obj)) {
5041
+ for (const item of obj) {
5042
+ collect(item, pathPrefix);
5043
+ }
5044
+ return;
5045
+ }
5046
+ for (const key of Object.keys(obj)) {
5047
+ if (DANGEROUS_KEYS.has(key))
5048
+ continue;
5049
+ if (key.includes("."))
5050
+ continue;
5051
+ const path = pathPrefix ? `${pathPrefix}.${key}` : key;
5052
+ const masked = isMatched(path, config.mask);
5053
+ const excluded = isMatched(path, config.exclude);
5054
+ const visible = !excluded &&
5055
+ (masked ||
5056
+ (config.mode === exports.PreviewMode.INCLUDE_ALL
5057
+ ? true
5058
+ : isMatched(path, config.include)));
5059
+ if (!visible) {
5060
+ collect(obj[key], path);
5061
+ continue;
5062
+ }
5063
+ if (masked) {
5064
+ pairs.push([path, maskString]);
5065
+ continue;
5066
+ }
5067
+ if (obj[key] !== null && typeof obj[key] === "object") {
5068
+ collect(obj[key], path);
5069
+ }
5070
+ else {
5071
+ pairs.push([path, obj[key]]);
5072
+ }
5073
+ }
5074
+ }
5075
+ collect(value, "");
5076
+ if (pairs.length === 0)
5077
+ return undefined;
5078
+ const accepted = [];
5079
+ let estimatedSize = 2; // "{}"
5080
+ for (const [path, val] of pairs) {
5081
+ const entrySize = Buffer.byteLength(`"${path}":${JSON.stringify(val)},`, "utf-8");
5082
+ if (estimatedSize + entrySize > maxBytes)
5083
+ break;
5084
+ accepted.push([path, val]);
5085
+ estimatedSize += entrySize;
5086
+ }
5087
+ if (accepted.length === 0)
5088
+ return undefined;
5089
+ const result = {};
5090
+ for (const [path, val] of accepted) {
5091
+ const parts = path.split(".");
5092
+ let node = result;
5093
+ for (let i = 0; i < parts.length - 1; i++) {
5094
+ if (typeof node[parts[i]] !== "object" || node[parts[i]] === null) {
5095
+ node[parts[i]] = {};
5096
+ }
5097
+ node = node[parts[i]];
5098
+ }
5099
+ node[parts[parts.length - 1]] = val;
5100
+ }
5101
+ return Object.keys(result).length > 0 ? result : undefined;
5102
+ }
5103
+
5104
+ // Subtract 1KB headroom for the envelope wrapper and other checkpoint metadata
5105
+ const OVERFLOW_THRESHOLD_BYTES = CHECKPOINT_SIZE_LIMIT_BYTES - 1024;
5106
+ /**
5107
+ * Controls when data is written to the filesystem.
5108
+ *
5109
+ * - `ALWAYS`: Every value is written to a file; the checkpoint stores only a file pointer.
5110
+ * Best for consistently large payloads or when you want predictable checkpoint sizes.
5111
+ *
5112
+ * - `OVERFLOW`: Data is written inline (as JSON) unless it exceeds the durable function
5113
+ * checkpoint size limit (~256KB), in which case it overflows to a file.
5114
+ * Best for mixed workloads where most payloads are small.
5115
+ *
5116
+ * @public
5117
+ */
5118
+ exports.FileSystemSerdesMode = void 0;
5119
+ (function (FileSystemSerdesMode) {
5120
+ FileSystemSerdesMode["ALWAYS"] = "ALWAYS";
5121
+ FileSystemSerdesMode["OVERFLOW"] = "OVERFLOW";
5122
+ })(exports.FileSystemSerdesMode || (exports.FileSystemSerdesMode = {}));
5123
+ async function writeToFile(basePath, value, context) {
5124
+ const dir = node_path.join(basePath, encodeURIComponent(context.durableExecutionArn));
5125
+ await promises.mkdir(dir, { recursive: true });
5126
+ const filePath = node_path.join(dir, `${context.entityId}.json`);
5127
+ await promises.writeFile(filePath, JSON.stringify(value), "utf-8");
5128
+ return filePath;
5129
+ }
5130
+ /**
5131
+ * Creates a Serdes that stores serialized values on a durable filesystem.
5132
+ *
5133
+ * **⚠️ WARNING: Do NOT use with Lambda's ephemeral `/tmp` storage.**
5134
+ * Lambda's `/tmp` filesystem is local to a single execution environment and is
5135
+ * not shared across invocations or function instances. On replay, a different
5136
+ * execution environment may be used and the file will not be found, causing
5137
+ * deserialization to fail.
5138
+ *
5139
+ * **Use only with a durable, shared filesystem such as:**
5140
+ * - **Amazon S3 Files** — mount an S3 bucket as a filesystem via the Lambda console or IaC
5141
+ * - **Amazon EFS** — mount an EFS file system to your Lambda function
5142
+ *
5143
+ * Both options provide persistence across invocations and are accessible from
5144
+ * multiple concurrent function instances, which is required for correct replay behavior.
5145
+ *
5146
+ * The checkpoint stores a JSON envelope that is either:
5147
+ * - `{"data":"<inline JSON>"}` — value stored inline (OVERFLOW mode, under threshold)
5148
+ * - `{"file":"<path>"}` — value stored in a file
5149
+ * - `{"file":"<path>","preview":{...}}` — file pointer with inline preview (when preview is configured)
5150
+ *
5151
+ * @param basePath - Directory path where data files will be stored (e.g. `/mnt/s3` for S3 Files, `/mnt/efs` for EFS)
5152
+ * @param config - Optional configuration options
5153
+ * @returns A Serdes that reads/writes JSON files under basePath
5154
+ *
5155
+ * @example
5156
+ * ```typescript
5157
+ * // Always write to S3 Files mount (default)
5158
+ * context.configureSerdes({
5159
+ * defaultSerdes: createFileSystemSerdes("/mnt/s3"),
5160
+ * });
5161
+ *
5162
+ * // Only overflow to filesystem when payload exceeds ~256KB
5163
+ * context.configureSerdes({
5164
+ * defaultSerdes: createFileSystemSerdes("/mnt/s3", { storageMode: FileSystemSerdesMode.OVERFLOW }),
5165
+ * });
5166
+ *
5167
+ * // With preview: show id and masked email in checkpoint
5168
+ * context.configureSerdes({
5169
+ * defaultSerdes: createFileSystemSerdes("/mnt/s3", {
5170
+ * generatePreview: (value) => buildPreview(value, {
5171
+ * mode: PreviewMode.EXCLUDE_ALL,
5172
+ * include: [{ name: "id" }, { name: "status" }],
5173
+ * mask: [{ name: "email" }],
5174
+ * }),
5175
+ * }),
5176
+ * });
5177
+ * ```
5178
+ *
5179
+ * Limitations:
5180
+ * - Field names containing dots are not supported in preview field selectors.
5181
+ * A dot in a field name is indistinguishable from a path separator.
5182
+ * - Array structure is not preserved in preview output — fields from array
5183
+ * elements are merged into a plain object at the array's path.
5184
+ *
5185
+ * @public
5186
+ */
5187
+ function createFileSystemSerdes(basePath, config = {}) {
5188
+ const storageMode = config.storageMode ?? exports.FileSystemSerdesMode.ALWAYS;
5189
+ return {
5190
+ serialize: async (
5191
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5192
+ value, context) => {
5193
+ if (value === undefined)
5194
+ return undefined;
5195
+ if (storageMode === exports.FileSystemSerdesMode.ALWAYS) {
5196
+ const filePath = await writeToFile(basePath, value, context);
5197
+ const preview = config.generatePreview?.(value);
5198
+ const envelope = preview
5199
+ ? { file: filePath, preview }
5200
+ : { file: filePath };
5201
+ return JSON.stringify(envelope);
5202
+ }
5203
+ // OVERFLOW mode: serialize inline first, overflow to file if too large
5204
+ const inlineJson = JSON.stringify(value);
5205
+ if (Buffer.byteLength(inlineJson, "utf-8") > OVERFLOW_THRESHOLD_BYTES) {
5206
+ const filePath = await writeToFile(basePath, value, context);
5207
+ const preview = config.generatePreview?.(value);
5208
+ const envelope = preview
5209
+ ? { file: filePath, preview }
5210
+ : { file: filePath };
5211
+ return JSON.stringify(envelope);
5212
+ }
5213
+ return JSON.stringify({ data: inlineJson });
5214
+ },
5215
+ deserialize: async (data, _context) => {
5216
+ if (data === undefined)
5217
+ return undefined;
5218
+ const envelope = JSON.parse(data);
5219
+ if ("file" in envelope) {
5220
+ const contents = await promises.readFile(envelope.file, "utf-8");
5221
+ return JSON.parse(contents);
5222
+ }
5223
+ return JSON.parse(envelope.data);
5224
+ },
5225
+ };
5226
+ }
5227
+
4707
5228
  const DEFAULT_CONFIG = {
4708
5229
  maxAttempts: 60,
4709
5230
  initialDelay: { seconds: 5 },
@@ -4753,7 +5274,46 @@ const createWaitStrategy = (config) => {
4753
5274
  };
4754
5275
  };
4755
5276
 
5277
+ async function withRetry(context, nameOrFunc, funcOrConfig, maybeConfig) {
5278
+ const hasName = typeof nameOrFunc === "string";
5279
+ const name = hasName ? nameOrFunc : undefined;
5280
+ const func = (hasName ? funcOrConfig : nameOrFunc);
5281
+ const config = (hasName ? maybeConfig : funcOrConfig);
5282
+ if (!config) {
5283
+ throw new TypeError("withRetry: config is required");
5284
+ }
5285
+ const { retryStrategy, wrapWithRunInChildContext = true, childContextConfig, } = config;
5286
+ const runLoop = async (ctx) => {
5287
+ let attempt = 0;
5288
+ while (true) {
5289
+ attempt++;
5290
+ try {
5291
+ return await func(ctx, attempt);
5292
+ }
5293
+ catch (err) {
5294
+ const decision = retryStrategy(err, attempt);
5295
+ if (!decision.shouldRetry)
5296
+ throw err;
5297
+ const delay = decision.delay ?? { seconds: 1 };
5298
+ if (name) {
5299
+ await ctx.wait(`${name}-backoff-${attempt}`, delay);
5300
+ }
5301
+ else {
5302
+ await ctx.wait(delay);
5303
+ }
5304
+ }
5305
+ }
5306
+ };
5307
+ return wrapWithRunInChildContext
5308
+ ? name
5309
+ ? await context.runInChildContext(name, runLoop, childContextConfig)
5310
+ : await context.runInChildContext(runLoop, childContextConfig)
5311
+ : await runLoop(context);
5312
+ }
5313
+
4756
5314
  exports.CallbackError = CallbackError;
5315
+ exports.CallbackSubmitterError = CallbackSubmitterError;
5316
+ exports.CallbackTimeoutError = CallbackTimeoutError;
4757
5317
  exports.ChildContextError = ChildContextError;
4758
5318
  exports.DurableExecutionApiClient = DurableExecutionApiClient;
4759
5319
  exports.DurableExecutionInvocationInputWithClient = DurableExecutionInvocationInputWithClient;
@@ -4763,11 +5323,15 @@ exports.InvokeError = InvokeError;
4763
5323
  exports.StepError = StepError;
4764
5324
  exports.StepInterruptedError = StepInterruptedError;
4765
5325
  exports.WaitForConditionError = WaitForConditionError;
5326
+ exports.buildPreview = buildPreview;
4766
5327
  exports.createClassSerdes = createClassSerdes;
4767
5328
  exports.createClassSerdesWithDates = createClassSerdesWithDates;
5329
+ exports.createFileSystemSerdes = createFileSystemSerdes;
5330
+ exports.createLinearRetryStrategy = createLinearRetryStrategy;
4768
5331
  exports.createRetryStrategy = createRetryStrategy;
4769
5332
  exports.createWaitStrategy = createWaitStrategy;
4770
5333
  exports.defaultSerdes = defaultSerdes;
4771
5334
  exports.retryPresets = retryPresets;
4772
5335
  exports.withDurableExecution = withDurableExecution;
5336
+ exports.withRetry = withRetry;
4773
5337
  //# sourceMappingURL=index.js.map