@aws/durable-execution-sdk-js 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cjs/index.js CHANGED
@@ -614,6 +614,26 @@ class StepInterruptedError extends Error {
614
614
  }
615
615
  }
616
616
 
617
+ /**
618
+ * Shared constants to avoid circular dependencies
619
+ */
620
+ /**
621
+ * Controls whether stack traces are stored in error objects
622
+ * TODO: Accept this as configuration parameter in the future
623
+ */
624
+ /**
625
+ * Checkpoint manager termination cooldown in milliseconds
626
+ * After the last operation completes, the checkpoint manager waits this duration
627
+ * before terminating to allow for any final checkpoint operations
628
+ */
629
+ const CHECKPOINT_TERMINATION_COOLDOWN_MS = 20;
630
+ /**
631
+ * Maximum polling duration in milliseconds (15 minutes)
632
+ * Used to cap setTimeout delays to prevent 32-bit signed integer overflow
633
+ * and limit polling duration for long-running operations
634
+ */
635
+ const MAX_POLL_DURATION_MS = 15 * 60 * 1000;
636
+
617
637
  /**
618
638
  * Base class for all durable operation errors
619
639
  * @public
@@ -1402,6 +1422,7 @@ const createInvokeHandler = (context, checkpoint, createStepId, parentId, checkA
1402
1422
  Payload: serializedPayload,
1403
1423
  ChainedInvokeOptions: {
1404
1424
  FunctionName: funcId,
1425
+ ...(config?.tenantId && { TenantId: config.tenantId }),
1405
1426
  },
1406
1427
  });
1407
1428
  }
@@ -2532,6 +2553,10 @@ class BatchResultImpl {
2532
2553
  * Restores methods to deserialized BatchResult data
2533
2554
  */
2534
2555
  function restoreBatchResult(data) {
2556
+ // If data is already a BatchResultImpl instance, return it as-is
2557
+ if (data instanceof BatchResultImpl) {
2558
+ return data;
2559
+ }
2535
2560
  if (data &&
2536
2561
  typeof data === "object" &&
2537
2562
  "all" in data &&
@@ -2999,7 +3024,7 @@ const getStepData = (stepData, stepId) => {
2999
3024
  };
3000
3025
 
3001
3026
  class DurableContextImpl {
3002
- executionContext;
3027
+ _executionContext;
3003
3028
  lambdaContext;
3004
3029
  _stepPrefix;
3005
3030
  _stepCounter = 0;
@@ -3011,8 +3036,9 @@ class DurableContextImpl {
3011
3036
  modeManagement;
3012
3037
  durableExecution;
3013
3038
  logger;
3014
- constructor(executionContext, lambdaContext, durableExecutionMode, inheritedLogger, stepPrefix, durableExecution, parentId) {
3015
- this.executionContext = executionContext;
3039
+ executionContext;
3040
+ constructor(_executionContext, lambdaContext, durableExecutionMode, inheritedLogger, stepPrefix, durableExecution, parentId) {
3041
+ this._executionContext = _executionContext;
3016
3042
  this.lambdaContext = lambdaContext;
3017
3043
  this._stepPrefix = stepPrefix;
3018
3044
  this._parentId = parentId;
@@ -3020,6 +3046,9 @@ class DurableContextImpl {
3020
3046
  this.durableLogger = inheritedLogger;
3021
3047
  this.durableLogger.configureDurableLoggingContext?.(this.getDurableLoggingContext());
3022
3048
  this.logger = this.createModeAwareLogger(inheritedLogger);
3049
+ this.executionContext = {
3050
+ durableExecutionArn: _executionContext.durableExecutionArn,
3051
+ };
3023
3052
  this.durableExecutionMode = durableExecutionMode;
3024
3053
  this.checkpoint = durableExecution.checkpointManager;
3025
3054
  this.modeManagement = new ModeManagement(this.captureExecutionState.bind(this), this.checkAndUpdateReplayMode.bind(this), this.checkForNonResolvingPromise.bind(this), () => this.durableExecutionMode, (mode) => {
@@ -3031,9 +3060,9 @@ class DurableContextImpl {
3031
3060
  getDurableLogData: () => {
3032
3061
  const activeContext = getActiveContext();
3033
3062
  const result = {
3034
- executionArn: this.executionContext.durableExecutionArn,
3035
- requestId: this.executionContext.requestId,
3036
- tenantId: this.executionContext.tenantId,
3063
+ executionArn: this._executionContext.durableExecutionArn,
3064
+ requestId: this._executionContext.requestId,
3065
+ tenantId: this._executionContext.tenantId,
3037
3066
  operationId: !activeContext || activeContext?.contextId === "root"
3038
3067
  ? undefined
3039
3068
  : hashId(activeContext.contextId),
@@ -3110,7 +3139,7 @@ class DurableContextImpl {
3110
3139
  checkAndUpdateReplayMode() {
3111
3140
  if (this.durableExecutionMode === DurableExecutionMode.ReplayMode) {
3112
3141
  const nextStepId = this.getNextStepId();
3113
- const nextStepData = this.executionContext.getStepData(nextStepId);
3142
+ const nextStepData = this._executionContext.getStepData(nextStepId);
3114
3143
  if (!nextStepData) {
3115
3144
  this.durableExecutionMode = DurableExecutionMode.ExecutionMode;
3116
3145
  }
@@ -3119,7 +3148,7 @@ class DurableContextImpl {
3119
3148
  captureExecutionState() {
3120
3149
  const wasInReplayMode = this.durableExecutionMode === DurableExecutionMode.ReplayMode;
3121
3150
  const nextStepId = this.getNextStepId();
3122
- const stepData = this.executionContext.getStepData(nextStepId);
3151
+ const stepData = this._executionContext.getStepData(nextStepId);
3123
3152
  const wasNotFinished = !!(stepData &&
3124
3153
  stepData.Status !== clientLambda.OperationStatus.SUCCEEDED &&
3125
3154
  stepData.Status !== clientLambda.OperationStatus.FAILED);
@@ -3128,7 +3157,7 @@ class DurableContextImpl {
3128
3157
  checkForNonResolvingPromise() {
3129
3158
  if (this.durableExecutionMode === DurableExecutionMode.ReplaySucceededContext) {
3130
3159
  const nextStepId = this.getNextStepId();
3131
- const nextStepData = this.executionContext.getStepData(nextStepId);
3160
+ const nextStepData = this._executionContext.getStepData(nextStepId);
3132
3161
  if (nextStepData &&
3133
3162
  nextStepData.Status !== clientLambda.OperationStatus.SUCCEEDED &&
3134
3163
  nextStepData.Status !== clientLambda.OperationStatus.FAILED) {
@@ -3144,16 +3173,16 @@ class DurableContextImpl {
3144
3173
  return this.modeManagement.withDurableModeManagement(operation);
3145
3174
  }
3146
3175
  step(nameOrFn, fnOrOptions, maybeOptions) {
3147
- validateContextUsage(this._stepPrefix, "step", this.executionContext.terminationManager);
3176
+ validateContextUsage(this._stepPrefix, "step", this._executionContext.terminationManager);
3148
3177
  return this.withDurableModeManagement(() => {
3149
- const stepHandler = createStepHandler(this.executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), this.durableLogger, this._parentId);
3178
+ const stepHandler = createStepHandler(this._executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), this.durableLogger, this._parentId);
3150
3179
  return stepHandler(nameOrFn, fnOrOptions, maybeOptions);
3151
3180
  });
3152
3181
  }
3153
3182
  invoke(nameOrFuncId, funcIdOrInput, inputOrConfig, maybeConfig) {
3154
- validateContextUsage(this._stepPrefix, "invoke", this.executionContext.terminationManager);
3183
+ validateContextUsage(this._stepPrefix, "invoke", this._executionContext.terminationManager);
3155
3184
  return this.withDurableModeManagement(() => {
3156
- const invokeHandler = createInvokeHandler(this.executionContext, this.checkpoint, this.createStepId.bind(this), this._parentId, this.checkAndUpdateReplayMode.bind(this));
3185
+ const invokeHandler = createInvokeHandler(this._executionContext, this.checkpoint, this.createStepId.bind(this), this._parentId, this.checkAndUpdateReplayMode.bind(this));
3157
3186
  return invokeHandler(...[
3158
3187
  nameOrFuncId,
3159
3188
  funcIdOrInput,
@@ -3163,18 +3192,18 @@ class DurableContextImpl {
3163
3192
  });
3164
3193
  }
3165
3194
  runInChildContext(nameOrFn, fnOrOptions, maybeOptions) {
3166
- validateContextUsage(this._stepPrefix, "runInChildContext", this.executionContext.terminationManager);
3195
+ validateContextUsage(this._stepPrefix, "runInChildContext", this._executionContext.terminationManager);
3167
3196
  return this.withDurableModeManagement(() => {
3168
- const blockHandler = createRunInChildContextHandler(this.executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), () => this.durableLogger,
3197
+ const blockHandler = createRunInChildContextHandler(this._executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), () => this.durableLogger,
3169
3198
  // Adapter function to maintain compatibility
3170
3199
  (executionContext, parentContext, durableExecutionMode, inheritedLogger, stepPrefix, _checkpointToken, parentId) => createDurableContext(executionContext, parentContext, durableExecutionMode, inheritedLogger, stepPrefix, this.durableExecution, parentId), this._parentId);
3171
3200
  return blockHandler(nameOrFn, fnOrOptions, maybeOptions);
3172
3201
  });
3173
3202
  }
3174
3203
  wait(nameOrDuration, maybeDuration) {
3175
- validateContextUsage(this._stepPrefix, "wait", this.executionContext.terminationManager);
3204
+ validateContextUsage(this._stepPrefix, "wait", this._executionContext.terminationManager);
3176
3205
  return this.withDurableModeManagement(() => {
3177
- const waitHandler = createWaitHandler(this.executionContext, this.checkpoint, this.createStepId.bind(this), this._parentId, this.checkAndUpdateReplayMode.bind(this));
3206
+ const waitHandler = createWaitHandler(this._executionContext, this.checkpoint, this.createStepId.bind(this), this._parentId, this.checkAndUpdateReplayMode.bind(this));
3178
3207
  return typeof nameOrDuration === "string"
3179
3208
  ? waitHandler(nameOrDuration, maybeDuration)
3180
3209
  : waitHandler(nameOrDuration);
@@ -3206,23 +3235,23 @@ class DurableContextImpl {
3206
3235
  }
3207
3236
  }
3208
3237
  createCallback(nameOrConfig, maybeConfig) {
3209
- validateContextUsage(this._stepPrefix, "createCallback", this.executionContext.terminationManager);
3238
+ validateContextUsage(this._stepPrefix, "createCallback", this._executionContext.terminationManager);
3210
3239
  return this.withDurableModeManagement(() => {
3211
- const callbackFactory = createCallback(this.executionContext, this.checkpoint, this.createStepId.bind(this), this.checkAndUpdateReplayMode.bind(this), this._parentId);
3240
+ const callbackFactory = createCallback(this._executionContext, this.checkpoint, this.createStepId.bind(this), this.checkAndUpdateReplayMode.bind(this), this._parentId);
3212
3241
  return callbackFactory(nameOrConfig, maybeConfig);
3213
3242
  });
3214
3243
  }
3215
3244
  waitForCallback(nameOrSubmitter, submitterOrConfig, maybeConfig) {
3216
- validateContextUsage(this._stepPrefix, "waitForCallback", this.executionContext.terminationManager);
3245
+ validateContextUsage(this._stepPrefix, "waitForCallback", this._executionContext.terminationManager);
3217
3246
  return this.withDurableModeManagement(() => {
3218
- const waitForCallbackHandler = createWaitForCallbackHandler(this.executionContext, this.getNextStepId.bind(this), this.runInChildContext.bind(this));
3247
+ const waitForCallbackHandler = createWaitForCallbackHandler(this._executionContext, this.getNextStepId.bind(this), this.runInChildContext.bind(this));
3219
3248
  return waitForCallbackHandler(nameOrSubmitter, submitterOrConfig, maybeConfig);
3220
3249
  });
3221
3250
  }
3222
3251
  waitForCondition(nameOrCheckFunc, checkFuncOrConfig, maybeConfig) {
3223
- validateContextUsage(this._stepPrefix, "waitForCondition", this.executionContext.terminationManager);
3252
+ validateContextUsage(this._stepPrefix, "waitForCondition", this._executionContext.terminationManager);
3224
3253
  return this.withDurableModeManagement(() => {
3225
- const waitForConditionHandler = createWaitForConditionHandler(this.executionContext, this.checkpoint, this.createStepId.bind(this), this.durableLogger, this._parentId);
3254
+ const waitForConditionHandler = createWaitForConditionHandler(this._executionContext, this.checkpoint, this.createStepId.bind(this), this.durableLogger, this._parentId);
3226
3255
  return typeof nameOrCheckFunc === "string" ||
3227
3256
  nameOrCheckFunc === undefined
3228
3257
  ? waitForConditionHandler(nameOrCheckFunc, checkFuncOrConfig, maybeConfig)
@@ -3230,23 +3259,23 @@ class DurableContextImpl {
3230
3259
  });
3231
3260
  }
3232
3261
  map(nameOrItems, itemsOrMapFunc, mapFuncOrConfig, maybeConfig) {
3233
- validateContextUsage(this._stepPrefix, "map", this.executionContext.terminationManager);
3262
+ validateContextUsage(this._stepPrefix, "map", this._executionContext.terminationManager);
3234
3263
  return this.withDurableModeManagement(() => {
3235
- const mapHandler = createMapHandler(this.executionContext, this._executeConcurrently.bind(this));
3264
+ const mapHandler = createMapHandler(this._executionContext, this._executeConcurrently.bind(this));
3236
3265
  return mapHandler(nameOrItems, itemsOrMapFunc, mapFuncOrConfig, maybeConfig);
3237
3266
  });
3238
3267
  }
3239
3268
  parallel(nameOrBranches, branchesOrConfig, maybeConfig) {
3240
- validateContextUsage(this._stepPrefix, "parallel", this.executionContext.terminationManager);
3269
+ validateContextUsage(this._stepPrefix, "parallel", this._executionContext.terminationManager);
3241
3270
  return this.withDurableModeManagement(() => {
3242
- const parallelHandler = createParallelHandler(this.executionContext, this._executeConcurrently.bind(this));
3271
+ const parallelHandler = createParallelHandler(this._executionContext, this._executeConcurrently.bind(this));
3243
3272
  return parallelHandler(nameOrBranches, branchesOrConfig, maybeConfig);
3244
3273
  });
3245
3274
  }
3246
3275
  _executeConcurrently(nameOrItems, itemsOrExecutor, executorOrConfig, maybeConfig) {
3247
- validateContextUsage(this._stepPrefix, "_executeConcurrently", this.executionContext.terminationManager);
3276
+ validateContextUsage(this._stepPrefix, "_executeConcurrently", this._executionContext.terminationManager);
3248
3277
  return this.withDurableModeManagement(() => {
3249
- const concurrentExecutionHandler = createConcurrentExecutionHandler(this.executionContext, this.runInChildContext.bind(this), this.skipNextOperation.bind(this));
3278
+ const concurrentExecutionHandler = createConcurrentExecutionHandler(this._executionContext, this.runInChildContext.bind(this), this.skipNextOperation.bind(this));
3250
3279
  const promise = concurrentExecutionHandler(nameOrItems, itemsOrExecutor, executorOrConfig, maybeConfig);
3251
3280
  // Prevent unhandled promise rejections
3252
3281
  promise?.catch(() => { });
@@ -3314,7 +3343,6 @@ class CheckpointManager {
3314
3343
  // Termination cooldown
3315
3344
  terminationTimer = null;
3316
3345
  terminationReason = null;
3317
- TERMINATION_COOLDOWN_MS = 50;
3318
3346
  constructor(durableExecutionArn, stepData, storage, terminationManager, initialTaskToken, stepDataEmitter, logger, finishedAncestors) {
3319
3347
  this.durableExecutionArn = durableExecutionArn;
3320
3348
  this.stepData = stepData;
@@ -3801,7 +3829,7 @@ class CheckpointManager {
3801
3829
  this.terminationReason = reason;
3802
3830
  log("⏱️", "Scheduling termination", {
3803
3831
  reason,
3804
- cooldownMs: this.TERMINATION_COOLDOWN_MS,
3832
+ cooldownMs: CHECKPOINT_TERMINATION_COOLDOWN_MS,
3805
3833
  });
3806
3834
  this.terminationTimer = setTimeout(() => {
3807
3835
  if (!this.shouldTerminate()) {
@@ -3810,7 +3838,7 @@ class CheckpointManager {
3810
3838
  return;
3811
3839
  }
3812
3840
  this.executeTermination(reason);
3813
- }, this.TERMINATION_COOLDOWN_MS);
3841
+ }, CHECKPOINT_TERMINATION_COOLDOWN_MS);
3814
3842
  }
3815
3843
  executeTermination(reason) {
3816
3844
  log("🛑", "Executing termination after cooldown", { reason });
@@ -3845,6 +3873,10 @@ class CheckpointManager {
3845
3873
  const timestamp = endTimestamp instanceof Date ? endTimestamp : new Date(endTimestamp);
3846
3874
  // Wait until endTimestamp
3847
3875
  delay = Math.max(0, timestamp.getTime() - Date.now());
3876
+ // Skip setTimeout if delay exceeds MAX_POLL_DURATION_MS (Lambda will timeout before it fires)
3877
+ if (delay > MAX_POLL_DURATION_MS) {
3878
+ return;
3879
+ }
3848
3880
  }
3849
3881
  else {
3850
3882
  // No timestamp, start polling immediately (1 second delay)
@@ -3864,7 +3896,6 @@ class CheckpointManager {
3864
3896
  if (!op)
3865
3897
  return;
3866
3898
  // Check if we've exceeded max polling duration (15 minutes)
3867
- const MAX_POLL_DURATION_MS = 15 * 60 * 1000; // 15 minutes
3868
3899
  if (op.pollStartTime &&
3869
3900
  Date.now() - op.pollStartTime > MAX_POLL_DURATION_MS) {
3870
3901
  // Stop polling after 15 minutes to prevent indefinite resource consumption.
@@ -3976,6 +4007,13 @@ class TerminationManager extends events.EventEmitter {
3976
4007
  // align the default behaviour of how logs are emitted to match the RIC logging behaviour for consistency.
3977
4008
  // For custom logic, users can implement their own logger to log data differently.
3978
4009
  // See: https://github.com/aws/aws-lambda-nodejs-runtime-interface-client/blob/962ed28eefbc052389c4de4366b1c0c49ee08a13/src/LogPatch.js
4010
+ /**
4011
+ * Format options for util.formatWithOptions.
4012
+ * Using breakLength: Infinity prevents util.inspect from inserting newlines
4013
+ * when formatting objects, regardless of object size (fixes issue #322).
4014
+ * Defined at module level to avoid creating a new object on every function call.
4015
+ */
4016
+ const FORMAT_OPTIONS = { breakLength: Infinity };
3979
4017
  /**
3980
4018
  * JSON.stringify replacer function for Error objects.
3981
4019
  * Based on AWS Lambda Runtime Interface Client LogPatch functionality.
@@ -4041,11 +4079,11 @@ function formatDurableLogData(level, logData, ...messageParams) {
4041
4079
  return JSON.stringify(result, jsonErrorReplacer);
4042
4080
  }
4043
4081
  catch (_) {
4044
- result.message = util.format(result.message);
4082
+ result.message = util.formatWithOptions(FORMAT_OPTIONS, result.message);
4045
4083
  return JSON.stringify(result);
4046
4084
  }
4047
4085
  }
4048
- result.message = util.format(...messageParams);
4086
+ result.message = util.formatWithOptions(FORMAT_OPTIONS, ...messageParams);
4049
4087
  for (const param of messageParams) {
4050
4088
  if (param instanceof Error) {
4051
4089
  result.errorType = param?.constructor?.name ?? "UnknownError";
@@ -4200,6 +4238,20 @@ const createDefaultLogger = (executionContext) => {
4200
4238
  return new DefaultLogger(executionContext);
4201
4239
  };
4202
4240
 
4241
+ /**
4242
+ * SDK metadata injected by Rollup at build time from package.json.
4243
+ *
4244
+ * At build time, Rollup replaces "@aws/durable-execution-sdk-js" and
4245
+ * "1.0.3" with actual values from package.json.
4246
+ *
4247
+ * Defaults are provided for test environments where Rollup doesn't run
4248
+ * and process.env values are undefined.
4249
+ *
4250
+ * @internal
4251
+ */
4252
+ const SDK_NAME = "@aws/durable-execution-sdk-js";
4253
+ const SDK_VERSION = "1.0.3";
4254
+
4203
4255
  let defaultLambdaClient;
4204
4256
  /**
4205
4257
  * Durable execution client which uses an API-based LambdaClient
@@ -4214,6 +4266,7 @@ class DurableExecutionApiClient {
4214
4266
  if (!client) {
4215
4267
  if (!defaultLambdaClient) {
4216
4268
  defaultLambdaClient = new clientLambda.LambdaClient({
4269
+ customUserAgent: [[SDK_NAME, SDK_VERSION]],
4217
4270
  requestHandler: {
4218
4271
  connectionTimeout: 5000,
4219
4272
  socketTimeout: 50000,
@@ -4412,8 +4465,9 @@ async function runHandler(event, context, executionContext, durableExecutionMode
4412
4465
  const durableContext = createDurableContext(executionContext, context, durableExecutionMode,
4413
4466
  // Default logger may not have the same type as Logger, but we should always provide a default logger even if the user overrides it
4414
4467
  createDefaultLogger(), undefined, durableExecution);
4415
- // Extract customerHandlerEvent from the original event
4416
- const initialExecutionEvent = event.InitialExecutionState.Operations?.[0];
4468
+ // Extract customerHandlerEvent from the complete operations array (after pagination)
4469
+ // This ensures we get the full payload even for large payloads that are paginated
4470
+ const initialExecutionEvent = executionContext._stepData[Object.keys(executionContext._stepData)[0]];
4417
4471
  const customerHandlerEvent = JSON.parse(initialExecutionEvent?.ExecutionDetails?.InputPayload ?? "{}");
4418
4472
  try {
4419
4473
  log("🎯", `Starting handler execution, handler event: ${customerHandlerEvent}`);