cloudformation-stack-drift-detector 0.1.3 → 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 +3 -3
- package/assets/funcs/detector.lambda/index.js +255 -64
- package/lib/index.js +1 -1
- package/package.json +5 -5
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.
|
|
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.
|
|
8700
|
-
"fingerprint": "
|
|
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 *
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
},
|
|
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.
|
|
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
|
-
|
|
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
|
|
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 !=
|
|
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.
|
|
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"
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
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(
|
|
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:
|
|
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.
|
|
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.
|
|
61
|
+
"jest": "^30.5.1",
|
|
62
62
|
"jest-junit": "^17",
|
|
63
63
|
"jsii": "6.0.x",
|
|
64
|
-
"jsii-diff": "^1.
|
|
64
|
+
"jsii-diff": "^1.140.0",
|
|
65
65
|
"jsii-docgen": "^10.5.0",
|
|
66
|
-
"jsii-pacmak": "^1.
|
|
66
|
+
"jsii-pacmak": "^1.140.0",
|
|
67
67
|
"jsii-rosetta": "6.0.x",
|
|
68
|
-
"projen": "^0.103.
|
|
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.
|
|
102
|
+
"version": "0.1.5",
|
|
103
103
|
"jest": {
|
|
104
104
|
"coverageProvider": "v8",
|
|
105
105
|
"testMatch": [
|