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