cloudformation-stack-drift-detector 0.1.4 → 0.1.5

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/.jsii CHANGED
@@ -8440,7 +8440,7 @@
8440
8440
  "stability": "stable"
8441
8441
  },
8442
8442
  "homepage": "https://github.com/gammarers-aws-cdk-constructs/cloudformation-stack-drift-detector.git",
8443
- "jsiiVersion": "6.0.12 (build 471f453)",
8443
+ "jsiiVersion": "6.0.13 (build 5c05d06)",
8444
8444
  "keywords": [
8445
8445
  "aws",
8446
8446
  "cdk",
@@ -8696,6 +8696,6 @@
8696
8696
  "symbolId": "src/index:TargetResource"
8697
8697
  }
8698
8698
  },
8699
- "version": "0.1.4",
8700
- "fingerprint": "Jqn/+VRW42kopD0Ht9dEiVpD114gOnmD3R6NdElONbc="
8699
+ "version": "0.1.5",
8700
+ "fingerprint": "QycxLNj3Gv0Vx1wHojgjVP6oHDL/ks+Ap0DXg8UgKfw="
8701
8701
  }
@@ -195,6 +195,7 @@ var DurablePromise = class {
195
195
  * Attaches callbacks for the resolution and/or rejection of the Promise
196
196
  * Triggers execution if not already started
197
197
  */
198
+ // biome-ignore lint/suspicious/noThenProperty: DurablePromise is deliberately a thenable. The "then" member is part of its public contract, so callers can await a durable operation directly.
198
199
  then(onfulfilled, onrejected) {
199
200
  return this.ensureExecution().then(onfulfilled, onrejected);
200
201
  }
@@ -263,7 +264,8 @@ function durationToSeconds(duration) {
263
264
  function terminateForUnrecoverableError(context, error, stepIdentifier) {
264
265
  context.terminationManager.terminate({
265
266
  reason: error.terminationReason,
266
- message: `Unrecoverable error in step ${stepIdentifier}: ${error.message}`
267
+ message: `Unrecoverable error in step ${stepIdentifier}: ${error.message}`,
268
+ error
267
269
  });
268
270
  return new Promise(() => {
269
271
  });
@@ -359,7 +361,7 @@ var createRetryStrategy = (config = {}) => {
359
361
  }
360
362
  const initialDelaySeconds = durationToSeconds(finalConfig.initialDelay);
361
363
  const maxDelaySeconds = durationToSeconds(finalConfig.maxDelay);
362
- const baseDelay = Math.min(initialDelaySeconds * Math.pow(finalConfig.backoffRate, attemptsMade - 1), maxDelaySeconds);
364
+ const baseDelay = Math.min(initialDelaySeconds * finalConfig.backoffRate ** (attemptsMade - 1), maxDelaySeconds);
363
365
  const finalDelay = finalizeDelaySeconds(baseDelay, finalConfig.jitter);
364
366
  return { shouldRetry: true, delay: { seconds: finalDelay } };
365
367
  };
@@ -591,8 +593,27 @@ var TerminationReason;
591
593
  TerminationReason2["SERDES_FAILED"] = "SERDES_FAILED";
592
594
  TerminationReason2["CONTEXT_VALIDATION_ERROR"] = "CONTEXT_VALIDATION_ERROR";
593
595
  TerminationReason2["CONFIG_VALIDATION_ERROR"] = "CONFIG_VALIDATION_ERROR";
596
+ TerminationReason2["NON_DETERMINISM"] = "NON_DETERMINISM";
594
597
  TerminationReason2["CUSTOM"] = "CUSTOM";
595
598
  })(TerminationReason || (TerminationReason = {}));
599
+ var TERMINATION_CLASS = {
600
+ [TerminationReason.OPERATION_TERMINATED]: "suspend",
601
+ [TerminationReason.RETRY_SCHEDULED]: "suspend",
602
+ [TerminationReason.RETRY_INTERRUPTED_STEP]: "suspend",
603
+ [TerminationReason.WAIT_SCHEDULED]: "suspend",
604
+ [TerminationReason.CALLBACK_PENDING]: "suspend",
605
+ [TerminationReason.CHECKPOINT_FAILED]: "fault",
606
+ [TerminationReason.SERDES_FAILED]: "fault",
607
+ [TerminationReason.CONTEXT_VALIDATION_ERROR]: "fault",
608
+ [TerminationReason.CONFIG_VALIDATION_ERROR]: "fault",
609
+ [TerminationReason.NON_DETERMINISM]: "fault",
610
+ // Deliberately unclassified in meaning: CUSTOM says nothing about whether the execution
611
+ // can continue, so it is treated as a fault. Reporting a fault that was really a suspend
612
+ // costs one failed execution; the reverse asks the service to retry something that can
613
+ // never progress, and hides the error while doing so.
614
+ [TerminationReason.CUSTOM]: "fault"
615
+ };
616
+ var classifyTermination = (reason) => TERMINATION_CLASS[reason] ?? "fault";
596
617
  var UnrecoverableError = class extends Error {
597
618
  originalError;
598
619
  isUnrecoverable = true;
@@ -744,7 +765,7 @@ function createErrorObjectFromError(error, data) {
744
765
  };
745
766
  }
746
767
  var NonDeterministicExecutionError = class extends UnrecoverableExecutionError {
747
- terminationReason = TerminationReason.CUSTOM;
768
+ terminationReason = TerminationReason.NON_DETERMINISM;
748
769
  constructor(message) {
749
770
  super(message);
750
771
  this.name = "NonDeterministicExecutionError";
@@ -839,7 +860,7 @@ var getStepData = (stepData, stepId) => {
839
860
  const hashedId = hashId(stepId);
840
861
  return stepData[hashedId];
841
862
  };
842
- var createStepHandler = (context, checkpoint, parentContext, createStepId, logger, parentId, getDefaultSerdes, plugin = {}) => {
863
+ var createStepHandler = (context, checkpoint, parentContext, createStepId, logger, parentId, getDefaultSerdes, plugin = {}, pluginOperationName) => {
843
864
  return (nameOrFn, fnOrOptions, maybeOptions) => {
844
865
  let name;
845
866
  let fn;
@@ -860,7 +881,7 @@ var createStepHandler = (context, checkpoint, parentContext, createStepId, logge
860
881
  validateReplayConsistency(stepId, { type: OperationType.STEP, name, subType: OperationSubType.STEP }, stepData, context);
861
882
  const opInfo = {
862
883
  id: hashId(stepId),
863
- name,
884
+ name: pluginOperationName ?? name,
864
885
  type: OperationType.STEP,
865
886
  subType: OperationSubType.STEP,
866
887
  parentId: parentId ? hashId(parentId) : void 0
@@ -1719,15 +1740,7 @@ var createWaitForConditionHandler = (context, checkpoint, createStepId, logger,
1719
1740
  if (stepData?.Status === OperationStatus.STARTED || stepData?.Status === OperationStatus.READY) {
1720
1741
  const checkpointData = stepData.StepDetails?.Result;
1721
1742
  if (checkpointData) {
1722
- try {
1723
- const serdesContext = {
1724
- entityId: stepId,
1725
- durableExecutionArn: context.durableExecutionArn
1726
- };
1727
- currentState = await serdes.deserialize(checkpointData, serdesContext);
1728
- } catch {
1729
- currentState = config.initialState;
1730
- }
1743
+ currentState = await safeDeserialize(serdes, checkpointData, stepId, name, context.terminationManager, context.durableExecutionArn);
1731
1744
  } else {
1732
1745
  currentState = config.initialState;
1733
1746
  }
@@ -1820,7 +1833,7 @@ var createWaitForConditionHandler = (context, checkpoint, createStepId, logger,
1820
1833
  }
1821
1834
  });
1822
1835
  stepData = context.getStepData(stepId);
1823
- const attemptEndInfo = toAttemptEndInfo(stepData, AttemptEndInfoOutcome.FAILED, {
1836
+ const attemptEndInfo = toAttemptEndInfo(stepData, AttemptEndInfoOutcome.SUCCEEDED, {
1824
1837
  attempt: currentAttempt
1825
1838
  });
1826
1839
  backfillOperationInfo(attemptEndInfo, opInfo);
@@ -1926,7 +1939,7 @@ var createPassThroughSerdes = () => ({
1926
1939
  serialize: async (value) => value,
1927
1940
  deserialize: async (data) => data
1928
1941
  });
1929
- var createCallback = (context, checkpoint, createStepId, checkAndUpdateReplayMode, parentId, getDefaultCallbackDeserializer, plugin = {}) => {
1942
+ var createCallback = (context, checkpoint, createStepId, checkAndUpdateReplayMode, parentId, getDefaultCallbackDeserializer, plugin = {}, pluginOperationName) => {
1930
1943
  return (nameOrConfig, maybeConfig) => {
1931
1944
  let name;
1932
1945
  let config;
@@ -1940,7 +1953,7 @@ var createCallback = (context, checkpoint, createStepId, checkAndUpdateReplayMod
1940
1953
  const serdes = config?.serdes || (getDefaultCallbackDeserializer ? getDefaultCallbackDeserializer() : createPassThroughSerdes());
1941
1954
  const opInfo = {
1942
1955
  id: hashId(stepId),
1943
- name,
1956
+ name: pluginOperationName ?? name,
1944
1957
  type: OperationType.CALLBACK,
1945
1958
  subType: OperationSubType.CALLBACK,
1946
1959
  parentId: parentId ? hashId(parentId) : void 0
@@ -2108,12 +2121,14 @@ var createWaitForCallbackHandler = (context, peekStepId, runInChildContext, getD
2108
2121
  serdes: createPassThroughSerdes()
2109
2122
  }
2110
2123
  } : void 0;
2111
- const [callbackPromise, callbackId] = await childCtx.createCallback(createCallbackConfig);
2124
+ const internalCtx = childCtx;
2125
+ const [callbackPromise, callbackId] = await internalCtx._createCallbackWithPluginOperationName(name ? `${name}-callback` : void 0, createCallbackConfig);
2112
2126
  log("\u{1F194}", "Callback created:", {
2113
2127
  callbackId,
2114
2128
  name
2115
2129
  });
2116
- await childCtx.step(async (stepContext) => {
2130
+ const submitterOptions = config?.retryStrategy ? { retryStrategy: config.retryStrategy } : void 0;
2131
+ await internalCtx._stepWithPluginOperationName(name ? `${name}-submitter` : void 0, async (stepContext) => {
2117
2132
  const callbackContext = {
2118
2133
  logger: stepContext.logger
2119
2134
  };
@@ -2126,7 +2141,7 @@ var createWaitForCallbackHandler = (context, peekStepId, runInChildContext, getD
2126
2141
  callbackId,
2127
2142
  name
2128
2143
  });
2129
- }, config?.retryStrategy ? { retryStrategy: config.retryStrategy } : void 0);
2144
+ }, submitterOptions);
2130
2145
  log("\u23F3", "Waiting for callback completion:", {
2131
2146
  callbackId,
2132
2147
  name
@@ -2165,6 +2180,36 @@ var createWaitForCallbackHandler = (context, peekStepId, runInChildContext, getD
2165
2180
  });
2166
2181
  };
2167
2182
  };
2183
+ var ITEM_STATUS_SUCCEEDED = "S";
2184
+ var ITEM_STATUS_FAILED = "F";
2185
+ var ITEM_STATUS_INCOMPLETE = "-";
2186
+ var encodeItemStatuses = (result) => {
2187
+ const items = result.all ?? [];
2188
+ if (items.length === 0) {
2189
+ return "";
2190
+ }
2191
+ let maxIndex = -1;
2192
+ for (const item of items) {
2193
+ if (typeof item.index === "number" && item.index > maxIndex) {
2194
+ maxIndex = item.index;
2195
+ }
2196
+ }
2197
+ if (maxIndex < 0) {
2198
+ return "";
2199
+ }
2200
+ const encoded = new Array(maxIndex + 1).fill(ITEM_STATUS_INCOMPLETE);
2201
+ for (const item of items) {
2202
+ if (typeof item.index !== "number") {
2203
+ continue;
2204
+ }
2205
+ if (item.status === BatchItemStatus.SUCCEEDED) {
2206
+ encoded[item.index] = ITEM_STATUS_SUCCEEDED;
2207
+ } else if (item.status === BatchItemStatus.FAILED) {
2208
+ encoded[item.index] = ITEM_STATUS_FAILED;
2209
+ }
2210
+ }
2211
+ return encoded.join("");
2212
+ };
2168
2213
  var createParallelSummaryGenerator = () => (result) => {
2169
2214
  return JSON.stringify({
2170
2215
  type: "ParallelResult",
@@ -2173,7 +2218,8 @@ var createParallelSummaryGenerator = () => (result) => {
2173
2218
  failureCount: result.failureCount,
2174
2219
  startedCount: result.startedCount,
2175
2220
  completionReason: result.completionReason,
2176
- status: result.status
2221
+ status: result.status,
2222
+ itemStatuses: encodeItemStatuses(result)
2177
2223
  });
2178
2224
  };
2179
2225
  var createMapSummaryGenerator = () => (result) => {
@@ -2183,7 +2229,8 @@ var createMapSummaryGenerator = () => (result) => {
2183
2229
  successCount: result.successCount,
2184
2230
  failureCount: result.failureCount,
2185
2231
  completionReason: result.completionReason,
2186
- status: result.status
2232
+ status: result.status,
2233
+ itemStatuses: encodeItemStatuses(result)
2187
2234
  });
2188
2235
  };
2189
2236
  var composeSummaryGenerator = (internalGenerator, customGenerator) => (result) => {
@@ -2671,18 +2718,21 @@ var ConcurrencyController = class {
2671
2718
  itemCount: items.length
2672
2719
  });
2673
2720
  let recordedCompletionReason;
2721
+ let recordedItemStatuses;
2674
2722
  if (entityId && executionContext) {
2675
2723
  const stepData = executionContext.getStepData(entityId);
2676
2724
  const summaryPayload = stepData?.ContextDetails?.Result;
2677
2725
  if (summaryPayload) {
2678
2726
  recordedCompletionReason = this.parseRecordedCompletionReason(summaryPayload);
2727
+ recordedItemStatuses = this.parseRecordedItemStatuses(summaryPayload);
2679
2728
  log("\u{1F4CA}", "Recovered completion reason from summary:", {
2680
- recordedCompletionReason
2729
+ recordedCompletionReason,
2730
+ hasRecordedItemStatuses: recordedItemStatuses !== void 0
2681
2731
  });
2682
2732
  }
2683
2733
  }
2684
2734
  if (entityId && executionContext) {
2685
- return await this.replayItems(items, executor, parentContext, config, recordedCompletionReason, executionContext, entityId);
2735
+ return await this.replayItems(items, executor, parentContext, config, recordedCompletionReason, recordedItemStatuses, executionContext, entityId);
2686
2736
  } else {
2687
2737
  log("\u26A0\uFE0F", "No entity id or execution context found, falling back to concurrent execution");
2688
2738
  }
@@ -2708,7 +2758,7 @@ var ConcurrencyController = class {
2708
2758
  const parsed = JSON.parse(summaryPayload);
2709
2759
  if (parsed && typeof parsed === "object") {
2710
2760
  const reason = parsed.completionReason;
2711
- if (typeof reason === "string" && Object.prototype.hasOwnProperty.call(VALID_COMPLETION_REASONS, reason)) {
2761
+ if (typeof reason === "string" && Object.hasOwn(VALID_COMPLETION_REASONS, reason)) {
2712
2762
  return reason;
2713
2763
  }
2714
2764
  }
@@ -2717,7 +2767,128 @@ var ConcurrencyController = class {
2717
2767
  }
2718
2768
  return void 0;
2719
2769
  }
2720
- async replayItems(items, executor, parentContext, config, recordedCompletionReason, executionContext, parentEntityId) {
2770
+ /**
2771
+ * Extracts the recorded per-item completion markers from a checkpointed batch
2772
+ * payload. One character per item in index order (`S`/`F`/`-`).
2773
+ *
2774
+ * Two payload shapes are recognised, because ReplayChildren is reached two
2775
+ * ways:
2776
+ *
2777
+ * - The summary written when the result exceeded the checkpoint size limit,
2778
+ * which carries `itemStatuses` directly.
2779
+ * - The FULL serialized BatchResult written when `childOperationsDepth` opts
2780
+ * a context into child preservation. That path sets ReplayChildren while
2781
+ * checkpointing the whole result, so the summary generator never runs and
2782
+ * there is no `itemStatuses` — but the payload already contains
2783
+ * `all: [{ index, status }]`, which carries the same information.
2784
+ *
2785
+ * Validated as strictly as `parseRecordedCompletionReason`: legacy checkpoints
2786
+ * can carry arbitrary JSON (including free-form strings from pre-fix custom
2787
+ * generators), and the consequence here is terminality decisions rather than a
2788
+ * display field. `Object.hasOwn` keeps inherited keys like `constructor` out,
2789
+ * and the marker alphabet is checked so a crafted value cannot be read as
2790
+ * terminality.
2791
+ *
2792
+ * Returns undefined when the payload is absent, unparseable, or carries
2793
+ * neither shape — in which case replay falls back to probing.
2794
+ */
2795
+ parseRecordedItemStatuses(summaryPayload) {
2796
+ if (typeof summaryPayload !== "string") {
2797
+ return void 0;
2798
+ }
2799
+ let parsed;
2800
+ try {
2801
+ parsed = JSON.parse(summaryPayload);
2802
+ } catch {
2803
+ return void 0;
2804
+ }
2805
+ if (!parsed || typeof parsed !== "object") {
2806
+ return void 0;
2807
+ }
2808
+ const record = parsed;
2809
+ if (Object.hasOwn(record, "itemStatuses")) {
2810
+ const statuses = record.itemStatuses;
2811
+ if (typeof statuses === "string" && statuses.length > 0 && /^[SF-]+$/.test(statuses)) {
2812
+ return statuses;
2813
+ }
2814
+ return void 0;
2815
+ }
2816
+ if (Object.hasOwn(record, "all") && Array.isArray(record.all)) {
2817
+ return this.encodeStatusesFromBatchItems(record.all);
2818
+ }
2819
+ return void 0;
2820
+ }
2821
+ /**
2822
+ * Derives the per-item markers from a checkpointed `all` array, so the
2823
+ * `childOperationsDepth` path (which checkpoints the full result and therefore
2824
+ * has no summary envelope) gets the same treatment as a summarized batch.
2825
+ */
2826
+ encodeStatusesFromBatchItems(items) {
2827
+ let maxIndex = -1;
2828
+ for (const entry of items) {
2829
+ if (!entry || typeof entry !== "object") {
2830
+ continue;
2831
+ }
2832
+ const index = entry.index;
2833
+ if (typeof index === "number" && index > maxIndex) {
2834
+ maxIndex = index;
2835
+ }
2836
+ }
2837
+ if (maxIndex < 0) {
2838
+ return void 0;
2839
+ }
2840
+ const encoded = new Array(maxIndex + 1).fill("-");
2841
+ for (const entry of items) {
2842
+ if (!entry || typeof entry !== "object") {
2843
+ continue;
2844
+ }
2845
+ const item = entry;
2846
+ if (typeof item.index !== "number") {
2847
+ continue;
2848
+ }
2849
+ if (item.status === BatchItemStatus.SUCCEEDED) {
2850
+ encoded[item.index] = "S";
2851
+ } else if (item.status === BatchItemStatus.FAILED) {
2852
+ encoded[item.index] = "F";
2853
+ }
2854
+ }
2855
+ return encoded.join("");
2856
+ }
2857
+ /**
2858
+ * Whether a virtual item finished, inferred from its own checkpointed
2859
+ * operations. Used ONLY for summaries written before per-item statuses were
2860
+ * recorded.
2861
+ *
2862
+ * A virtual (FLAT) item has no context checkpoint, so its operations are
2863
+ * probed directly. Probing only the first operation would be unsound: an item
2864
+ * that was still in flight can have a SUCCEEDED first operation and would be
2865
+ * misread as finished. Every recorded operation is therefore walked, and the
2866
+ * item counts as finished only if all of them are terminal.
2867
+ *
2868
+ * Step ids come from a per-context counter starting at 1 and increasing by one
2869
+ * per operation, so the recorded ids are contiguous and the walk stops at the
2870
+ * first gap.
2871
+ *
2872
+ * Residual limitation, unavoidable without recorded statuses: an item whose
2873
+ * body creates no durable operation leaves nothing to probe and is
2874
+ * indistinguishable from one that never started, so it reads as unfinished and
2875
+ * is not rebuilt.
2876
+ */
2877
+ isVirtualItemTerminalByProbe(executionContext, childEntityId) {
2878
+ let found = false;
2879
+ for (let opIndex = 1; ; opIndex++) {
2880
+ const op = executionContext.getStepData(`${childEntityId}-${opIndex}`);
2881
+ if (!op) {
2882
+ break;
2883
+ }
2884
+ found = true;
2885
+ if (op.Status !== OperationStatus.SUCCEEDED && op.Status !== OperationStatus.FAILED) {
2886
+ return false;
2887
+ }
2888
+ }
2889
+ return found;
2890
+ }
2891
+ async replayItems(items, executor, parentContext, config, recordedCompletionReason, recordedItemStatuses, executionContext, parentEntityId) {
2721
2892
  const resultItems = [];
2722
2893
  log("\u{1F504}", `Replaying ${items.length} items from child checkpoints`, {
2723
2894
  recordedCompletionReason
@@ -2726,7 +2897,16 @@ var ConcurrencyController = class {
2726
2897
  for (const item of items) {
2727
2898
  const childEntityId = `${parentEntityId}-${stepCounter + 1}`;
2728
2899
  const childStepData = executionContext.getStepData(childEntityId);
2729
- const isTerminal = !!childStepData && (childStepData.Status === OperationStatus.SUCCEEDED || childStepData.Status === OperationStatus.FAILED);
2900
+ const childRecordIsTerminal = !childStepData || childStepData.Status === OperationStatus.SUCCEEDED || childStepData.Status === OperationStatus.FAILED;
2901
+ let isTerminal;
2902
+ if (recordedItemStatuses !== void 0) {
2903
+ const marker = recordedItemStatuses[item.index];
2904
+ isTerminal = (marker === "S" || marker === "F") && childRecordIsTerminal;
2905
+ } else if (childStepData) {
2906
+ isTerminal = childStepData.Status === OperationStatus.SUCCEEDED || childStepData.Status === OperationStatus.FAILED;
2907
+ } else {
2908
+ isTerminal = this.isVirtualItemTerminalByProbe(executionContext, childEntityId);
2909
+ }
2730
2910
  if (isTerminal) {
2731
2911
  try {
2732
2912
  const result = await parentContext.runInChildContext(item.name || item.id, (childContext) => executor(item, childContext), {
@@ -3222,9 +3402,20 @@ var DurableContextImpl = class {
3222
3402
  return this.modeManagement.withDurableModeManagement(operation);
3223
3403
  }
3224
3404
  step(nameOrFn, fnOrOptions, maybeOptions) {
3405
+ return this.stepInternal(void 0, nameOrFn, fnOrOptions, maybeOptions);
3406
+ }
3407
+ /**
3408
+ * {@link step} variant that also labels the span with a plugin-only name.
3409
+ * Never checkpointed. See {@link InternalDurableContext}.
3410
+ * @internal
3411
+ */
3412
+ _stepWithPluginOperationName(pluginOperationName, nameOrFn, fnOrOptions, maybeOptions) {
3413
+ return this.stepInternal(pluginOperationName, nameOrFn, fnOrOptions, maybeOptions);
3414
+ }
3415
+ stepInternal(pluginOperationName, nameOrFn, fnOrOptions, maybeOptions) {
3225
3416
  validateContextUsage(this._stepPrefix, "step", this._executionContext.terminationManager);
3226
3417
  return this.withDurableModeManagement(() => {
3227
- const stepHandler = createStepHandler(this._executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), this.durableLogger, this._parentId, () => this._defaultSerdes, this.durableExecution.plugin);
3418
+ const stepHandler = createStepHandler(this._executionContext, this.checkpoint, this.lambdaContext, this.createStepId.bind(this), this.durableLogger, this._parentId, () => this._defaultSerdes, this.durableExecution.plugin, pluginOperationName);
3228
3419
  return stepHandler(nameOrFn, fnOrOptions, maybeOptions);
3229
3420
  });
3230
3421
  }
@@ -3313,9 +3504,20 @@ var DurableContextImpl = class {
3313
3504
  }
3314
3505
  }
3315
3506
  createCallback(nameOrConfig, maybeConfig) {
3507
+ return this.createCallbackInternal(void 0, nameOrConfig, maybeConfig);
3508
+ }
3509
+ /**
3510
+ * {@link createCallback} variant that also labels the span with a
3511
+ * plugin-only name. Never checkpointed. See {@link InternalDurableContext}.
3512
+ * @internal
3513
+ */
3514
+ _createCallbackWithPluginOperationName(pluginOperationName, nameOrConfig, maybeConfig) {
3515
+ return this.createCallbackInternal(pluginOperationName, nameOrConfig, maybeConfig);
3516
+ }
3517
+ createCallbackInternal(pluginOperationName, nameOrConfig, maybeConfig) {
3316
3518
  validateContextUsage(this._stepPrefix, "createCallback", this._executionContext.terminationManager);
3317
3519
  return this.withDurableModeManagement(() => {
3318
- const callbackFactory = createCallback(this._executionContext, this.checkpoint, this.createStepId.bind(this), this.checkAndUpdateReplayMode.bind(this), this._parentId, () => this._defaultCallbackDeserializer, this.durableExecution.plugin);
3520
+ const callbackFactory = createCallback(this._executionContext, this.checkpoint, this.createStepId.bind(this), this.checkAndUpdateReplayMode.bind(this), this._parentId, () => this._defaultCallbackDeserializer, this.durableExecution.plugin, pluginOperationName);
3319
3521
  return callbackFactory(nameOrConfig, maybeConfig);
3320
3522
  });
3321
3523
  }
@@ -4151,7 +4353,7 @@ function formatDurableLogData(level, logData, enrichLogContext, ...messageParams
4151
4353
  executionArn: logData.executionArn
4152
4354
  };
4153
4355
  const tenantId = logData.tenantId;
4154
- if (tenantId != void 0 && tenantId != null) {
4356
+ if (tenantId != null) {
4155
4357
  result.tenantId = tenantId;
4156
4358
  }
4157
4359
  if (logData.operationId !== void 0) {
@@ -4324,7 +4526,7 @@ function isInLambdaRuntime() {
4324
4526
  }
4325
4527
  var isRuntimeBundled = isInLambdaRuntime();
4326
4528
  var SDK_NAME = "aws-durable-execution-sdk-js";
4327
- var baseVersion = "2.3.0";
4529
+ var baseVersion = "2.3.1";
4328
4530
  var SDK_VERSION = isRuntimeBundled ? `${baseVersion}-bundled` : baseVersion;
4329
4531
  var lambdaModulePromise;
4330
4532
  var defaultLambdaClient;
@@ -4893,52 +5095,41 @@ async function runHandler(event, context, executionContext, durableExecutionMode
4893
5095
  log("\u{1F6D1}", "Serdes failed - terminating Lambda execution");
4894
5096
  throw new SerdesFailedError(result.message);
4895
5097
  }
4896
- if (resultType === "termination" && result.reason === TerminationReason.CONTEXT_VALIDATION_ERROR) {
4897
- log("\u{1F6D1}", "Context validation error - returning FAILED status");
4898
- const response = {
4899
- Status: InvocationStatus.FAILED,
4900
- Error: createErrorObjectFromError(result.error || new Error(result.message))
4901
- };
4902
- await plugin.onInvocationEnd?.({
4903
- ...invocationBaseInfo,
4904
- status: PluginInvocationStatus.FAILED,
4905
- executionInput: customerHandlerEvent,
4906
- executionError: result.error || new Error(result.message),
4907
- executionResult: void 0,
4908
- operations: toOperationInfoMap(executionContext._stepData)
5098
+ if (resultType === "termination") {
5099
+ if (classifyTermination(result.reason) === "suspend") {
5100
+ log("\u{1F6D1}", "Returning termination response", {
5101
+ reason: result.reason
5102
+ });
5103
+ await plugin.onInvocationEnd?.({
5104
+ ...invocationBaseInfo,
5105
+ status: PluginInvocationStatus.PENDING,
5106
+ executionInput: customerHandlerEvent,
5107
+ executionResult: void 0,
5108
+ executionError: void 0,
5109
+ operations: toOperationInfoMap(executionContext._stepData)
5110
+ });
5111
+ return {
5112
+ Status: InvocationStatus.PENDING
5113
+ };
5114
+ }
5115
+ const error = result.error ?? new Error(result.message);
5116
+ log("\u{1F6D1}", `Terminated with ${result.reason} - returning FAILED status`, {
5117
+ message: result.message
4909
5118
  });
4910
- return response;
4911
- }
4912
- if (resultType === "termination" && result.reason === TerminationReason.CONFIG_VALIDATION_ERROR) {
4913
- log("\u{1F6D1}", "Config validation error - returning FAILED status");
4914
5119
  const response = {
4915
5120
  Status: InvocationStatus.FAILED,
4916
- Error: createErrorObjectFromError(result.error || new Error(result.message))
5121
+ Error: createErrorObjectFromError(error)
4917
5122
  };
4918
5123
  await plugin.onInvocationEnd?.({
4919
5124
  ...invocationBaseInfo,
4920
5125
  status: PluginInvocationStatus.FAILED,
4921
5126
  executionInput: customerHandlerEvent,
4922
- executionError: result.error || new Error(result.message),
5127
+ executionError: error,
4923
5128
  executionResult: void 0,
4924
5129
  operations: toOperationInfoMap(executionContext._stepData)
4925
5130
  });
4926
5131
  return response;
4927
5132
  }
4928
- if (resultType === "termination") {
4929
- log("\u{1F6D1}", "Returning termination response");
4930
- await plugin.onInvocationEnd?.({
4931
- ...invocationBaseInfo,
4932
- status: PluginInvocationStatus.PENDING,
4933
- executionInput: customerHandlerEvent,
4934
- executionResult: void 0,
4935
- executionError: void 0,
4936
- operations: toOperationInfoMap(executionContext._stepData)
4937
- });
4938
- return {
4939
- Status: InvocationStatus.PENDING
4940
- };
4941
- }
4942
5133
  log("\u2705", "Returning normal completion response");
4943
5134
  const serializedResult = JSON.stringify(result);
4944
5135
  const serializedSize = new TextEncoder().encode(serializedResult).length;
package/lib/index.js CHANGED
@@ -50,7 +50,7 @@ const detector_function_1 = require("./funcs/detector-function");
50
50
  * every stable stack in the account and region is inspected.
51
51
  */
52
52
  class CloudformationStackDriftDetector extends constructs_1.Construct {
53
- static [JSII_RTTI_SYMBOL_1] = { fqn: "cloudformation-stack-drift-detector.CloudformationStackDriftDetector", version: "0.1.4" };
53
+ static [JSII_RTTI_SYMBOL_1] = { fqn: "cloudformation-stack-drift-detector.CloudformationStackDriftDetector", version: "0.1.5" };
54
54
  /**
55
55
  * SNS topic that receives drift notifications.
56
56
  */
package/package.json CHANGED
@@ -58,14 +58,14 @@
58
58
  "eslint": "^9",
59
59
  "eslint-import-resolver-typescript": "^4.4.5",
60
60
  "eslint-plugin-import": "^2.32.0",
61
- "jest": "^30.5.0",
61
+ "jest": "^30.5.1",
62
62
  "jest-junit": "^17",
63
63
  "jsii": "6.0.x",
64
64
  "jsii-diff": "^1.140.0",
65
65
  "jsii-docgen": "^10.5.0",
66
66
  "jsii-pacmak": "^1.140.0",
67
67
  "jsii-rosetta": "6.0.x",
68
- "projen": "^0.103.11",
68
+ "projen": "^0.103.20",
69
69
  "ts-jest": "^29.4.12",
70
70
  "ts-node": "^10.9.2",
71
71
  "typescript": "6.0.x"
@@ -99,7 +99,7 @@
99
99
  "publishConfig": {
100
100
  "access": "public"
101
101
  },
102
- "version": "0.1.4",
102
+ "version": "0.1.5",
103
103
  "jest": {
104
104
  "coverageProvider": "v8",
105
105
  "testMatch": [