@go-to-k/cdkd 0.288.1 → 0.288.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/{asg-provider-4QzW_6q5.js → asg-provider-CQrHElrl.js} +16 -12
- package/dist/{asg-provider-4QzW_6q5.js.map → asg-provider-CQrHElrl.js.map} +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-ZPkBIilF.js → deploy-engine-CfxcC3q3.js} +798 -110
- package/dist/deploy-engine-CfxcC3q3.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-BPeZLD0q.js → program-BXH3FeeV.js} +1436 -552
- package/dist/{program-BPeZLD0q.js.map → program-BXH3FeeV.js.map} +1 -1
- package/dist/{version-CGFRodgE.js → version-CEyZvTcB.js} +2 -2
- package/dist/{version-CGFRodgE.js.map → version-CEyZvTcB.js.map} +1 -1
- package/package.json +11 -21
- package/dist/deploy-engine-ZPkBIilF.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { _ as withStackName, d as applyDefaultNameForFallback, f as generateResourceName, h as looksLikeCdkdGeneratedName, m as getCurrentStackName, n as getLogger, p as generateResourceNameWithFallback, r as isStdoutReservedForPayload, s as getLiveRenderer } from "./logger-Dw-3y48G.js";
|
|
2
2
|
import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-CEyZvTcB.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
-
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
7
7
|
import { CloudControlClient, CreateResourceCommand, DeleteResourceCommand, GetResourceCommand, GetResourceRequestStatusCommand, ListResourcesCommand, UpdateResourceCommand } from "@aws-sdk/client-cloudcontrol";
|
|
8
8
|
import { AttachRolePolicyCommand, CreateRoleCommand, DeleteRoleCommand, DeleteRolePermissionsBoundaryCommand, DeleteRolePolicyCommand, DetachRolePolicyCommand, GetRoleCommand, GetRolePolicyCommand, IAMClient, ListAttachedRolePoliciesCommand, ListInstanceProfilesForRoleCommand, ListRolePoliciesCommand, ListRoleTagsCommand, NoSuchEntityException, PutRolePermissionsBoundaryCommand, PutRolePolicyCommand, RemoveRoleFromInstanceProfileCommand, TagRoleCommand, UntagRoleCommand, UpdateAssumeRolePolicyCommand, UpdateRoleCommand } from "@aws-sdk/client-iam";
|
|
@@ -2337,7 +2337,7 @@ async function resolveCrossAccountStateBucket(accountId, credentials) {
|
|
|
2337
2337
|
/** Cloud assembly schema version compatible with CDK v2 */
|
|
2338
2338
|
const CDK_ASM_VERSION = "38.0.0";
|
|
2339
2339
|
/** Maximum context size before overflow to temp file (32KB) */
|
|
2340
|
-
const CONTEXT_OVERFLOW_LIMIT =
|
|
2340
|
+
const CONTEXT_OVERFLOW_LIMIT = 32768;
|
|
2341
2341
|
/**
|
|
2342
2342
|
* Remove one matched pair of surrounding quotes from a shell token, so a quoted
|
|
2343
2343
|
* entrypoint (`"bin/app.js"`) is recognized as a .js file and re-quoted cleanly.
|
|
@@ -2551,7 +2551,6 @@ function processStackMessages(stacks, logger, options = {}) {
|
|
|
2551
2551
|
case "error":
|
|
2552
2552
|
hasErrors = true;
|
|
2553
2553
|
logger.error(`[Error at ${msg.path}] ${msg.message}`);
|
|
2554
|
-
break;
|
|
2555
2554
|
}
|
|
2556
2555
|
const failAt = options.strict ? "warn" : options.ignoreErrors ? "none" : "error";
|
|
2557
2556
|
if (hasErrors && failAt !== "none") throw new SynthesisError("Found errors");
|
|
@@ -3174,10 +3173,7 @@ var CcApiContextProvider = class {
|
|
|
3174
3173
|
case "at-least-one":
|
|
3175
3174
|
if (count < 1) throw new Error(`Expected at least one ${typeName}${context}, found none`);
|
|
3176
3175
|
break;
|
|
3177
|
-
case "at-most-one":
|
|
3178
|
-
if (count > 1) throw new Error(`Expected at most one ${typeName}${context}, found ${count}`);
|
|
3179
|
-
break;
|
|
3180
|
-
case "any": break;
|
|
3176
|
+
case "at-most-one": if (count > 1) throw new Error(`Expected at most one ${typeName}${context}, found ${count}`);
|
|
3181
3177
|
}
|
|
3182
3178
|
}
|
|
3183
3179
|
/**
|
|
@@ -4514,7 +4510,8 @@ async function purgeNoncurrentKeyVersions(s3Client, bucket, keys, options = {})
|
|
|
4514
4510
|
const elided = failed.size - named.length;
|
|
4515
4511
|
logger.warn(`Could not purge noncurrent versions of ${failed.size} key(s) in s3://${displaySafe(bucket, { asciiOnly: true })}. Their previous versions survive and remain readable via GetObject with a VersionId (${options.objectDescription ?? "the body of an object cdkd has just reported as removed"}). Grant s3:ListBucketVersions and s3:DeleteObjectVersion on the state bucket, or purge the key(s) by hand. Failures: ${named.join(", ")}` + (elided > 0 ? ` (and ${elided} more)` : ""));
|
|
4516
4512
|
}
|
|
4517
|
-
|
|
4513
|
+
const replicationKeys = [.../* @__PURE__ */ new Set([...purged, ...unsettledBodies])];
|
|
4514
|
+
await warnIfPurgeIsReplicated(s3Client, bucket, replicationKeys, {
|
|
4518
4515
|
requestFields,
|
|
4519
4516
|
logger,
|
|
4520
4517
|
...options.objectDescription !== void 0 && { objectDescription: options.objectDescription }
|
|
@@ -5114,7 +5111,8 @@ async function expandMacrosAttempt(template, opts, logger) {
|
|
|
5114
5111
|
ChangeSetName: changeSetName
|
|
5115
5112
|
})).catch(() => void 0);
|
|
5116
5113
|
const reason = desc?.StatusReason ?? "unknown (DescribeChangeSet failed)";
|
|
5117
|
-
|
|
5114
|
+
const status = desc?.Status ?? "UNKNOWN";
|
|
5115
|
+
throw new MacroExpansionError(`CloudFormation macro expansion failed (status=${status}): ${reason}`, waiterError instanceof Error ? waiterError : void 0);
|
|
5118
5116
|
}
|
|
5119
5117
|
const tpl = await cfn.send(new GetTemplateCommand({
|
|
5120
5118
|
StackName: transientStackName,
|
|
@@ -5123,7 +5121,10 @@ async function expandMacrosAttempt(template, opts, logger) {
|
|
|
5123
5121
|
}));
|
|
5124
5122
|
if (tpl.TemplateBody === void 0 || tpl.TemplateBody === null) throw new MacroExpansionError(`CloudFormation returned no Processed-stage template body for the macro-expansion changeset. This typically indicates a CFn-side regression — re-run, and if the failure persists open an issue with the transforms involved: [${macros.join(", ")}].`);
|
|
5125
5123
|
const expanded = parseTemplateBody(tpl.TemplateBody);
|
|
5126
|
-
if (containsMacro(expanded))
|
|
5124
|
+
if (containsMacro(expanded)) {
|
|
5125
|
+
const inner = enumerateMacros(expanded);
|
|
5126
|
+
throw new MacroExpansionError(`Macro expansion produced a template that still contains macros [${inner.join(", ")}]. Multi-stage macros (a macro whose expansion emits another macro reference) are intentionally out of scope in cdkd v1 — see https://github.com/go-to-k/cdkd/issues/463. If you need this pattern, manually pre-expand the template and deploy the result.`);
|
|
5127
|
+
}
|
|
5127
5128
|
logger.debug(`Macro expansion: success — ${Object.keys(expanded.Resources ?? {}).length} resources after expansion.`);
|
|
5128
5129
|
return expanded;
|
|
5129
5130
|
} finally {
|
|
@@ -5245,7 +5246,8 @@ function loadJsonConfig(filePath) {
|
|
|
5245
5246
|
* Load cdk.json from the current working directory
|
|
5246
5247
|
*/
|
|
5247
5248
|
function loadCdkJson(cwd) {
|
|
5248
|
-
|
|
5249
|
+
const dir = cwd || process.cwd();
|
|
5250
|
+
return loadJsonConfig(resolve(dir, "cdk.json"));
|
|
5249
5251
|
}
|
|
5250
5252
|
/**
|
|
5251
5253
|
* Load user-level defaults from ~/.cdk.json
|
|
@@ -6200,8 +6202,10 @@ async function spawnStreaming(cmd, args, options = {}) {
|
|
|
6200
6202
|
const stderrChunks = [];
|
|
6201
6203
|
child.stdout.on("data", (chunk) => {
|
|
6202
6204
|
stdoutChunks.push(chunk);
|
|
6203
|
-
if (streamLive)
|
|
6204
|
-
|
|
6205
|
+
if (streamLive) {
|
|
6206
|
+
if (isStdoutReservedForPayload()) process.stderr.write(chunk);
|
|
6207
|
+
else process.stdout.write(chunk);
|
|
6208
|
+
}
|
|
6205
6209
|
});
|
|
6206
6210
|
child.stderr.on("data", (chunk) => {
|
|
6207
6211
|
stderrChunks.push(chunk);
|
|
@@ -7831,10 +7835,11 @@ async function assertAssetBucketRegion(s3Client, bucketName, expectedRegion, acc
|
|
|
7831
7835
|
const fromHeader = readBucketRegionHeader(cause);
|
|
7832
7836
|
if (fromHeader) actual = canonicalizeRegion(fromHeader);
|
|
7833
7837
|
else try {
|
|
7834
|
-
|
|
7838
|
+
const location = await s3Client.send(new GetBucketLocationCommand({
|
|
7835
7839
|
Bucket: bucketName,
|
|
7836
7840
|
ExpectedBucketOwner: accountId
|
|
7837
|
-
}))
|
|
7841
|
+
}));
|
|
7842
|
+
actual = canonicalizeRegion(bucketLocationToRegion$1(location.LocationConstraint));
|
|
7838
7843
|
} catch (probeError) {
|
|
7839
7844
|
const fromProbe = readBucketRegionHeader(probeError);
|
|
7840
7845
|
if (fromProbe) actual = canonicalizeRegion(fromProbe);
|
|
@@ -9000,7 +9005,8 @@ var AssetPublisher = class {
|
|
|
9000
9005
|
const err = error;
|
|
9001
9006
|
const message = stringifyValue(err["message"] || err["name"] || error);
|
|
9002
9007
|
const code = stringifyValue(err["Code"] || err["code"] || err["name"] || "");
|
|
9003
|
-
|
|
9008
|
+
const detail = code ? `${code}: ${message}` : message;
|
|
9009
|
+
throw new AssetError(`Asset publishing failed: ${detail}`, error instanceof Error ? error : void 0);
|
|
9004
9010
|
}
|
|
9005
9011
|
}
|
|
9006
9012
|
/**
|
|
@@ -9095,6 +9101,18 @@ function importableOutputs(state) {
|
|
|
9095
9101
|
function exportNamesCarriedFrom(previous) {
|
|
9096
9102
|
return previous.exportNames === void 0 ? {} : { exportNames: previous.exportNames };
|
|
9097
9103
|
}
|
|
9104
|
+
/**
|
|
9105
|
+
* The `skippedOutputs` field to write when a save carries the PREVIOUS
|
|
9106
|
+
* record's `outputs` bag forward instead of re-resolving it (issue #2740). The
|
|
9107
|
+
* record describes that bag — which keys the deploy that wrote it could not
|
|
9108
|
+
* resolve — so it travels with the bag, exactly like {@link
|
|
9109
|
+
* exportNamesCarriedFrom}: absent stays absent, present stays as it was. A
|
|
9110
|
+
* writer that re-resolves outputs does NOT call this; it writes the set the
|
|
9111
|
+
* resolution produced, or omits the field when nothing was skipped.
|
|
9112
|
+
*/
|
|
9113
|
+
function skippedOutputsCarriedFrom(previous) {
|
|
9114
|
+
return previous.skippedOutputs === void 0 ? {} : { skippedOutputs: previous.skippedOutputs };
|
|
9115
|
+
}
|
|
9098
9116
|
|
|
9099
9117
|
//#endregion
|
|
9100
9118
|
//#region src/types/rollback-journal.ts
|
|
@@ -10184,9 +10202,10 @@ var S3StateBackend = class {
|
|
|
10184
10202
|
if (isNoSuchKey(error)) return { kind: "absent" };
|
|
10185
10203
|
const { detail } = describeAwsFailure(error);
|
|
10186
10204
|
this.logger.debug(`Could not read legacy state region for '${this.displayName(stackName)}': ${displaySafe(detail, { asciiOnly: true }) || "<unrenderable>"}`);
|
|
10205
|
+
const cls = error instanceof Error && error.name ? error.name : "an unknown error";
|
|
10187
10206
|
return {
|
|
10188
10207
|
kind: "unreadable",
|
|
10189
|
-
reason: displaySafe(
|
|
10208
|
+
reason: displaySafe(cls, { asciiOnly: true }) || "<unrenderable>"
|
|
10190
10209
|
};
|
|
10191
10210
|
}
|
|
10192
10211
|
}
|
|
@@ -10289,7 +10308,7 @@ function isNoSuchKey(error) {
|
|
|
10289
10308
|
* tolerates FOURTEEN consecutive missed renewals before it lapses, and an
|
|
10290
10309
|
* ordinary broad-set integ (~8 min) performs three.
|
|
10291
10310
|
*/
|
|
10292
|
-
const MAX_RENEWAL_INTERVAL_MS =
|
|
10311
|
+
const MAX_RENEWAL_INTERVAL_MS = 12e4;
|
|
10293
10312
|
/**
|
|
10294
10313
|
* Renew after at most a quarter of the TTL, so a short TTL still gets three
|
|
10295
10314
|
* chances to renew before it lapses. This is what binds the two numbers
|
|
@@ -11093,7 +11112,9 @@ var LockManager = class {
|
|
|
11093
11112
|
const expiresIn = lockInfo ? this.formatDuration(lockInfo.expiresAt - Date.now()) : "unknown";
|
|
11094
11113
|
const forceUnlockCommand = buildForceUnlockCommand(stackName, region);
|
|
11095
11114
|
const recovery = forceUnlockCommand ? `If you are certain no other process is active, run: ${forceUnlockCommand}` : `If you are certain no other process is active, ${UNREPRODUCIBLE_LOCK_CLAUSE.charAt(0).toLowerCase()}${UNREPRODUCIBLE_LOCK_CLAUSE.slice(1)}`;
|
|
11096
|
-
|
|
11115
|
+
const safeStack = displaySafe(stackName, { asciiOnly: true }) || "<unrenderable>";
|
|
11116
|
+
const safeRegion = displaySafe(region, { asciiOnly: true }) || "<unrenderable>";
|
|
11117
|
+
throw new LockError(`Failed to acquire lock for stack '${safeStack}' (${safeRegion}) after ${maxRetries + 1} attempts. ` + (lockInfo ? `Locked by: ${lockInfo.owner}${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ""}, expires in: ${expiresIn}. ` + recovery : `Lock exists but could not read lock info. ${recovery}`));
|
|
11097
11118
|
}
|
|
11098
11119
|
};
|
|
11099
11120
|
|
|
@@ -11197,6 +11218,102 @@ function resolvedPlaintextOf(secrets, expression) {
|
|
|
11197
11218
|
return typeof recorded === "string" ? recorded : void 0;
|
|
11198
11219
|
}
|
|
11199
11220
|
/**
|
|
11221
|
+
* The bag OBJECTS a deploy pass produced ITSELF and installed on a success
|
|
11222
|
+
* path — the fact the resolved-pair evidence above cannot state (issue
|
|
11223
|
+
* [#2516](https://github.com/go-to-k/cdkd/issues/2516)).
|
|
11224
|
+
*
|
|
11225
|
+
* A pair proves that THIS pass resolved a token to a plaintext; it says nothing
|
|
11226
|
+
* about which bag is being walked. The deploy engine's persist choke point
|
|
11227
|
+
* walks EVERY record in the state map against today's template, and a record
|
|
11228
|
+
* that merely ENTERED the create/update arm is still the PREVIOUS generation
|
|
11229
|
+
* until its provider call succeeds (an intermediate save, a pre/post-rollback
|
|
11230
|
+
* save, Ctrl-C — the same population the `sourceIsSameGeneration` note on
|
|
11231
|
+
* {@link PathSourceRules} names). One record also carries two bags of
|
|
11232
|
+
* different provenance: `properties`, this pass's resolved bag once the
|
|
11233
|
+
* provider succeeded, and `observedProperties`, an AWS readback installed
|
|
11234
|
+
* separately. So the fact is BAG-specific, not caller- or record-level, and it
|
|
11235
|
+
* is carried on the object: {@link markSameGenerationBag} at the moment the
|
|
11236
|
+
* successful result is installed, consulted by {@link redactSecretsForState}
|
|
11237
|
+
* for the object it is handed. A copy of the bag, a derived needle map, a
|
|
11238
|
+
* previous generation's record, a scrub / import / drift walk and a bag the
|
|
11239
|
+
* engine did not mark all answer `false` and keep the fall-through.
|
|
11240
|
+
*
|
|
11241
|
+
* A `WeakSet` for the reason {@link resolvedPairsOf} is a `WeakMap`: the mark
|
|
11242
|
+
* dies with the object it is on, and nothing has to clear it.
|
|
11243
|
+
*/
|
|
11244
|
+
const sameGenerationBags = /* @__PURE__ */ new WeakSet();
|
|
11245
|
+
/**
|
|
11246
|
+
* Mark `bag` as an object THIS pass is entitled to speak for, and return it.
|
|
11247
|
+
* The one evidence {@link positionByEmbeddedSpan} needs beyond a resolved pair
|
|
11248
|
+
* to write a middle SHORTER than {@link MIN_NEEDLE_LENGTH} — see the arm.
|
|
11249
|
+
*
|
|
11250
|
+
* Deliberately NOT "produced by resolving today's template, read back from a
|
|
11251
|
+
* resource it just wrote, and about to be installed on a success path": each
|
|
11252
|
+
* of those three was in this sentence and each is false at one site (an
|
|
11253
|
+
* unchanged resource's auto-refresh resolves nothing; the no-change re-check
|
|
11254
|
+
* and the journal are never installed on the record). The conditions are per
|
|
11255
|
+
* site and are listed below.
|
|
11256
|
+
*
|
|
11257
|
+
* WHAT THE MARK CLAIMS, which is the only thing every site shares: the object
|
|
11258
|
+
* is one THIS PASS is entitled to speak for, so a sub-floor middle in it may
|
|
11259
|
+
* be written as the token this pass recorded. What never qualifies is a bag of
|
|
11260
|
+
* MIXED provenance: a provider's `effectiveProperties` replacement may carry
|
|
11261
|
+
* previous-state values IN, so an object-level mark on it would prove nothing
|
|
11262
|
+
* for those leaves — it stays unmarked and keeps the residual. Nor does the
|
|
11263
|
+
* object the resolver produced, marked at resolution time: it is not
|
|
11264
|
+
* necessarily the object state ends up holding.
|
|
11265
|
+
*
|
|
11266
|
+
* The CONDITIONS are per SITE, not global, and stating them globally is what
|
|
11267
|
+
* this paragraph kept getting wrong (PR 2753, rounds 3 and 4). "After the
|
|
11268
|
+
* provider call" is false for the no-change re-check, which marks BEFORE it
|
|
11269
|
+
* and may skip it entirely. "Every leaf this pass produced" is false for the
|
|
11270
|
+
* auto-refresh readback of an UNCHANGED resource, where nothing was resolved
|
|
11271
|
+
* at all — there the safety comes from the empty secrets map, not from the
|
|
11272
|
+
* mark. Read the per-site list below rather than a rule over all five.
|
|
11273
|
+
*
|
|
11274
|
+
* The five call sites, and which of them the record HOLDS, because the earlier
|
|
11275
|
+
* "two never-installed copies" reading of this paragraph was false once the
|
|
11276
|
+
* third copy arrived:
|
|
11277
|
+
* - `propertiesToRecord` — the record's `properties`, installed. The resolved
|
|
11278
|
+
* bag itself when the route drops nothing, a narrowed copy of it when it
|
|
11279
|
+
* does, and `withoutSilentDropProperties` returns its input by reference in
|
|
11280
|
+
* the first case, which is why the mark must be taken AFTER the narrowing
|
|
11281
|
+
* and not before.
|
|
11282
|
+
* - `drainObservedCaptures` — the `observedProperties` readback it installs,
|
|
11283
|
+
* and a COPY: the object a provider returned is never the marked one, so a
|
|
11284
|
+
* provider that hands back its own `properties` argument cannot get a
|
|
11285
|
+
* previous generation marked. It marks EVERY capture it drains, which is
|
|
11286
|
+
* wider than "a resource this pass wrote" — the schema-upgrade auto-refresh
|
|
11287
|
+
* of an UNCHANGED resource is marked too. What keeps that safe is the map,
|
|
11288
|
+
* not the mark: `perResourceSecrets` is populated only in the create /
|
|
11289
|
+
* update arms, so an unchanged resource's walk carries an empty one and the
|
|
11290
|
+
* span arm cannot fire whatever the object is marked.
|
|
11291
|
+
* - `resolveOutputs` — the `outputs` bag this pass resolved. What every site
|
|
11292
|
+
* shares is that the marked object is the redaction INPUT; whether the
|
|
11293
|
+
* record then holds that same object varies, and here it depends on whether
|
|
11294
|
+
* this pass recorded any output secret: with one,
|
|
11295
|
+
* `redactOutputs` returns a fresh redacted bag and that return value is
|
|
11296
|
+
* installed; with none — the ordinary deploy — it returns its input
|
|
11297
|
+
* unchanged and the marked object IS the one
|
|
11298
|
+
* stored. On the no-change path the newly redacted bag is installed when
|
|
11299
|
+
* the outputs CHANGED, and the previous `persistedOutputs` is kept
|
|
11300
|
+
* otherwise; that previous bag is unmarked, which is the answer that arm
|
|
11301
|
+
* wants.
|
|
11302
|
+
* - the update arm's no-change re-check — a marked `{ ...resolvedProps }`
|
|
11303
|
+
* compared against the stored record so a stored token reads as a no-op.
|
|
11304
|
+
* NOT installed; the object the provider is handed is the unmarked original.
|
|
11305
|
+
* - the rollback journal's FAILED-op `attemptedProperties` — a marked copy, so
|
|
11306
|
+
* that persisted artifact does not carry the plaintext on exactly the
|
|
11307
|
+
* failure path. NOT installed on the record.
|
|
11308
|
+
*/
|
|
11309
|
+
function markSameGenerationBag(bag) {
|
|
11310
|
+
sameGenerationBags.add(bag);
|
|
11311
|
+
return bag;
|
|
11312
|
+
}
|
|
11313
|
+
function isSameGenerationBag(bag) {
|
|
11314
|
+
return sameGenerationBags.has(bag);
|
|
11315
|
+
}
|
|
11316
|
+
/**
|
|
11200
11317
|
* Every `{{resolve:...}}` expression this process has PROVEN resolves to a
|
|
11201
11318
|
* secret, as a SET — uncollapsed by resolved value (issue #1910).
|
|
11202
11319
|
*
|
|
@@ -12847,15 +12964,78 @@ function singleSpanFrame(bag, source) {
|
|
|
12847
12964
|
* not create is the whole-token arm's: a middle that is ALREADY an expression
|
|
12848
12965
|
* (a persisted answer from another generation), which the token refusal below
|
|
12849
12966
|
* keeps out — and, by the same argument, any leaf the value scan would NOT
|
|
12850
|
-
* rewrite to exactly `prefix + survivor + suffix`: a
|
|
12851
|
-
*
|
|
12852
|
-
*
|
|
12853
|
-
*
|
|
12854
|
-
*
|
|
12855
|
-
*
|
|
12856
|
-
*
|
|
12857
|
-
*
|
|
12858
|
-
*
|
|
12967
|
+
* rewrite to exactly `prefix + survivor + suffix`: a whole leaf that is itself
|
|
12968
|
+
* another recorded plaintext, a needle starting in the prefix and overlapping
|
|
12969
|
+
* the middle, and — on a bag whose generation is NOT proven — a middle shorter
|
|
12970
|
+
* than the scan's needle floor. The arm checks that equivalence against the
|
|
12971
|
+
* scan's own answer rather than re-deriving the scan's rules. Pinned by the
|
|
12972
|
+
* cross-generation cases in `secret-redaction-embedded-span.test.ts`.
|
|
12973
|
+
*
|
|
12974
|
+
* BELOW THE NEEDLE FLOOR the scan makes no claim at all (issue
|
|
12975
|
+
* [#2516](https://github.com/go-to-k/cdkd/issues/2516)): {@link buildNeedleRegex}
|
|
12976
|
+
* drops every plaintext shorter than {@link MIN_NEEDLE_LENGTH} from its
|
|
12977
|
+
* alternation, so an embedded 1-3 character secret is left in plaintext by
|
|
12978
|
+
* the scan, with or without a same-plaintext sibling. Accepting the scan's
|
|
12979
|
+
* silence (`scanned === bag`) as equivalence would therefore be a NEW claim
|
|
12980
|
+
* rather than a choice within the scan's class, and on a previous
|
|
12981
|
+
* generation's bag it would fabricate: a 1-3 character readback or old
|
|
12982
|
+
* record that COINCIDES with today's plaintext (`port:0` where AWS returns a
|
|
12983
|
+
* default and today's secret resolved to `0`) would be persisted as today's
|
|
12984
|
+
* expression, which round-trips today and, after a rotation, reports a drift
|
|
12985
|
+
* that never happened. So that arm is admitted only for a bag whose
|
|
12986
|
+
* generation IS proven: `bagIsSameGeneration`, the object-level mark
|
|
12987
|
+
* {@link markSameGenerationBag} puts on the bags the deploy engine hands to
|
|
12988
|
+
* redaction on a success path (the record's `properties`, which are the
|
|
12989
|
+
* resolved bag or the subset of it the SDK route writes; EVERY
|
|
12990
|
+
* `observedProperties` readback `drainObservedCaptures` drains, an unchanged
|
|
12991
|
+
* resource's auto-refresh included, where the empty secrets map rather than
|
|
12992
|
+
* the mark is what keeps it safe; and the `outputs` bag this pass resolved,
|
|
12993
|
+
* whose redacted RETURN VALUE is usually what the record holds) and on two
|
|
12994
|
+
* copies of the resolver's own output that the record never holds. That
|
|
12995
|
+
* function's contract is the authority — the conditions differ per site and
|
|
12996
|
+
* do not survive being stated once. Threaded down from
|
|
12997
|
+
* {@link redactSecretsForState} for exactly the object it was handed. With
|
|
12998
|
+
* the mark AND the pair, `recorded === middle` says this pass resolved the
|
|
12999
|
+
* source token to the middle and the bag is this pass's own, so
|
|
13000
|
+
* `prefix + token + suffix` is what the template says at that leaf and what
|
|
13001
|
+
* the resource holds. Without the mark the sub-floor middle keeps today's
|
|
13002
|
+
* bound, which is the scan's answer: the plaintext, unchanged.
|
|
13003
|
+
*
|
|
13004
|
+
* What stays, stated here rather than papered over. A provider that
|
|
13005
|
+
* substituted `effectiveProperties` built a bag of MIXED provenance, so that
|
|
13006
|
+
* object is never marked and a sub-floor middle in it keeps the scan's
|
|
13007
|
+
* answer. A readback AWS rewrote at that offset to a value that coincides
|
|
13008
|
+
* with the 1-3 character secret is persisted as the expression: value-equal
|
|
13009
|
+
* until the secret rotates, and the same residual the value scan already has
|
|
13010
|
+
* for a 4+ character coincidence in a readback. Every refusal of this arm
|
|
13011
|
+
* returns the scan's answer, so a leaf refused for interference (a frame
|
|
13012
|
+
* that is itself a recorded plaintext, say) has its frame rewritten and its
|
|
13013
|
+
* sub-floor middle left in plaintext. And this arm positions a LITERAL
|
|
13014
|
+
* source leaf only: an `Fn::Join` / `Fn::Sub` source rendering the same
|
|
13015
|
+
* `port:` + token goes to {@link positionByIntrinsicSkeleton}, which refuses
|
|
13016
|
+
* unless the WHOLE bag is a recorded plaintext, so a sub-floor secret
|
|
13017
|
+
* embedded through an intrinsic — the dominant CDK shape — still falls to
|
|
13018
|
+
* the value scan and persists in plaintext. `cdkd scrub`, the documented
|
|
13019
|
+
* repair tool for a pre-GHSA record, cannot repair a sub-floor embedded leaf
|
|
13020
|
+
* either: it walks a STORED bag, which no deploy marked, so the arm is
|
|
13021
|
+
* unreachable from it by construction and the leaf keeps the scan's answer.
|
|
13022
|
+
* The MASKING channel keeps the residual whole: `maskSecretsInText`'s
|
|
13023
|
+
* substring arm carries the same four-character floor, so once this arm has
|
|
13024
|
+
* put `port:{{resolve:...}}` in state, a warn line quoting an AWS message can
|
|
13025
|
+
* still print `port:q7`. Pre-existing and not a regression -- the #2453 class,
|
|
13026
|
+
* and the reason the list would otherwise read as complete when it is not
|
|
13027
|
+
* (maintainer review of PR 2753, round 2).
|
|
13028
|
+
*
|
|
13029
|
+
* The next DEPLOY of that resource repairs the leaves this arm is eligible
|
|
13030
|
+
* for — a LITERAL source leaf on a bag the engine marks — because the
|
|
13031
|
+
* re-check compares unequal against a record holding the plaintext and the
|
|
13032
|
+
* resource is written again from a bag this pass produced. The residuals
|
|
13033
|
+
* named above (an `effectiveProperties` substitution, an intrinsic source
|
|
13034
|
+
* shape) are not repaired by that deploy either; they stay tracked by
|
|
13035
|
+
* #2745. Tracked, with the sub-floor
|
|
13036
|
+
* residuals of a nested-stack child's inherited parameter and of `cdkd
|
|
13037
|
+
* import`'s own resolution, by issue
|
|
13038
|
+
* [#2745](https://github.com/go-to-k/cdkd/issues/2745).
|
|
12859
13039
|
*
|
|
12860
13040
|
* One shape reaches this arm that a reader may not expect: a WHOLE-token
|
|
12861
13041
|
* source that FAILED the whole-token arm's `isKnownSecretExpression` gate (an
|
|
@@ -12883,7 +13063,7 @@ function singleSpanFrame(bag, source) {
|
|
|
12883
13063
|
* the scan as a parameter, which left the bound one wrong caller away from
|
|
12884
13064
|
* comparing against a scan of some other bag with no type error.
|
|
12885
13065
|
*/
|
|
12886
|
-
function positionByEmbeddedSpan(bag, source, secrets) {
|
|
13066
|
+
function positionByEmbeddedSpan(bag, source, secrets, bagIsSameGeneration) {
|
|
12887
13067
|
const scanned = redactSecretsForState(bag, secrets);
|
|
12888
13068
|
const frame = singleSpanFrame(bag, source);
|
|
12889
13069
|
if (frame === void 0) return scanned;
|
|
@@ -12892,8 +13072,9 @@ function positionByEmbeddedSpan(bag, source, secrets) {
|
|
|
12892
13072
|
if (recorded === void 0 || recorded !== middle) return scanned;
|
|
12893
13073
|
const survivor = secrets.get(middle);
|
|
12894
13074
|
if (survivor === void 0) return scanned;
|
|
12895
|
-
if (scanned
|
|
12896
|
-
return prefix + token + suffix;
|
|
13075
|
+
if (scanned === prefix + survivor + suffix) return prefix + token + suffix;
|
|
13076
|
+
if (bagIsSameGeneration && scanned === bag) return prefix + token + suffix;
|
|
13077
|
+
return scanned;
|
|
12897
13078
|
}
|
|
12898
13079
|
/**
|
|
12899
13080
|
* Keys tried, in order, when pairing two arrays whose ORDER cannot be trusted
|
|
@@ -13089,13 +13270,13 @@ function positionListByCrossStackSource(bag, source, secrets) {
|
|
|
13089
13270
|
* association where the position is a cross-stack read, skeleton where it is a
|
|
13090
13271
|
* describable intrinsic, value where none is.
|
|
13091
13272
|
*/
|
|
13092
|
-
function redactByPath(bag, source, secrets, rules, secretExpressions) {
|
|
13273
|
+
function redactByPath(bag, source, secrets, rules, secretExpressions, bagIsSameGeneration) {
|
|
13093
13274
|
if (isDynamicReferenceString(source) && typeof bag === "string") {
|
|
13094
13275
|
if (isSingleDynamicReferenceToken(source) && (rules.trustAnyExpression || isKnownSecretExpression(source, secretExpressions))) {
|
|
13095
13276
|
if (!rules.sourceIsSameGeneration && isSingleDynamicReferenceToken(bag)) return secrets.get(bag) ?? bag;
|
|
13096
13277
|
return source;
|
|
13097
13278
|
}
|
|
13098
|
-
return positionByEmbeddedSpan(bag, source, secrets);
|
|
13279
|
+
return positionByEmbeddedSpan(bag, source, secrets, bagIsSameGeneration);
|
|
13099
13280
|
}
|
|
13100
13281
|
if (typeof bag === "string" && isPlainObject$2(source)) {
|
|
13101
13282
|
const certified = positionByCrossStackSource(bag, source, secrets);
|
|
@@ -13124,16 +13305,16 @@ function redactByPath(bag, source, secrets, rules, secretExpressions) {
|
|
|
13124
13305
|
const positionalIsExact = rules.descendArrays && bag.length === source.length && orderPreserved;
|
|
13125
13306
|
return bag.map((item, i) => {
|
|
13126
13307
|
const j = partnerIndex[i];
|
|
13127
|
-
if (j >= 0) return redactByPath(item, source[j], secrets, rules, secretExpressions);
|
|
13128
|
-
if (positionalIsExact) return redactByPath(item, source[i], secrets, rules, secretExpressions);
|
|
13308
|
+
if (j >= 0) return redactByPath(item, source[j], secrets, rules, secretExpressions, bagIsSameGeneration);
|
|
13309
|
+
if (positionalIsExact) return redactByPath(item, source[i], secrets, rules, secretExpressions, bagIsSameGeneration);
|
|
13129
13310
|
return redactSecretsForState(item, secrets);
|
|
13130
13311
|
});
|
|
13131
13312
|
}
|
|
13132
|
-
if (rules.descendArrays && bag.length === source.length) return bag.map((item, i) => redactByPath(item, source[i], secrets, rules, secretExpressions));
|
|
13313
|
+
if (rules.descendArrays && bag.length === source.length) return bag.map((item, i) => redactByPath(item, source[i], secrets, rules, secretExpressions, bagIsSameGeneration));
|
|
13133
13314
|
}
|
|
13134
13315
|
if (isPlainObject$2(bag) && isPlainObject$2(source)) {
|
|
13135
13316
|
const out = Object.create(null);
|
|
13136
|
-
for (const [k, v] of Object.entries(bag)) out[k] = Object.hasOwn(source, k) ? redactByPath(v, source[k], secrets, rules, secretExpressions) : redactSecretsForState(v, secrets);
|
|
13317
|
+
for (const [k, v] of Object.entries(bag)) out[k] = Object.hasOwn(source, k) ? redactByPath(v, source[k], secrets, rules, secretExpressions, bagIsSameGeneration) : redactSecretsForState(v, secrets);
|
|
13137
13318
|
return out;
|
|
13138
13319
|
}
|
|
13139
13320
|
return redactSecretsForState(bag, secrets);
|
|
@@ -14151,7 +14332,7 @@ function refuseUncertifiedReadbackPositions(bag, source, secrets, learn, mark) {
|
|
|
14151
14332
|
function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RULES) {
|
|
14152
14333
|
if (secrets.size === 0 && source === void 0) return bag;
|
|
14153
14334
|
if (source !== void 0) {
|
|
14154
|
-
const positioned = redactByPath(bag, source, secrets, rules, recordedExpressionsOf(secrets));
|
|
14335
|
+
const positioned = redactByPath(bag, source, secrets, rules, recordedExpressionsOf(secrets), isSameGenerationBag(bag));
|
|
14155
14336
|
if (!isReadbackProjectedFromState(rules)) return positioned;
|
|
14156
14337
|
const refused = refuseUncertifiedReadbackPositions(positioned, source, secrets);
|
|
14157
14338
|
const derived = deriveReadbackNeedles(bag, source, secrets, rules);
|
|
@@ -14991,7 +15172,10 @@ var DagBuilder = class {
|
|
|
14991
15172
|
this.logger.debug(`Dependency graph built: ${resourceIds.length} nodes, ${edgeCount} edges`);
|
|
14992
15173
|
edgeCount += this.addCustomResourcePolicyEdges(graph, template);
|
|
14993
15174
|
edgeCount += this.addLambdaVpcEdges(graph, template);
|
|
14994
|
-
if (!alg.isAcyclic(graph))
|
|
15175
|
+
if (!alg.isAcyclic(graph)) {
|
|
15176
|
+
const cycles = this.findCycles(graph);
|
|
15177
|
+
throw new DependencyError(`Circular dependency detected in template. Cycles: ${cycles.map((c) => c.join(" -> ")).join("; ")}`);
|
|
15178
|
+
}
|
|
14995
15179
|
return graph;
|
|
14996
15180
|
}
|
|
14997
15181
|
/**
|
|
@@ -15020,7 +15204,10 @@ var DagBuilder = class {
|
|
|
15020
15204
|
const predecessors = graphCopy.predecessors(node);
|
|
15021
15205
|
return !predecessors || predecessors.length === 0;
|
|
15022
15206
|
});
|
|
15023
|
-
if (readyNodes.length === 0)
|
|
15207
|
+
if (readyNodes.length === 0) {
|
|
15208
|
+
const remaining = graphCopy.nodes();
|
|
15209
|
+
throw new DependencyError(`Circular dependency detected. Remaining nodes: ${remaining.join(", ")}`);
|
|
15210
|
+
}
|
|
15024
15211
|
this.logger.debug(`Level ${levelNum}: ${readyNodes.length} resources - ${readyNodes.join(", ")}`);
|
|
15025
15212
|
levels.push(readyNodes);
|
|
15026
15213
|
readyNodes.forEach((node) => {
|
|
@@ -15826,9 +16013,10 @@ const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
15826
16013
|
* 4s/8s steps overshoot it. The dense schedule applies ONLY when the caller
|
|
15827
16014
|
* left the schedule at its defaults — a caller that passed its own
|
|
15828
16015
|
* `maxRetries` / `initialDelayMs` / `maxDelayMs` / `isRetryable` picked that
|
|
15829
|
-
* schedule deliberately (e.g. the DELETE path's 3
|
|
15830
|
-
*
|
|
15831
|
-
*
|
|
16016
|
+
* schedule deliberately (e.g. the DELETE path's 3 retries from 5s — 5s/8s/8s,
|
|
16017
|
+
* since it passes `initialDelayMs` but leaves `maxDelayMs` at the 8s default —
|
|
16018
|
+
* or the delete-then-re-create sites' ~64s budget covering SQS's 60s name
|
|
16019
|
+
* cooldown) and gets it verbatim.
|
|
15832
16020
|
*
|
|
15833
16021
|
* A THIRD class rides its own grid on the default schedule since issue
|
|
15834
16022
|
* [#2116](https://github.com/go-to-k/cdkd/issues/2116): a NAME COOLDOWN (a
|
|
@@ -16511,6 +16699,7 @@ const PROPERTY_COVERAGE_BY_TYPE = /* @__PURE__ */ new Map([
|
|
|
16511
16699
|
"RoleArn"
|
|
16512
16700
|
]),
|
|
16513
16701
|
silentDrop: /* @__PURE__ */ new Map([
|
|
16702
|
+
["CapacityProviderConfiguration", "not yet implemented by cdkd"],
|
|
16514
16703
|
["FilesystemConfigurations", "not yet implemented by cdkd"],
|
|
16515
16704
|
["RequestHeaderConfiguration", "not yet implemented by cdkd"],
|
|
16516
16705
|
["Tags", "not yet implemented by cdkd"]
|
|
@@ -16629,7 +16818,8 @@ const PROPERTY_COVERAGE_BY_TYPE = /* @__PURE__ */ new Map([
|
|
|
16629
16818
|
silentDrop: /* @__PURE__ */ new Map([
|
|
16630
16819
|
["EvaluationCriteria", "Absent from both the SDK PutMetricAlarm input and the aws-cdk-lib CfnAlarm L1 (a newer CFn-schema-only property ahead of SDK/CDK support); no wire path to forward it and no CDK app can emit it."],
|
|
16631
16820
|
["EvaluationInterval", "Absent from both the SDK PutMetricAlarm input and the aws-cdk-lib CfnAlarm L1 (a newer CFn-schema-only property ahead of SDK/CDK support); no wire path to forward it and no CDK app can emit it."],
|
|
16632
|
-
["EvaluationWindow", "not yet implemented by cdkd"]
|
|
16821
|
+
["EvaluationWindow", "not yet implemented by cdkd"],
|
|
16822
|
+
["WarmUpConfiguration", "not yet implemented by cdkd"]
|
|
16633
16823
|
]),
|
|
16634
16824
|
createOnlyDrops: /* @__PURE__ */ new Set()
|
|
16635
16825
|
}],
|
|
@@ -17457,7 +17647,7 @@ const PROPERTY_COVERAGE_BY_TYPE = /* @__PURE__ */ new Map([
|
|
|
17457
17647
|
}],
|
|
17458
17648
|
["AWS::Glue::Connection", {
|
|
17459
17649
|
handled: /* @__PURE__ */ new Set(["CatalogId", "ConnectionInput"]),
|
|
17460
|
-
silentDrop: /* @__PURE__ */ new Map(),
|
|
17650
|
+
silentDrop: /* @__PURE__ */ new Map([["Tags", "not yet implemented by cdkd"]]),
|
|
17461
17651
|
createOnlyDrops: /* @__PURE__ */ new Set()
|
|
17462
17652
|
}],
|
|
17463
17653
|
["AWS::Glue::Crawler", {
|
|
@@ -18021,7 +18211,6 @@ const PROPERTY_COVERAGE_BY_TYPE = /* @__PURE__ */ new Map([
|
|
|
18021
18211
|
["UseLatestRestorableTime", "not yet implemented by cdkd"]
|
|
18022
18212
|
]),
|
|
18023
18213
|
createOnlyDrops: /* @__PURE__ */ new Set([
|
|
18024
|
-
"AvailabilityZones",
|
|
18025
18214
|
"ClusterScalabilityType",
|
|
18026
18215
|
"DBSystemId",
|
|
18027
18216
|
"EngineMode",
|
|
@@ -19370,9 +19559,7 @@ var DiffCalculator = class DiffCalculator {
|
|
|
19370
19559
|
case "DELETE":
|
|
19371
19560
|
summary.delete++;
|
|
19372
19561
|
break;
|
|
19373
|
-
case "NO_CHANGE":
|
|
19374
|
-
summary.noChange++;
|
|
19375
|
-
break;
|
|
19562
|
+
case "NO_CHANGE": summary.noChange++;
|
|
19376
19563
|
}
|
|
19377
19564
|
return summary;
|
|
19378
19565
|
}
|
|
@@ -21122,7 +21309,8 @@ var WAFv2WebACLProvider = class {
|
|
|
21122
21309
|
this.logger.debug(`Successfully deleted WAFv2 WebACL ${logicalId}`);
|
|
21123
21310
|
} catch (error) {
|
|
21124
21311
|
if (error instanceof WAFNonexistentItemException) {
|
|
21125
|
-
|
|
21312
|
+
const clientRegion = await this.getClient().config.region();
|
|
21313
|
+
assertRegionMatch(clientRegion, context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
21126
21314
|
this.logger.debug(`WAFv2 WebACL ${physicalId} does not exist, skipping deletion`);
|
|
21127
21315
|
return;
|
|
21128
21316
|
}
|
|
@@ -21224,7 +21412,8 @@ var WAFv2WebACLProvider = class {
|
|
|
21224
21412
|
result["TokenDomains"] = webACL.TokenDomains ? [...webACL.TokenDomains] : [];
|
|
21225
21413
|
if (webACL.AssociationConfig) result["AssociationConfig"] = webACL.AssociationConfig;
|
|
21226
21414
|
try {
|
|
21227
|
-
|
|
21415
|
+
const tagsResp = await this.getClient().send(new ListTagsForResourceCommand$11({ ResourceARN: physicalId }));
|
|
21416
|
+
result["Tags"] = normalizeAwsTagsToCfn(tagsResp.TagInfoForResource?.TagList);
|
|
21228
21417
|
} catch (err) {
|
|
21229
21418
|
this.logger.debug(`WAFv2 ListTagsForResource(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21230
21419
|
}
|
|
@@ -22929,14 +23118,15 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
22929
23118
|
const resolved = {};
|
|
22930
23119
|
for (const [key, val] of Object.entries(obj)) {
|
|
22931
23120
|
const resolvedVal = await this.resolveValue(val, context);
|
|
22932
|
-
if (resolvedVal !== AWS_NO_VALUE)
|
|
22933
|
-
|
|
22934
|
-
|
|
22935
|
-
|
|
22936
|
-
|
|
22937
|
-
|
|
22938
|
-
|
|
22939
|
-
|
|
23121
|
+
if (resolvedVal !== AWS_NO_VALUE) {
|
|
23122
|
+
if (key === "__proto__") Object.defineProperty(resolved, key, {
|
|
23123
|
+
value: resolvedVal,
|
|
23124
|
+
enumerable: true,
|
|
23125
|
+
writable: true,
|
|
23126
|
+
configurable: true
|
|
23127
|
+
});
|
|
23128
|
+
else resolved[key] = resolvedVal;
|
|
23129
|
+
} else this.logger.debug(`Property ${key} resolved to AWS::NoValue, omitting from object`);
|
|
22940
23130
|
}
|
|
22941
23131
|
return resolved;
|
|
22942
23132
|
}
|
|
@@ -23516,6 +23706,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
23516
23706
|
case "ApiId": return physicalId;
|
|
23517
23707
|
default: return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);
|
|
23518
23708
|
}
|
|
23709
|
+
if (resourceType === "AWS::ApiGatewayV2::Api") switch (attributeName) {
|
|
23710
|
+
case "ExecuteApiArn": return `arn:${partition}:execute-api:${region}:${accountId}:${physicalId}`;
|
|
23711
|
+
case "ApiId": return physicalId;
|
|
23712
|
+
default: return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);
|
|
23713
|
+
}
|
|
23519
23714
|
if (resourceType === "AWS::ServiceDiscovery::PrivateDnsNamespace" || resourceType === "AWS::ServiceDiscovery::HttpNamespace" || resourceType === "AWS::ServiceDiscovery::PublicDnsNamespace") switch (attributeName) {
|
|
23520
23715
|
case "Arn": return `arn:${partition}:servicediscovery:${region}:${accountId}:namespace/${physicalId}`;
|
|
23521
23716
|
case "Id": return physicalId;
|
|
@@ -23649,9 +23844,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
23649
23844
|
case "PublicDnsName":
|
|
23650
23845
|
value = instance?.PublicDnsName;
|
|
23651
23846
|
break;
|
|
23652
|
-
case "AvailabilityZone":
|
|
23653
|
-
value = instance?.Placement?.AvailabilityZone;
|
|
23654
|
-
break;
|
|
23847
|
+
case "AvailabilityZone": value = instance?.Placement?.AvailabilityZone;
|
|
23655
23848
|
}
|
|
23656
23849
|
if (value !== void 0 && value !== null && value !== "") {
|
|
23657
23850
|
cachedEc2InstanceAttributes[cacheKey] = value;
|
|
@@ -24775,7 +24968,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
24775
24968
|
const credentials = await assumeRoleForCrossAccountStateRead(roleArn);
|
|
24776
24969
|
const { bucket, region: bucketRegion } = await resolveCrossAccountStateBucket(parsed.accountId, credentials);
|
|
24777
24970
|
const prefix = context.stateBackend?.prefix ?? "cdkd";
|
|
24778
|
-
|
|
24971
|
+
const s3 = new S3Client({
|
|
24779
24972
|
...awsClientDefaults(),
|
|
24780
24973
|
region: bucketRegion,
|
|
24781
24974
|
credentials: {
|
|
@@ -24789,7 +24982,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
24789
24982
|
warn: () => {},
|
|
24790
24983
|
error: () => {}
|
|
24791
24984
|
}
|
|
24792
|
-
})
|
|
24985
|
+
});
|
|
24986
|
+
return new S3StateBackend(s3, {
|
|
24793
24987
|
bucket,
|
|
24794
24988
|
prefix
|
|
24795
24989
|
}, {
|
|
@@ -25984,7 +26178,7 @@ function unsupportedTypeIssueUrl(resourceType) {
|
|
|
25984
26178
|
|
|
25985
26179
|
//#endregion
|
|
25986
26180
|
//#region src/provisioning/slow-cc-operation-timeouts.ts
|
|
25987
|
-
const MINUTE_MS =
|
|
26181
|
+
const MINUTE_MS = 6e4;
|
|
25988
26182
|
/**
|
|
25989
26183
|
* 60 min covers the worst-case observed for each type with headroom over the
|
|
25990
26184
|
* 15-30 min typical range. Keyed by CloudFormation type name. RDS / ElastiCache
|
|
@@ -26307,7 +26501,7 @@ var CloudControlProvider = class {
|
|
|
26307
26501
|
cloudControlClient;
|
|
26308
26502
|
logger = getLogger().child("CloudControlProvider");
|
|
26309
26503
|
patchGenerator = new JsonPatchGenerator();
|
|
26310
|
-
MAX_WAIT_TIME_MS =
|
|
26504
|
+
MAX_WAIT_TIME_MS = 9e5;
|
|
26311
26505
|
INITIAL_POLL_INTERVAL_MS = 1e3;
|
|
26312
26506
|
MAX_POLL_INTERVAL_MS = 1e4;
|
|
26313
26507
|
constructor() {
|
|
@@ -26466,8 +26660,9 @@ var CloudControlProvider = class {
|
|
|
26466
26660
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
26467
26661
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
26468
26662
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
26469
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
26470
|
-
|
|
26663
|
+
const { ASGProvider } = await import("./asg-provider-CQrHElrl.js").then((n) => n.n);
|
|
26664
|
+
const asgProvider = new ASGProvider();
|
|
26665
|
+
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
26471
26666
|
}
|
|
26472
26667
|
const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
|
|
26473
26668
|
if (isProtectedEc2Instance) await disableInstanceApiTermination(getAwsClients().ec2, physicalId, this.logger);
|
|
@@ -27119,16 +27314,13 @@ var CloudControlProvider = class {
|
|
|
27119
27314
|
}
|
|
27120
27315
|
}
|
|
27121
27316
|
break;
|
|
27122
|
-
case "AWS::ResourceGroups::Group":
|
|
27123
|
-
|
|
27124
|
-
|
|
27125
|
-
if (model)
|
|
27126
|
-
|
|
27127
|
-
this.logger.debug(`Enriched ResourceGroups Group ${physicalId} with Arn from CC GetResource`);
|
|
27128
|
-
}
|
|
27317
|
+
case "AWS::ResourceGroups::Group": if (!enriched["Arn"]) {
|
|
27318
|
+
const model = await this.readCcResourceModel(resourceType, physicalId);
|
|
27319
|
+
if (model) {
|
|
27320
|
+
if (typeof model["Arn"] === "string") enriched["Arn"] = model["Arn"];
|
|
27321
|
+
this.logger.debug(`Enriched ResourceGroups Group ${physicalId} with Arn from CC GetResource`);
|
|
27129
27322
|
}
|
|
27130
|
-
|
|
27131
|
-
default: break;
|
|
27323
|
+
}
|
|
27132
27324
|
}
|
|
27133
27325
|
return enriched;
|
|
27134
27326
|
}
|
|
@@ -30321,7 +30513,8 @@ var IAMRoleProvider = class {
|
|
|
30321
30513
|
await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
|
|
30322
30514
|
} catch (error) {
|
|
30323
30515
|
if (error instanceof NoSuchEntityException) {
|
|
30324
|
-
|
|
30516
|
+
const clientRegion = await this.iamClient.config.region();
|
|
30517
|
+
assertRegionMatch(clientRegion, context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
30325
30518
|
this.logger.debug(`Role ${physicalId} does not exist, skipping deletion`);
|
|
30326
30519
|
return;
|
|
30327
30520
|
}
|
|
@@ -31065,6 +31258,67 @@ function secretBearingExportNameWarning(outputKey, exportName, exposure) {
|
|
|
31065
31258
|
return `Output ${stripControlChars(outputKey)} has an Export.Name that resolves to a value containing a secret ${shown}— skipping the export alias. An export name becomes a key in state.json and in the exports index, and redaction rewrites VALUES only, so publishing it would persist the secret in plaintext. Use a non-secret Export.Name.`;
|
|
31066
31259
|
}
|
|
31067
31260
|
/**
|
|
31261
|
+
* Test `key` for recorded secret plaintext and return how it may be shown.
|
|
31262
|
+
*
|
|
31263
|
+
* Reuses {@link stateKeySecretExposure} and the same `maskEveryOccurrence` the
|
|
31264
|
+
* warnings below use, rather than restating either: two spellings of "is this
|
|
31265
|
+
* key safe to print" would disagree on the boundary cases those two encode
|
|
31266
|
+
* (the whole-key match for a sub-floor needle, longest-needle-first masking).
|
|
31267
|
+
*
|
|
31268
|
+
* SANITISED WITH BOTH helpers, because neither is a superset of the other —
|
|
31269
|
+
* measured, after a first cut swapped one for the other and silently traded
|
|
31270
|
+
* one class of character for another (issue #2667 review):
|
|
31271
|
+
*
|
|
31272
|
+
* | input | `stripControlChars` | `displaySafe` |
|
|
31273
|
+
* | -------- | ------------------- | ------------- |
|
|
31274
|
+
* | `U+200E` | removed | KEPT |
|
|
31275
|
+
* | `U+200F` | removed | KEPT |
|
|
31276
|
+
* | `U+2028` | KEPT | replaced |
|
|
31277
|
+
* | `U+2029` | KEPT | replaced |
|
|
31278
|
+
*
|
|
31279
|
+
* `U+2028` / `U+2029` matter because this text is PERSISTED and re-rendered by
|
|
31280
|
+
* JSON and web log viewers that treat both as line terminators — the CI-log
|
|
31281
|
+
* surface this masking exists to protect, where an `Fn::Sub`-built export name
|
|
31282
|
+
* carrying one could forge a log line (`display-safe.ts` states that
|
|
31283
|
+
* rationale). `U+200E` / `U+200F` are the bidi MARKS, named as residuals in
|
|
31284
|
+
* that same file; they reorder rendered text without terminating a line. On a
|
|
31285
|
+
* path whose subject is a possibly-secret-bearing name in an operator's log,
|
|
31286
|
+
* neither loss is worth taking, and composing costs nothing.
|
|
31287
|
+
*
|
|
31288
|
+
* ORDER IS LOAD-BEARING, and for the OVERLAP set — not for the marks, which
|
|
31289
|
+
* an earlier revision of this comment named and measurement contradicted.
|
|
31290
|
+
* `displaySafe` never touches `U+200E` / `U+200F`, so those give identical
|
|
31291
|
+
* output either way. Where the two classes OVERLAP — `U+0000`-`U+001F`,
|
|
31292
|
+
* `U+007F`-`U+009F`, `U+202A`-`U+202E`, `U+2066`-`U+2069` — `stripControlChars`
|
|
31293
|
+
* DELETES while `displaySafe` replaces with a space, so strip-then-display
|
|
31294
|
+
* yields `"ab"` and display-then-strip yields `"a b"`. Stripping first keeps a
|
|
31295
|
+
* name carrying them from being padded out.
|
|
31296
|
+
*
|
|
31297
|
+
* `displaySafe` also `.trim()`s, which `stripControlChars` alone did not: a
|
|
31298
|
+
* display-shape change for a key with leading or trailing whitespace. Stated
|
|
31299
|
+
* because it is a real difference, not hidden.
|
|
31300
|
+
*
|
|
31301
|
+
* The sibling warnings in this file still use `stripControlChars` ALONE and
|
|
31302
|
+
* carry the `U+2028` half of the gap; widening that helper, or converting
|
|
31303
|
+
* them, changes call sites this issue does not touch, so it is filed as issue
|
|
31304
|
+
* [#2874](https://github.com/go-to-k/cdkd/issues/2874) rather than done here.
|
|
31305
|
+
*/
|
|
31306
|
+
function secretSafeKeyDisplay(key, secrets) {
|
|
31307
|
+
const sanitise = (text) => displaySafe(stripControlChars(text));
|
|
31308
|
+
const shown = sanitise(key);
|
|
31309
|
+
const exposure = stateKeySecretExposure(shown, secrets) ?? stateKeySecretExposure(key, secrets);
|
|
31310
|
+
if (!exposure) return {
|
|
31311
|
+
kind: "safe",
|
|
31312
|
+
text: shown
|
|
31313
|
+
};
|
|
31314
|
+
const masked = sanitise(maskEveryOccurrence(shown, exposure));
|
|
31315
|
+
if (masked === shown) return { kind: "withheld" };
|
|
31316
|
+
return {
|
|
31317
|
+
kind: "masked",
|
|
31318
|
+
text: masked
|
|
31319
|
+
};
|
|
31320
|
+
}
|
|
31321
|
+
/**
|
|
31068
31322
|
* Warning for a state KEY that already holds secret plaintext — the residue an
|
|
31069
31323
|
* EARLIER binary left when it published an export name that resolved to one.
|
|
31070
31324
|
*
|
|
@@ -32121,9 +32375,9 @@ const EBS_FINAL_SNAPSHOT_TAG_KEY = "cdkd:final-snapshot-of";
|
|
|
32121
32375
|
/** Test seam (matches the `describe-type.ts` / macro-expander pattern). */
|
|
32122
32376
|
const finalSnapshotDelays = { sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) };
|
|
32123
32377
|
const PRE_DELETE_SNAPSHOT_POLL_INTERVAL_MS = 5e3;
|
|
32124
|
-
const PRE_DELETE_SNAPSHOT_TIMEOUT_MS =
|
|
32378
|
+
const PRE_DELETE_SNAPSHOT_TIMEOUT_MS = 36e5;
|
|
32125
32379
|
/** Post-snapshot settle budget for a Redshift cluster before its delete. */
|
|
32126
|
-
const REDSHIFT_CLUSTER_SETTLE_TIMEOUT_MS =
|
|
32380
|
+
const REDSHIFT_CLUSTER_SETTLE_TIMEOUT_MS = 12e5;
|
|
32127
32381
|
function errMsg(error) {
|
|
32128
32382
|
return error instanceof Error ? error.message : String(error);
|
|
32129
32383
|
}
|
|
@@ -32507,6 +32761,397 @@ async function createReplicationGroupFinalSnapshot(client, replicationGroupId, l
|
|
|
32507
32761
|
});
|
|
32508
32762
|
}
|
|
32509
32763
|
|
|
32764
|
+
//#endregion
|
|
32765
|
+
//#region src/analyzer/skipped-outputs.ts
|
|
32766
|
+
/**
|
|
32767
|
+
* The record `cdkd deploy` leaves behind for an Output it could NOT resolve
|
|
32768
|
+
* and SKIPPED, and the digest `cdkd diff` compares it against (issue
|
|
32769
|
+
* [#2740](https://github.com/go-to-k/cdkd/issues/2740)).
|
|
32770
|
+
*
|
|
32771
|
+
* The deploy side (`DeployEngine.handleOutputResolutionFailure`, default arm)
|
|
32772
|
+
* warns, stores `undefined` for the key and moves on, so a re-resolved
|
|
32773
|
+
* `state.outputs` lacks it (the no-change path keeps the previous bag whole
|
|
32774
|
+
* when any output fails, so a key that resolved on an EARLIER deploy can keep
|
|
32775
|
+
* its value beside a record — the reader checks absence first and ignores the
|
|
32776
|
+
* record then). The diff side resolves outputs with `skipDynamicReferences`, so a
|
|
32777
|
+
* failure that happens INSIDE a secret lookup — a JSON key the secret does not
|
|
32778
|
+
* hold, a reference assembled from another secret's value — does not reproduce
|
|
32779
|
+
* there: the value assembles into its token, the key is absent from state, and
|
|
32780
|
+
* `computeOutputsDiff` pushed an `ADD` the deploy would never perform, on every
|
|
32781
|
+
* run of an unchanged stack. The diff cannot tell from assembly alone that the
|
|
32782
|
+
* deploy would fail (a composed string carrying a secret reference is a
|
|
32783
|
+
* legitimate output), so it learns what the deploy learned instead:
|
|
32784
|
+
* `StackState.skippedOutputs` maps each skipped key to a digest of the
|
|
32785
|
+
* template inputs its resolution read, and the diff trusts the record only
|
|
32786
|
+
* while that digest is unchanged.
|
|
32787
|
+
*
|
|
32788
|
+
* ## What the digest covers, and why
|
|
32789
|
+
*
|
|
32790
|
+
* `skippedOutputDigest` hashes the output's OWN entry (`Value`, `Export`,
|
|
32791
|
+
* `Condition`, whatever else it declares) together with every top-level
|
|
32792
|
+
* template section EXCEPT `Resources` and `Outputs`:
|
|
32793
|
+
*
|
|
32794
|
+
* - The entry itself: repairing the `Value` expression is the obvious repair.
|
|
32795
|
+
* The `Export.Name` is in it too, because the alias pass routes an
|
|
32796
|
+
* `Export.Name` failure through the same handler, which blanks the output's
|
|
32797
|
+
* own key even when its value resolved — so a repaired or removed export
|
|
32798
|
+
* name must invalidate the record as well.
|
|
32799
|
+
* - `Parameters` (a `Ref`-built reference repaired through a parameter
|
|
32800
|
+
* default), `Conditions` (an `Fn::If` whose branch flips), `Mappings` (an
|
|
32801
|
+
* `Fn::FindInMap`-built key), and the remaining sections for the same
|
|
32802
|
+
* reason, spelled as one rule rather than a list that drifts: everything an
|
|
32803
|
+
* output's resolution can read besides resources. One VALUE inside them is
|
|
32804
|
+
* excluded — the `Default` of a `NoEcho: true` parameter, hashed as a
|
|
32805
|
+
* constant; see `withNoEchoDefaultsMasked` for why. That is the POSITION,
|
|
32806
|
+
* not the parameter: its `AllowedValues`, and any literal an author puts in
|
|
32807
|
+
* `Conditions`, `Rules` or `Metadata`, are hashed as before.
|
|
32808
|
+
* - NOT `Resources`: hashing the section would invalidate the record on every
|
|
32809
|
+
* unrelated resource edit. The output CAN be repaired from that side —
|
|
32810
|
+
* `collectSkippedOutputs` records both skip arms, and the quiet one is an
|
|
32811
|
+
* `Fn::GetAtt` whose attribute `constructAttribute` could not build — so a
|
|
32812
|
+
* digest alone would keep binding while the next deploy publishes the row
|
|
32813
|
+
* and its `Export.Name`, the phantom's inverse. {@link
|
|
32814
|
+
* bindingSkippedOutputs} closes that with the CHANGE MAP rather than the
|
|
32815
|
+
* digest: a key whose entry references a logical id with a pending resource
|
|
32816
|
+
* change does not bind. The diff has that map (it ran the resource diff
|
|
32817
|
+
* first); the deploy does not need it, because a stack with any resource
|
|
32818
|
+
* change takes the path that re-resolves every output and rewrites the
|
|
32819
|
+
* record. REFERENCE, not repair: whether an edit could actually make the
|
|
32820
|
+
* output resolvable is undecidable from a template, so an unrelated edit to
|
|
32821
|
+
* a referenced resource un-binds too and the key can preview as an `ADD`
|
|
32822
|
+
* again. The cost is bounded — a run with a changed resource is already
|
|
32823
|
+
* reporting that row, so `--fail` exits 1 regardless — while the case it
|
|
32824
|
+
* buys depends on whether the diff can resolve the output at all. For an
|
|
32825
|
+
* output reading an attribute that does not exist yet — the SECOND skip arm
|
|
32826
|
+
* this record covers, not the secret lookup issue #2740 was filed on — it is
|
|
32827
|
+
* the VERDICT and not the row: unresolvable in
|
|
32828
|
+
* both readings, so bound the diff reports the outputs settled while the
|
|
32829
|
+
* next deploy is about to publish that key and its `Export.Name`. For an
|
|
32830
|
+
* output the diff CAN resolve, whose reference is to a name rather than to
|
|
32831
|
+
* a pending attribute (an `Fn::Sub` over an SSM parameter's name, say), the
|
|
32832
|
+
* row IS the difference and reappears as an `ADD` the moment the record
|
|
32833
|
+
* stops binding.
|
|
32834
|
+
* - NOT the sibling `Outputs`: a CloudFormation output cannot reference
|
|
32835
|
+
* another output, so a sibling change cannot repair this one, and excluding
|
|
32836
|
+
* them keeps an unrelated output edit from producing a one-run phantom.
|
|
32837
|
+
*
|
|
32838
|
+
* What the digest CANNOT see is a repair outside the template — the secret
|
|
32839
|
+
* gained the JSON key, the SSM parameter was created, a parameter VALUE
|
|
32840
|
+
* handed to the engine rather than declared changed (a nested stack's inputs
|
|
32841
|
+
* from its parent, `DeployEngineOptions.parameters`; `Parameters`
|
|
32842
|
+
* DECLARATIONS are hashed, supplied values are not), or **cdkd itself was
|
|
32843
|
+
* upgraded**: the quiet skip arm is `constructAttribute` returning nothing,
|
|
32844
|
+
* so a provider that gains that attribute repairs the output while neither
|
|
32845
|
+
* the template nor any resource moves.
|
|
32846
|
+
*
|
|
32847
|
+
* They are blind spots for three different reasons, and only the first is
|
|
32848
|
+
* about knowledge. The secret's key set and the SSM parameter's existence
|
|
32849
|
+
* need the external lookup the diff deliberately refuses to make. A supplied
|
|
32850
|
+
* parameter VALUE is not one of those — a nested stack's inputs are resolved
|
|
32851
|
+
* and forwarded to the child diff (`resolveChildStackParameters`) — it is
|
|
32852
|
+
* simply outside what the digest hashes, because hashing values would make
|
|
32853
|
+
* the record depend on a caller's arguments rather than on the template. And
|
|
32854
|
+
* the writing binary's version is knowable and merely unstored: keeping it
|
|
32855
|
+
* beside the digest and un-binding on mismatch would close that one. All four
|
|
32856
|
+
* end the same way: the record keeps binding until the next deploy re-resolves
|
|
32857
|
+
* the output, and that deploy publishes the key — unless a SIBLING output is
|
|
32858
|
+
* still unresolved on a run with no resource change, where the engine keeps
|
|
32859
|
+
* the previous outputs bag wholesale and the key stays unpublished for a
|
|
32860
|
+
* further run (go-to-k/cdkd#2771, pre-existing and not introduced here).
|
|
32861
|
+
* Documented as the accepted limitation in `docs/cli-diff.md`; it is narrower
|
|
32862
|
+
* than the pre-#2740 behaviour, where the diff was wrong on EVERY run.
|
|
32863
|
+
*
|
|
32864
|
+
* A repair on the RESOURCE side is NOT on that list either, but the reason is
|
|
32865
|
+
* narrower than it first looks. The change map above closes it only when the
|
|
32866
|
+
* repair arrives WITH a template resource change, which is what makes the
|
|
32867
|
+
* resource show on the diff at all. An OUT-OF-BAND state write does not:
|
|
32868
|
+
* every writer that rebuilds state outside a deploy DROPS the record rather
|
|
32869
|
+
* than carry it (one enumerated exception, the partial-destroy snapshot, is
|
|
32870
|
+
* explained with the field) — `cdkd import` refreshes `attributes` for the
|
|
32871
|
+
* resources it imports, `cdkd drift --accept` rewrites the properties an
|
|
32872
|
+
* attribute may be built from, `cdkd rollback`'s replacement arm rebuilds a
|
|
32873
|
+
* record with the attributes a FRESH create returned, and the rest are listed
|
|
32874
|
+
* with the field.
|
|
32875
|
+
* Dropping returns those keys to pre-#2740 behaviour until the next deploy
|
|
32876
|
+
* recomputes the record — a row where the diff can resolve them, the ordinary
|
|
32877
|
+
* whole-section suppression where it cannot — which is what keeps a
|
|
32878
|
+
* resource-side repair out of the list above.
|
|
32879
|
+
*
|
|
32880
|
+
* The rule is flat and it is meant to be: EVERY writer that rebuilds state
|
|
32881
|
+
* outside a deploy drops the record, the partial-destroy snapshot being the
|
|
32882
|
+
* one enumerated exception — the full list lives with the field, in
|
|
32883
|
+
* `StackState.skippedOutputs`. Three per-writer arguments for carrying it were
|
|
32884
|
+
* written during review, which is the reason for the flat rule rather than an
|
|
32885
|
+
* aside: two were shown wrong (substituting a value repairs an output whose
|
|
32886
|
+
* enclosing intrinsic was choking on what was there; scrubbing a plaintext
|
|
32887
|
+
* back to its expression rewrites a string outputs read verbatim) and the
|
|
32888
|
+
* third — deleting a property EXPOSING an attribute of the same name — could
|
|
32889
|
+
* not be settled either way, which argues for flatness at least as strongly.
|
|
32890
|
+
*
|
|
32891
|
+
* ## Where the digest must be taken from
|
|
32892
|
+
*
|
|
32893
|
+
* The template AS HANDED to the engine / to `computeStackDiff` — before
|
|
32894
|
+
* parameter binding, condition evaluation or any output resolution. The
|
|
32895
|
+
* INVARIANT, and the reason both callers `structuredClone` rather than pass
|
|
32896
|
+
* their live object: **no resolution may be visible to the digest, on either
|
|
32897
|
+
* side, and both sides must take it at the same point of their own flow.**
|
|
32898
|
+
* Two independent reasons, neither of which depends on any particular
|
|
32899
|
+
* resolver doing the writing: a digest that could see a resolved value would
|
|
32900
|
+
* (1) differ between the two sides, which computes a record the diff can
|
|
32901
|
+
* never re-derive, and (2) fingerprint a secret into `state.json`, since a
|
|
32902
|
+
* resolved value can be a decrypted one.
|
|
32903
|
+
*
|
|
32904
|
+
* The clone is therefore NOT dead weight even while no resolver mutates its
|
|
32905
|
+
* input. It used to be load-bearing against a live rewrite — `resolveSub`
|
|
32906
|
+
* substituted into the caller's two-argument `Fn::Sub` variable map until
|
|
32907
|
+
* go-to-k/cdkd#2764 gave it a fresh object — and the protection was never
|
|
32908
|
+
* derived from that call site: it is derived from the invariant above, which
|
|
32909
|
+
* a future in-place optimisation anywhere in either flow would silently
|
|
32910
|
+
* violate. `tests/unit/cli/diff-recursive-skipped-outputs.test.ts` pins the
|
|
32911
|
+
* current no-mutation contract from the other direction (the template handed
|
|
32912
|
+
* to `computeStackDiff` comes back byte-identical), so reintroducing a
|
|
32913
|
+
* rewrite reds a test rather than moving a digest;
|
|
32914
|
+
* `tests/unit/deployment/deploy-engine-skipped-outputs.test.ts` INJECTS a
|
|
32915
|
+
* rewrite through its resolver mock and requires the recorded digest to equal
|
|
32916
|
+
* a fresh parse's, which pins the ordering itself.
|
|
32917
|
+
*
|
|
32918
|
+
* Given that contract the hash covers template text only. It is over
|
|
32919
|
+
* canonical JSON (object keys sorted at every level), so a template
|
|
32920
|
+
* re-serialised with keys in another order digests identically.
|
|
32921
|
+
*
|
|
32922
|
+
* ## What the diff does with a binding record
|
|
32923
|
+
*
|
|
32924
|
+
* It previews the key as ABSENT — no row — because that is exactly what the
|
|
32925
|
+
* deploy will leave in state. It does NOT flag the section as failed: a
|
|
32926
|
+
* sibling output that genuinely changed still renders, and `--fail` still
|
|
32927
|
+
* exits 1 for it. A record whose digest no longer matches is simply ignored:
|
|
32928
|
+
* the output is previewed under the ordinary rules again (usually an `ADD`;
|
|
32929
|
+
* an intrinsic `Export.Name` the diff still cannot resolve keeps omitting the
|
|
32930
|
+
* section, as before), the next deploy re-decides it —
|
|
32931
|
+
* publishing it if the repair took, or recording it again under the new
|
|
32932
|
+
* digest — and the diff follows that decision.
|
|
32933
|
+
*/
|
|
32934
|
+
/**
|
|
32935
|
+
* JSON with object keys sorted at every level. Arrays keep their order (an
|
|
32936
|
+
* `Fn::Join` list is positional). `undefined` members are dropped, as
|
|
32937
|
+
* `JSON.stringify` drops them, so a parsed template and its in-memory twin
|
|
32938
|
+
* digest identically.
|
|
32939
|
+
*/
|
|
32940
|
+
function canonicalJson(value) {
|
|
32941
|
+
return JSON.stringify(value, (_key, v) => {
|
|
32942
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) {
|
|
32943
|
+
const sorted = Object.create(null);
|
|
32944
|
+
for (const k of Object.keys(v).sort()) sorted[k] = v[k];
|
|
32945
|
+
return sorted;
|
|
32946
|
+
}
|
|
32947
|
+
return v;
|
|
32948
|
+
});
|
|
32949
|
+
}
|
|
32950
|
+
/** The constant a `NoEcho` `Default` is hashed as. Its text is irrelevant; that
|
|
32951
|
+
* it is CONSTANT is the point. */
|
|
32952
|
+
const NO_ECHO_DEFAULT_MASK = "<cdkd:noecho-default>";
|
|
32953
|
+
/**
|
|
32954
|
+
* `Parameters` with every `NoEcho: true` parameter's `Default` replaced by a
|
|
32955
|
+
* constant, for hashing only.
|
|
32956
|
+
*
|
|
32957
|
+
* The pre-resolution snapshot already keeps RESOLVED secret values out of the
|
|
32958
|
+
* digest. An authored `Default` under `NoEcho: true` is not one of those — it
|
|
32959
|
+
* is template text, and it went into the hash. With the rest of the
|
|
32960
|
+
* non-`Resources` sections known, a reader of `state.json` holds a confirm
|
|
32961
|
+
* ORACLE for a low-entropy default: guess it, recompute, compare.
|
|
32962
|
+
*
|
|
32963
|
+
* This does NOT claim the value is otherwise absent from state — a parameter a
|
|
32964
|
+
* resource reads can persist its RESOLVED default in that resource's
|
|
32965
|
+
* properties, and parameter `NoEcho` is outside the state-redaction model
|
|
32966
|
+
* either way. What it claims is narrower and still worth having: this record
|
|
32967
|
+
* must not ADD an oracle of its own for the case where the value is not
|
|
32968
|
+
* already there.
|
|
32969
|
+
*
|
|
32970
|
+
* The cost, stated rather than hidden: changing ONLY such a default no longer
|
|
32971
|
+
* moves the digest, so the record keeps binding through that edit until the
|
|
32972
|
+
* next deploy re-resolves the output. That is the same bounded staleness the
|
|
32973
|
+
* documented blind spots carry.
|
|
32974
|
+
*
|
|
32975
|
+
* Everything else about the parameter is still hashed — its `Type`, its
|
|
32976
|
+
* `AllowedValues`, the `NoEcho` flag itself — so a parameter appearing,
|
|
32977
|
+
* disappearing or changing shape still un-binds. Unmatched parameter content
|
|
32978
|
+
* hashes exactly as before: the map is rebuilt, but the canonicaliser sorts
|
|
32979
|
+
* keys and ignores prototypes, so a template with no `NoEcho` default gets the
|
|
32980
|
+
* digest it always had.
|
|
32981
|
+
*/
|
|
32982
|
+
function withNoEchoDefaultsMasked(parameters) {
|
|
32983
|
+
if (parameters === null || typeof parameters !== "object" || Array.isArray(parameters)) return parameters;
|
|
32984
|
+
const masked = Object.create(null);
|
|
32985
|
+
for (const [name, decl] of Object.entries(parameters)) {
|
|
32986
|
+
const body = decl;
|
|
32987
|
+
masked[name] = typeof body === "object" && body !== null && body["NoEcho"] === true && "Default" in body ? {
|
|
32988
|
+
...body,
|
|
32989
|
+
Default: NO_ECHO_DEFAULT_MASK
|
|
32990
|
+
} : decl;
|
|
32991
|
+
}
|
|
32992
|
+
return masked;
|
|
32993
|
+
}
|
|
32994
|
+
/**
|
|
32995
|
+
* The digest recorded in `StackState.skippedOutputs[outputKey]` by the deploy
|
|
32996
|
+
* and recomputed by the diff — see the module doc for what it covers. Both
|
|
32997
|
+
* sides call THIS function, so the coverage is spelled once.
|
|
32998
|
+
*
|
|
32999
|
+
* `template` must be the pre-resolution snapshot the module doc describes.
|
|
33000
|
+
*/
|
|
33001
|
+
function skippedOutputDigest(template, outputKey) {
|
|
33002
|
+
const sections = Object.create(null);
|
|
33003
|
+
for (const [section, body] of Object.entries(template)) {
|
|
33004
|
+
if (section === "Resources" || section === "Outputs") continue;
|
|
33005
|
+
sections[section] = section === "Parameters" ? withNoEchoDefaultsMasked(body) : body;
|
|
33006
|
+
}
|
|
33007
|
+
const entry = template.Outputs?.[outputKey];
|
|
33008
|
+
return createHash("sha256").update(canonicalJson({
|
|
33009
|
+
output: entry,
|
|
33010
|
+
template: sections
|
|
33011
|
+
})).digest("hex");
|
|
33012
|
+
}
|
|
33013
|
+
/**
|
|
33014
|
+
* The record a deploy writes for the bag `resolveOutputs` produced: every key
|
|
33015
|
+
* whose value is `undefined`, mapped to its digest. Two arms write that value:
|
|
33016
|
+
* the default arm of `handleOutputResolutionFailure` (the resolver THREW —
|
|
33017
|
+
* warned, and refused under `--strict-getatt`), and a resolver that returned
|
|
33018
|
+
* `undefined` outright without throwing (an attribute it could construct
|
|
33019
|
+
* nothing for, `constructAttribute`'s empty arm — no warn, no strict refusal).
|
|
33020
|
+
* Both leave the key out of a re-resolved bag (the no-change path may keep a
|
|
33021
|
+
* previous value under it, see the module doc), and that path's
|
|
33022
|
+
* `resolutionFailed` treats both the same, so this record does too: the
|
|
33023
|
+
* question it answers is "will the next deploy leave this key absent?", and
|
|
33024
|
+
* the answer is yes either way. `undefined` (the field OMITTED) when nothing
|
|
33025
|
+
* was skipped, so a record that carries the field says at least one output
|
|
33026
|
+
* was skipped.
|
|
33027
|
+
*
|
|
33028
|
+
* `template` must be the pre-resolution snapshot (see the module doc).
|
|
33029
|
+
*/
|
|
33030
|
+
function collectSkippedOutputs(template, resolvedOutputs) {
|
|
33031
|
+
let record;
|
|
33032
|
+
for (const [outputKey, value] of Object.entries(resolvedOutputs)) {
|
|
33033
|
+
if (value !== void 0) continue;
|
|
33034
|
+
record ??= Object.create(null);
|
|
33035
|
+
record[outputKey] = skippedOutputDigest(template, outputKey);
|
|
33036
|
+
}
|
|
33037
|
+
return record;
|
|
33038
|
+
}
|
|
33039
|
+
/**
|
|
33040
|
+
* Every resource logical id an output entry could reference, from `Ref`,
|
|
33041
|
+
* either `Fn::GetAtt` spelling, and an `Fn::Sub` placeholder, walked to any
|
|
33042
|
+
* depth over the whole entry (`Value`, `Export.Name` and anything else it
|
|
33043
|
+
* declares).
|
|
33044
|
+
*
|
|
33045
|
+
* A deliberate SUPERSET: an `Fn::Sub` placeholder naming a template
|
|
33046
|
+
* PARAMETER, or a `Ref` to one, lands here too, since telling the two apart
|
|
33047
|
+
* needs the `Parameters` section and the answer only ever widens the set. The
|
|
33048
|
+
* one consumer intersects it with the ids that actually have a pending
|
|
33049
|
+
* RESOURCE change, so a name that is not a resource cannot match, and a
|
|
33050
|
+
* parameter deliberately named after a changed resource merely un-binds one
|
|
33051
|
+
* record — the direction that previews a row rather than hiding one. Pseudo
|
|
33052
|
+
* parameters (`AWS::…`) and the `${!Literal}` escape are dropped.
|
|
33053
|
+
*/
|
|
33054
|
+
function referencedLogicalIds(entry) {
|
|
33055
|
+
const ids = /* @__PURE__ */ new Set();
|
|
33056
|
+
const addName = (name) => {
|
|
33057
|
+
const logicalId = name.split(".")[0];
|
|
33058
|
+
if (logicalId !== void 0 && logicalId !== "" && !logicalId.startsWith("AWS::")) ids.add(logicalId);
|
|
33059
|
+
};
|
|
33060
|
+
const walk = (value) => {
|
|
33061
|
+
if (Array.isArray(value)) {
|
|
33062
|
+
for (const item of value) walk(item);
|
|
33063
|
+
return;
|
|
33064
|
+
}
|
|
33065
|
+
if (value === null || typeof value !== "object") return;
|
|
33066
|
+
const obj = value;
|
|
33067
|
+
if (typeof obj["Ref"] === "string") addName(obj["Ref"]);
|
|
33068
|
+
const getAtt = obj["Fn::GetAtt"];
|
|
33069
|
+
if (typeof getAtt === "string") addName(getAtt);
|
|
33070
|
+
else if (Array.isArray(getAtt) && typeof getAtt[0] === "string") addName(getAtt[0]);
|
|
33071
|
+
for (const [key, nested] of Object.entries(obj)) {
|
|
33072
|
+
if (key === "Fn::Sub") {
|
|
33073
|
+
walkSub(nested);
|
|
33074
|
+
continue;
|
|
33075
|
+
}
|
|
33076
|
+
walk(nested);
|
|
33077
|
+
}
|
|
33078
|
+
};
|
|
33079
|
+
/**
|
|
33080
|
+
* `Fn::Sub` in both spellings. Handled here rather than by the generic walk
|
|
33081
|
+
* because the two-argument form's second element is a VARIABLE MAP, not a
|
|
33082
|
+
* template fragment: its KEYS are variable names, so letting the generic
|
|
33083
|
+
* walk see `{ Ref: 'Unrelated' }` there would read a variable literally
|
|
33084
|
+
* named `Ref` as a `Ref` intrinsic. The map's VALUES are walked, since the
|
|
33085
|
+
* resolver resolves them.
|
|
33086
|
+
*/
|
|
33087
|
+
const walkSub = (sub) => {
|
|
33088
|
+
const isPair = Array.isArray(sub);
|
|
33089
|
+
const subTemplate = typeof sub === "string" ? sub : isPair ? sub[0] : void 0;
|
|
33090
|
+
const variableMap = isPair && sub[1] !== null && typeof sub[1] === "object" ? sub[1] : void 0;
|
|
33091
|
+
if (typeof subTemplate === "string") {
|
|
33092
|
+
const declaredVars = new Set(variableMap === void 0 ? [] : Object.keys(variableMap));
|
|
33093
|
+
for (const [, placeholder] of subTemplate.matchAll(/\$\{([^}]*)\}/g)) {
|
|
33094
|
+
if (placeholder === void 0 || placeholder.startsWith("!")) continue;
|
|
33095
|
+
if (declaredVars.has(placeholder)) continue;
|
|
33096
|
+
addName(placeholder);
|
|
33097
|
+
}
|
|
33098
|
+
}
|
|
33099
|
+
if (isPair) for (const [index, element] of sub.entries()) {
|
|
33100
|
+
if (index === 1) {
|
|
33101
|
+
if (variableMap !== void 0) for (const declared of Object.values(variableMap)) walk(declared);
|
|
33102
|
+
continue;
|
|
33103
|
+
}
|
|
33104
|
+
walk(element);
|
|
33105
|
+
}
|
|
33106
|
+
else walk(sub);
|
|
33107
|
+
};
|
|
33108
|
+
walk(entry);
|
|
33109
|
+
return ids;
|
|
33110
|
+
}
|
|
33111
|
+
/**
|
|
33112
|
+
* The keys of a `StackState.skippedOutputs` record that still BIND against
|
|
33113
|
+
* today's template: recorded digest equal to the digest of the same key over
|
|
33114
|
+
* `pristineTemplate`, which must be the pre-resolution snapshot the module doc
|
|
33115
|
+
* describes. A key the template no longer declares is skipped outright — the
|
|
33116
|
+
* record is inert for it, and the next deploy clears it. The diff passes the
|
|
33117
|
+
* result to `resolveTemplateOutputs`, which adds the one check only state can
|
|
33118
|
+
* answer — that the key is still absent from the stored bag.
|
|
33119
|
+
*
|
|
33120
|
+
* `changedLogicalIds` is the second half of the binding rule, and the one the
|
|
33121
|
+
* digest structurally cannot carry (see the module doc's `Resources` bullet):
|
|
33122
|
+
* the logical ids this run's resource diff reports as changing. An output
|
|
33123
|
+
* whose entry references one of them does NOT bind, because the deploy that
|
|
33124
|
+
* follows takes the changed-resources path, re-resolves every output, and may
|
|
33125
|
+
* publish the very key the record would have hidden. Omitted by a caller with
|
|
33126
|
+
* no resource diff in hand — every such caller today is a test asserting the
|
|
33127
|
+
* digest half alone.
|
|
33128
|
+
*/
|
|
33129
|
+
function bindingSkippedOutputs(pristineTemplate, record, changedLogicalIds) {
|
|
33130
|
+
const binding = /* @__PURE__ */ new Set();
|
|
33131
|
+
if (record === null || record === void 0) return binding;
|
|
33132
|
+
const declared = pristineTemplate.Outputs ?? {};
|
|
33133
|
+
for (const [outputKey, digest] of Object.entries(record)) {
|
|
33134
|
+
if (!Object.prototype.hasOwnProperty.call(declared, outputKey)) continue;
|
|
33135
|
+
const entry = declared[outputKey];
|
|
33136
|
+
if (digest !== skippedOutputDigest(pristineTemplate, outputKey)) continue;
|
|
33137
|
+
if (changedLogicalIds !== void 0 && [...referencedLogicalIds(entry)].some((id) => changedLogicalIds.has(id))) continue;
|
|
33138
|
+
binding.add(outputKey);
|
|
33139
|
+
}
|
|
33140
|
+
return binding;
|
|
33141
|
+
}
|
|
33142
|
+
/**
|
|
33143
|
+
* Whether two records — either possibly absent — describe the same skipped
|
|
33144
|
+
* set with the same digests. The no-change deploy path persists on a
|
|
33145
|
+
* difference here: it is the ONLY save trigger for this field on that path,
|
|
33146
|
+
* because a skipped output switches the outputs-changed trigger off.
|
|
33147
|
+
*/
|
|
33148
|
+
function skippedOutputsEqual(a, b) {
|
|
33149
|
+
const aKeys = a === null || a === void 0 ? [] : Object.keys(a);
|
|
33150
|
+
const bKeys = b === null || b === void 0 ? [] : Object.keys(b);
|
|
33151
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
33152
|
+
return aKeys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && a?.[k] === b?.[k]);
|
|
33153
|
+
}
|
|
33154
|
+
|
|
32510
33155
|
//#endregion
|
|
32511
33156
|
//#region src/analyzer/implicit-delete-deps.ts
|
|
32512
33157
|
/**
|
|
@@ -33432,7 +34077,8 @@ var ReplayResolvers = class {
|
|
|
33432
34077
|
* `state.json` already stores in the clear.
|
|
33433
34078
|
*/
|
|
33434
34079
|
function regionAmbiguousReplaySecretError(logicalId, propertyPath, secretName, foreignProducerRegions, consumerRegion) {
|
|
33435
|
-
|
|
34080
|
+
const where = propertyPath === "" ? "" : ` property '${propertyPath}'`;
|
|
34081
|
+
return new CdkdError(`Rollback of ${logicalId}${where} cannot re-resolve the secret reference '${secretName}': the reference carries no region of its own, and this stack read across a region boundary (producer region(s) on record: ${foreignProducerRegions.join(", ")}), so it may have been resolved in one of those rather than in '${consumerRegion}'. A secret of the same name in two regions is two independent values, so replaying this would write the WRONG secret to a live resource. Refusing instead. Resolve the reference in its own region and set the property directly (or spell it as a full ARN, which names its region and is resolved there), then re-run 'cdkd rollback'.`, "ROLLBACK_SECRET_REGION_AMBIGUOUS");
|
|
33436
34082
|
}
|
|
33437
34083
|
/**
|
|
33438
34084
|
* Re-resolve one LEAF string, sending each `{{resolve:...}}` reference in it to
|
|
@@ -33535,10 +34181,11 @@ async function resolveLeafByRegion(leaf, propertyPath, logicalId, execCtx, resol
|
|
|
33535
34181
|
*/
|
|
33536
34182
|
function redactRollbackRecord(record, secrets, journaledProps) {
|
|
33537
34183
|
if (secrets.size === 0) return record;
|
|
33538
|
-
|
|
34184
|
+
const positioned = journaledProps === void 0 ? record : {
|
|
33539
34185
|
...record,
|
|
33540
34186
|
properties: redactSecretsForState(record.properties, secrets, journaledProps, STATE_DERIVED_RULES)
|
|
33541
|
-
}
|
|
34187
|
+
};
|
|
34188
|
+
return scrubResourceRecord(positioned, secrets);
|
|
33542
34189
|
}
|
|
33543
34190
|
async function updateWithRollbackRetry(provider, args, logicalId, logger, isInterrupted, secrets) {
|
|
33544
34191
|
if (provider.disableOuterRetry) return await withCurrentResourceSecrets(secrets, () => provider.update(...args));
|
|
@@ -34759,13 +35406,13 @@ const EMPTY_SECRETS = /* @__PURE__ */ new Map();
|
|
|
34759
35406
|
* resource has been in flight for 5 minutes. Most CC API resources
|
|
34760
35407
|
* complete in under a minute; 5m is the agreed elbow.
|
|
34761
35408
|
*/
|
|
34762
|
-
const DEFAULT_RESOURCE_WARN_AFTER_MS =
|
|
35409
|
+
const DEFAULT_RESOURCE_WARN_AFTER_MS = 3e5;
|
|
34763
35410
|
/**
|
|
34764
35411
|
* Default per-resource hard timeout: abort after 30 minutes. Matches the
|
|
34765
35412
|
* design doc — Custom-Resource-heavy stacks should pass `--resource-timeout 1h`
|
|
34766
35413
|
* explicitly because the Custom Resource provider's polling cap is 1h.
|
|
34767
35414
|
*/
|
|
34768
|
-
const DEFAULT_RESOURCE_TIMEOUT_MS =
|
|
35415
|
+
const DEFAULT_RESOURCE_TIMEOUT_MS = 18e5;
|
|
34769
35416
|
var InterruptedError = class extends Error {
|
|
34770
35417
|
constructor(reason = "user") {
|
|
34771
35418
|
super(reason === "user" ? "Deployment interrupted by user (Ctrl+C)" : "Deployment aborted after another resource failed");
|
|
@@ -35021,8 +35668,17 @@ var DeployEngine = class {
|
|
|
35021
35668
|
* secret collapse onto whichever expression was recorded last, exactly as two
|
|
35022
35669
|
* resource properties did before #1904. Captured in `resolveOutputs` at the
|
|
35023
35670
|
* same point `outputSecrets` is accumulated. Reset per `deploy()`.
|
|
35671
|
+
*
|
|
35672
|
+
* Null-prototype for the same reason as the outputs bag `resolveOutputs`
|
|
35673
|
+
* builds two lines away: both are keyed by TEMPLATE-CONTROLLED output names,
|
|
35674
|
+
* so an output called `__proto__` would replace this bag's prototype and a
|
|
35675
|
+
* later lookup of a name it never stored would inherit from it. Only the
|
|
35676
|
+
* RESET in `deploy()` is pinned — reverting this initialiser alone survives
|
|
35677
|
+
* the suite, measured, because `deploy()` replaces the bag before anything
|
|
35678
|
+
* writes to it, so the initialiser matches the reset rather than standing as
|
|
35679
|
+
* a second guard.
|
|
35024
35680
|
*/
|
|
35025
|
-
outputsTemplateSource =
|
|
35681
|
+
outputsTemplateSource = Object.create(null);
|
|
35026
35682
|
/**
|
|
35027
35683
|
* The export aliases the last `resolveOutputs` pass WROTE into its bag
|
|
35028
35684
|
* (issue #2193) — exactly the keys `outputs[exportName] = value` landed on,
|
|
@@ -35035,6 +35691,18 @@ var DeployEngine = class {
|
|
|
35035
35691
|
*/
|
|
35036
35692
|
resolvedExportNames = [];
|
|
35037
35693
|
/**
|
|
35694
|
+
* The outputs the last `resolveOutputs` pass could NOT resolve and SKIPPED
|
|
35695
|
+
* (the resolver threw under the default arm of
|
|
35696
|
+
* `handleOutputResolutionFailure`, or returned `undefined` outright — see
|
|
35697
|
+
* `collectSkippedOutputs`), each mapped to the
|
|
35698
|
+
* digest `cdkd diff` compares against (issue #2740, `StackState.
|
|
35699
|
+
* skippedOutputs`). `undefined` when nothing was skipped, so the saves that
|
|
35700
|
+
* persist the resolved bag spread it as-is and the field stays omitted. Same
|
|
35701
|
+
* lifetime rule as `resolvedExportNames`: reset at the top of every
|
|
35702
|
+
* `resolveOutputs`, meaningful only right after that call returns.
|
|
35703
|
+
*/
|
|
35704
|
+
skippedOutputs;
|
|
35705
|
+
/**
|
|
35038
35706
|
* Whether {@link outputsTemplateSource} may be used to POSITION the outputs
|
|
35039
35707
|
* redaction. False once an outputs pass threw partway: the post-loop
|
|
35040
35708
|
* name pass never ran, so the bag holds only the alias keys written before
|
|
@@ -35104,7 +35772,7 @@ var DeployEngine = class {
|
|
|
35104
35772
|
});
|
|
35105
35773
|
this.options.concurrency = options.concurrency ?? 10;
|
|
35106
35774
|
this.options.dryRun = options.dryRun ?? false;
|
|
35107
|
-
this.options.lockTimeout = options.lockTimeout ??
|
|
35775
|
+
this.options.lockTimeout = options.lockTimeout ?? 3e5;
|
|
35108
35776
|
this.options.noRollback = options.noRollback ?? false;
|
|
35109
35777
|
this.options.resourceWarnAfterMs = options.resourceWarnAfterMs ?? 3e5;
|
|
35110
35778
|
this.options.resourceTimeoutMs = options.resourceTimeoutMs ?? 18e5;
|
|
@@ -35119,9 +35787,11 @@ var DeployEngine = class {
|
|
|
35119
35787
|
this.perResourceSecrets = /* @__PURE__ */ new Map();
|
|
35120
35788
|
this.noEchoAttributeResources = /* @__PURE__ */ new Map();
|
|
35121
35789
|
this.perResourceTemplateProps = /* @__PURE__ */ new Map();
|
|
35790
|
+
this.attemptedResolvedProps = /* @__PURE__ */ new Map();
|
|
35122
35791
|
this.outputSecrets = /* @__PURE__ */ new Map();
|
|
35123
|
-
this.outputsTemplateSource =
|
|
35792
|
+
this.outputsTemplateSource = Object.create(null);
|
|
35124
35793
|
this.outputsSourceUsable = true;
|
|
35794
|
+
this.skippedOutputs = void 0;
|
|
35125
35795
|
this.retainedOldOnReplacement = /* @__PURE__ */ new Set();
|
|
35126
35796
|
this.resolver.resetPhysicalIdFallbackCount();
|
|
35127
35797
|
return withStackName(stackName, () => this.doDeploy(stackName, template));
|
|
@@ -35339,7 +36009,7 @@ var DeployEngine = class {
|
|
|
35339
36009
|
const ownSecrets = secrets ?? /* @__PURE__ */ new Map();
|
|
35340
36010
|
const next = { ...op };
|
|
35341
36011
|
if (next.properties) next.properties = redactSecretsForState(next.properties, ownSecrets, templateProps);
|
|
35342
|
-
if (next.attemptedProperties) next.attemptedProperties = redactSecretsForState(next.attemptedProperties, ownSecrets, templateProps);
|
|
36012
|
+
if (next.attemptedProperties) next.attemptedProperties = redactSecretsForState(markSameGenerationBag({ ...next.attemptedProperties }), ownSecrets, templateProps);
|
|
35343
36013
|
if (next.previousState) next.previousState = scrubResourceRecord(next.previousState, ownSecrets);
|
|
35344
36014
|
return next;
|
|
35345
36015
|
});
|
|
@@ -35427,7 +36097,7 @@ var DeployEngine = class {
|
|
|
35427
36097
|
const logicalId = entries[i][0];
|
|
35428
36098
|
const observed = resolved[i];
|
|
35429
36099
|
const target = stateResources[logicalId];
|
|
35430
|
-
if (target && observed !== void 0) target.observedProperties = observed;
|
|
36100
|
+
if (target && observed !== void 0) target.observedProperties = markSameGenerationBag({ ...observed });
|
|
35431
36101
|
}
|
|
35432
36102
|
}
|
|
35433
36103
|
/**
|
|
@@ -35605,6 +36275,10 @@ var DeployEngine = class {
|
|
|
35605
36275
|
} catch {}
|
|
35606
36276
|
this.kickOffAutoRefreshObservedProperties(currentState.resources);
|
|
35607
36277
|
this.logger.debug(`Template has ${Object.keys(template.Resources || {}).length} resources`);
|
|
36278
|
+
const outputsDigestSource = structuredClone({
|
|
36279
|
+
...template,
|
|
36280
|
+
Resources: {}
|
|
36281
|
+
});
|
|
35608
36282
|
const parameterValues = await this.resolver.resolveParameters(template, this.options.parameters, { ...this.options.inheritedSecrets && this.options.inheritedSecrets.size > 0 && { inheritedSecrets: this.options.inheritedSecrets } });
|
|
35609
36283
|
this.logger.debug(`Resolved ${Object.keys(parameterValues).length} parameters: ${Object.keys(parameterValues).join(", ")}`);
|
|
35610
36284
|
const context = this.buildResolverContext({
|
|
@@ -35648,7 +36322,7 @@ var DeployEngine = class {
|
|
|
35648
36322
|
this.logger.info("No changes detected. Stack is up to date.");
|
|
35649
36323
|
let persistedOutputs = currentState.outputs ?? {};
|
|
35650
36324
|
if (!this.options.dryRun) {
|
|
35651
|
-
const resolvedOutputs = this.redactOutputs(await this.resolveOutputs(effectiveTemplate, currentState.resources, stackName, parameterValues, conditions));
|
|
36325
|
+
const resolvedOutputs = this.redactOutputs(await this.resolveOutputs(effectiveTemplate, currentState.resources, stackName, outputsDigestSource, parameterValues, conditions));
|
|
35652
36326
|
const resolutionFailed = Object.values(resolvedOutputs).some((v) => v === void 0);
|
|
35653
36327
|
const outputsChanged = !resolutionFailed && !outputMapsEqual(persistedOutputs, resolvedOutputs);
|
|
35654
36328
|
const currentEffectiveExports = new Set(importableOutputKeys(currentState));
|
|
@@ -35657,7 +36331,8 @@ var DeployEngine = class {
|
|
|
35657
36331
|
if (resolutionFailed && !outputMapsEqual(persistedOutputs, resolvedOutputs)) this.logger.warn("Outputs changed but one or more could not be resolved; keeping the previously persisted outputs. A downstream Fn::ImportValue may fail until the next deploy.");
|
|
35658
36332
|
const observedRefresh = this.observedCaptureTasks.size > 0;
|
|
35659
36333
|
if (observedRefresh) await this.drainObservedCaptures(currentState.resources);
|
|
35660
|
-
|
|
36334
|
+
const skippedOutputsChanged = !skippedOutputsEqual(currentState.skippedOutputs, this.skippedOutputs);
|
|
36335
|
+
if (observedRefresh || outputsChanged || exportSetChanged || skippedOutputsChanged) try {
|
|
35661
36336
|
const refreshedState = {
|
|
35662
36337
|
version: 9,
|
|
35663
36338
|
region: this.stackRegion,
|
|
@@ -35665,6 +36340,7 @@ var DeployEngine = class {
|
|
|
35665
36340
|
resources: currentState.resources,
|
|
35666
36341
|
outputs: outputsChanged ? resolvedOutputs : persistedOutputs,
|
|
35667
36342
|
...resolutionFailed ? exportNamesCarriedFrom(currentState) : { exportNames: [...this.resolvedExportNames] },
|
|
36343
|
+
...this.skippedOutputs && { skippedOutputs: { ...this.skippedOutputs } },
|
|
35668
36344
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
35669
36345
|
lastModified: Date.now()
|
|
35670
36346
|
};
|
|
@@ -35677,7 +36353,8 @@ var DeployEngine = class {
|
|
|
35677
36353
|
if (outputsChanged) this.logger.info("Persisted Outputs-only change (no resource diff).");
|
|
35678
36354
|
else this.logger.debug("Persisted export-set change (no outputs-value diff, no-change path, #2193)");
|
|
35679
36355
|
if (this.exportIndexStore) await this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs(refreshedState));
|
|
35680
|
-
} else this.logger.debug("Persisted refreshed observedProperties (no-change path)");
|
|
36356
|
+
} else if (observedRefresh) this.logger.debug("Persisted refreshed observedProperties (no-change path)");
|
|
36357
|
+
else this.logger.debug("Persisted skipped-outputs record (no outputs-value diff, no-change path, #2740)");
|
|
35681
36358
|
} catch (saveError) {
|
|
35682
36359
|
this.logger.warn(`Failed to persist no-change state update: ${saveError instanceof Error ? saveError.message : String(saveError)} — drift baseline / outputs will be re-resolved on next deploy.`);
|
|
35683
36360
|
}
|
|
@@ -35719,7 +36396,7 @@ var DeployEngine = class {
|
|
|
35719
36396
|
current: 0,
|
|
35720
36397
|
total: createChanges.length + updateChanges.length + deleteChanges.length
|
|
35721
36398
|
};
|
|
35722
|
-
const { state: newState, actualCounts } = await this.executeDeployment(effectiveTemplate, currentState, changes, dag, executionLevels, stackName, parameterValues, conditions, currentEtag, progress, migrationPending);
|
|
36399
|
+
const { state: newState, actualCounts } = await this.executeDeployment(effectiveTemplate, currentState, changes, dag, executionLevels, stackName, outputsDigestSource, parameterValues, conditions, currentEtag, progress, migrationPending);
|
|
35723
36400
|
await this.drainObservedCaptures(newState.resources);
|
|
35724
36401
|
const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(newState));
|
|
35725
36402
|
this.logger.debug(`State saved (ETag: ${newEtag})`);
|
|
@@ -35761,7 +36438,7 @@ var DeployEngine = class {
|
|
|
35761
36438
|
* - DELETE follows reverse dependency order (a node starts as soon as all
|
|
35762
36439
|
* resources that depend ON it have finished deleting)
|
|
35763
36440
|
*/
|
|
35764
|
-
async executeDeployment(template, currentState, changes, dag, executionLevels, stackName, parameterValues, conditions, currentEtag, progress, migrationPending = false) {
|
|
36441
|
+
async executeDeployment(template, currentState, changes, dag, executionLevels, stackName, outputsDigestSource, parameterValues, conditions, currentEtag, progress, migrationPending = false) {
|
|
35765
36442
|
const concurrency = this.options.concurrency;
|
|
35766
36443
|
const newResources = { ...currentState.resources };
|
|
35767
36444
|
const actualCounts = {
|
|
@@ -35787,6 +36464,7 @@ var DeployEngine = class {
|
|
|
35787
36464
|
resources: newResources,
|
|
35788
36465
|
outputs: currentState.outputs,
|
|
35789
36466
|
...exportNamesCarriedFrom(currentState),
|
|
36467
|
+
...skippedOutputsCarriedFrom(currentState),
|
|
35790
36468
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
35791
36469
|
lastModified: Date.now()
|
|
35792
36470
|
};
|
|
@@ -35919,6 +36597,7 @@ var DeployEngine = class {
|
|
|
35919
36597
|
resources: newResources,
|
|
35920
36598
|
outputs: currentState.outputs,
|
|
35921
36599
|
...exportNamesCarriedFrom(currentState),
|
|
36600
|
+
...skippedOutputsCarriedFrom(currentState),
|
|
35922
36601
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
35923
36602
|
lastModified: Date.now()
|
|
35924
36603
|
};
|
|
@@ -35955,6 +36634,7 @@ var DeployEngine = class {
|
|
|
35955
36634
|
resources: newResources,
|
|
35956
36635
|
outputs: currentState.outputs,
|
|
35957
36636
|
...exportNamesCarriedFrom(currentState),
|
|
36637
|
+
...skippedOutputsCarriedFrom(currentState),
|
|
35958
36638
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
35959
36639
|
lastModified: Date.now()
|
|
35960
36640
|
};
|
|
@@ -35972,6 +36652,7 @@ var DeployEngine = class {
|
|
|
35972
36652
|
resources: newResources,
|
|
35973
36653
|
outputs: currentState.outputs,
|
|
35974
36654
|
...exportNamesCarriedFrom(currentState),
|
|
36655
|
+
...skippedOutputsCarriedFrom(currentState),
|
|
35975
36656
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
35976
36657
|
lastModified: Date.now()
|
|
35977
36658
|
};
|
|
@@ -35986,7 +36667,7 @@ var DeployEngine = class {
|
|
|
35986
36667
|
}
|
|
35987
36668
|
let outputs;
|
|
35988
36669
|
try {
|
|
35989
|
-
outputs = await this.resolveOutputs(template, newResources, stackName, parameterValues, conditions);
|
|
36670
|
+
outputs = await this.resolveOutputs(template, newResources, stackName, outputsDigestSource, parameterValues, conditions);
|
|
35990
36671
|
const resolvedOutputsBeforeRedaction = outputs;
|
|
35991
36672
|
outputs = this.redactOutputs(outputs);
|
|
35992
36673
|
this.rememberRecoverableMaskedOutputs(stackName, resolvedOutputsBeforeRedaction, outputs);
|
|
@@ -36003,6 +36684,7 @@ var DeployEngine = class {
|
|
|
36003
36684
|
resources: newResources,
|
|
36004
36685
|
outputs,
|
|
36005
36686
|
exportNames: [...this.resolvedExportNames],
|
|
36687
|
+
...this.skippedOutputs && { skippedOutputs: { ...this.skippedOutputs } },
|
|
36006
36688
|
...this.recordedImports.length > 0 && { imports: [...this.recordedImports] },
|
|
36007
36689
|
...this.recordedOutputReads.length > 0 && { outputReads: [...this.recordedOutputReads] },
|
|
36008
36690
|
lastModified: Date.now()
|
|
@@ -36041,6 +36723,7 @@ var DeployEngine = class {
|
|
|
36041
36723
|
resources: newResources,
|
|
36042
36724
|
outputs: currentState.outputs,
|
|
36043
36725
|
...exportNamesCarriedFrom(currentState),
|
|
36726
|
+
...skippedOutputsCarriedFrom(currentState),
|
|
36044
36727
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
36045
36728
|
lastModified: Date.now()
|
|
36046
36729
|
});
|
|
@@ -36361,7 +37044,8 @@ var DeployEngine = class {
|
|
|
36361
37044
|
if (!redirect) return;
|
|
36362
37045
|
const findings = findUnrewrittenAssetReferences(resolvedProps, redirect);
|
|
36363
37046
|
if (findings.length === 0) return;
|
|
36364
|
-
|
|
37047
|
+
const detail = findings.map((f) => ` - ${f.path}: still references '${f.source}'`).join("\n");
|
|
37048
|
+
throw new ProvisioningError(`Unrewritten asset reference on '${logicalId}' (${resourceType}): this region uses cdkd-owned asset storage, but the following resolved properties still point at the CDK bootstrap storage that 'cdk gc' may garbage-collect:\n${detail}\nThis is a template shape cdkd's asset-reference rewrite did not cover — deploying it would split-brain the stack (assets in cdkd storage, properties reading the CDK bucket). Please report this at https://github.com/go-to-k/cdkd/issues with the property shape. Workaround: deploy with --use-cdk-bootstrap-assets to pin the legacy destinations for this app.`, resourceType, logicalId);
|
|
36365
37049
|
}
|
|
36366
37050
|
/**
|
|
36367
37051
|
* The `Snapshot`-policy gate every engine delete site runs BEFORE its
|
|
@@ -36503,7 +37187,7 @@ var DeployEngine = class {
|
|
|
36503
37187
|
recordNestedStackParameterExpressions(updateSecrets, resourceType, resolvedProps, desiredProps);
|
|
36504
37188
|
this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);
|
|
36505
37189
|
this.attemptedResolvedProps.set(logicalId, resolvedProps);
|
|
36506
|
-
if (JSON.stringify(redactSecretsForState(resolvedProps, updateSecrets, desiredProps)) === JSON.stringify(currentPropsAsWritten)) {
|
|
37190
|
+
if (JSON.stringify(redactSecretsForState(markSameGenerationBag({ ...resolvedProps }), updateSecrets, desiredProps)) === JSON.stringify(currentPropsAsWritten)) {
|
|
36507
37191
|
if (change.attributeChanges && change.attributeChanges.length > 0) {
|
|
36508
37192
|
const attrSummary = change.attributeChanges.map((a) => `${a.attribute}: ${a.oldValue ?? "(unset)"} → ${a.newValue ?? "(unset)"}`).join(", ");
|
|
36509
37193
|
this.logger.info(` ↻ ${logicalId} (${resourceType}) attribute update: ${attrSummary}`);
|
|
@@ -36733,7 +37417,8 @@ var DeployEngine = class {
|
|
|
36733
37417
|
createResult = await this.withRetry(() => withCurrentResourceSecrets(updateSecrets, () => replProvider.create(logicalId, resourceType, replProps, { maskSecrets: createSecretMasker(updateSecrets) })), logicalId, void 0, void 0, replProvider);
|
|
36734
37418
|
} catch (createError) {
|
|
36735
37419
|
if (!retainOldOnReplace) throw new Error(maskSecretsInText(`Failed to create ${logicalId} after the UPDATE-not-supported replacement: the old resource (${currentResource.physicalId}) is now gone. Cause: ${createError instanceof Error ? createError.message : String(createError)}. Re-run the deploy to create it fresh.`, updateSecrets), { cause: createError instanceof Error ? createError : void 0 });
|
|
36736
|
-
|
|
37420
|
+
const createMsg = createError instanceof Error ? createError.message : String(createError);
|
|
37421
|
+
if (!isNameCollisionError(createMsg)) throw createError;
|
|
36737
37422
|
const nameOrigin = this.replacementNameOrigin(logicalId, currentResource.physicalId);
|
|
36738
37423
|
throw markNonRetryable(new CdkdError(`${logicalId} (${resourceType}) requires replacement because the provisioning layer cannot update it in place — but its physical name is still held by the existing resource AND UpdateReplacePolicy: Retain pins that resource in place. ${nameOrigin.descriptor}. ${nameOrigin.remedy} — with Retain, the old resource keeps the name, so a same-name replacement can never proceed. Removing UpdateReplacePolicy: Retain lets cdkd delete the old resource first, which destroys it and any data it holds.`, "NAMED_REPLACEMENT_COLLISION", createError instanceof Error ? createError : void 0));
|
|
36739
37424
|
}
|
|
@@ -36782,8 +37467,10 @@ var DeployEngine = class {
|
|
|
36782
37467
|
const updateCaptureSiblings = await this.buildObservedCaptureSiblings(resourceType, logicalId, result.physicalId, template, stateResources, stackName, parameterValues, conditions);
|
|
36783
37468
|
this.kickOffObservedCapture(captureProvider, logicalId, result.physicalId, resourceType, resolvedProps, updateCaptureSiblings);
|
|
36784
37469
|
const updatePartial = updatePartialReason(result);
|
|
36785
|
-
if (counts)
|
|
36786
|
-
|
|
37470
|
+
if (counts) {
|
|
37471
|
+
if (updatePartial !== void 0) counts.updatePartial++;
|
|
37472
|
+
else counts.updated++;
|
|
37473
|
+
}
|
|
36787
37474
|
if (progress) progress.current++;
|
|
36788
37475
|
const updatePrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
36789
37476
|
renderer.removeTask(logicalId);
|
|
@@ -36903,9 +37590,9 @@ var DeployEngine = class {
|
|
|
36903
37590
|
* `cdkd export`, `cdkd state`.
|
|
36904
37591
|
*/
|
|
36905
37592
|
propertiesToRecord(desiredProperties, result, resourceType, provisionedBy) {
|
|
36906
|
-
|
|
36907
|
-
|
|
36908
|
-
return
|
|
37593
|
+
if (result.effectiveProperties) return provisionedBy === "sdk" ? withoutSilentDropProperties(resourceType, result.effectiveProperties) : result.effectiveProperties;
|
|
37594
|
+
const written = provisionedBy === "sdk" ? withoutSilentDropProperties(resourceType, desiredProperties) : desiredProperties;
|
|
37595
|
+
return markSameGenerationBag(written);
|
|
36909
37596
|
}
|
|
36910
37597
|
/**
|
|
36911
37598
|
* The name-origin half of the replacement-collision messages (issue #1636).
|
|
@@ -37217,10 +37904,10 @@ var DeployEngine = class {
|
|
|
37217
37904
|
* rather than whatever the declaration order happened to have recorded by
|
|
37218
37905
|
* then (issue #1919).
|
|
37219
37906
|
*/
|
|
37220
|
-
async resolveOutputs(template, resources, stackName, parameterValues, conditions) {
|
|
37907
|
+
async resolveOutputs(template, resources, stackName, digestSource, parameterValues, conditions) {
|
|
37221
37908
|
this.resolvedExportNames = [];
|
|
37222
37909
|
if (!template.Outputs) return {};
|
|
37223
|
-
const outputs =
|
|
37910
|
+
const outputs = Object.create(null);
|
|
37224
37911
|
const context = this.buildResolverContext({
|
|
37225
37912
|
template,
|
|
37226
37913
|
resources,
|
|
@@ -37285,7 +37972,8 @@ var DeployEngine = class {
|
|
|
37285
37972
|
if (!publishedOutputNames.has(outputKey)) continue;
|
|
37286
37973
|
this.outputsTemplateSource[outputKey] = output.Value;
|
|
37287
37974
|
}
|
|
37288
|
-
|
|
37975
|
+
this.skippedOutputs = collectSkippedOutputs(digestSource, outputs);
|
|
37976
|
+
return markSameGenerationBag(outputs);
|
|
37289
37977
|
}
|
|
37290
37978
|
buildDisplayOutputs(template, resolvedOutputs) {
|
|
37291
37979
|
const display = {};
|
|
@@ -37299,5 +37987,5 @@ var DeployEngine = class {
|
|
|
37299
37987
|
};
|
|
37300
37988
|
|
|
37301
37989
|
//#endregion
|
|
37302
|
-
export {
|
|
37303
|
-
//# sourceMappingURL=deploy-engine-
|
|
37990
|
+
export { ProviderRegistry as $, redactDockerArgvValues as $n, ResourceUpdateNotSupportedError as $r, STATE_SOURCED_READBACK_RULES as $t, renderStatefulReason as A, createAssetRedirectResolver as An, AssemblyReader as Ar, configStringRefusal as At, red as B, readBootstrapMarkerBody as Bn, ConfigError as Br, s3BucketDualStackDomainName as Bt, refusesFinalSnapshot as C, importableOutputKeys as Cn, uploadCfnTemplate as Cr, refStateLookupFromResource as Ct, MULTI_REGION_RECREATE_BLOCKED_TYPES as D, stringifyValue as Dn, PARTITION_TABLE as Dr, assertRegionMatch as Dt, extractDeploymentEventError as E, AssetPublisher as En, expectedOwnerParam as Er, resolveExplicitPhysicalId as Et, formatResourceLine as F, assertAssetBucketRegion as Fn, getAwsClients as Fr, requireConfigString as Ft, isExportAliasCollision as G, buildDockerImage as Gn, IntrinsicResolutionRefusalError as Gr, INTRINSIC_KEYS as Gt, collectDeclaredOutputNames as H, validateContainerRepoName as Hn, DependencyError as Hr, s3BucketWebsiteUrl as Ht, bold as I, ensureAssetStorage as In, resetAwsClients as Ir, classifyReplaySecretRegion as It, stateKeySecretExposure as J, describeDockerFailure as Jn, LockError as Jr, describeTypeWithThrottleRetry as Jt, secretBearingStateKeyWarning as K, describeDockerCapturedOutput as Kn, LocalInvokeBuildError as Kr, findActionableSilentDrops as Kt, cyan as L, getBootstrapMarkerKey as Ln, setAwsClients as Lr, producerRegionsFromState as Lt, coerceWarmThroughput as M, rewriteTemplateAssetReferences as Mn, clearBucketRegionCache as Mr, replayWarn as Mt, isWarmThroughputDecrease as N, AssetModeResolver as Nn, resolveBucketRegion as Nr, requireConfigArray as Nt, isStatefulRecreateTargetForReplace as O, WorkGraph as On, canonicalizeRegion as Or, coerceCfnBoolean as Ot, toFiniteNumber as P, BOOTSTRAP_MARKER_PREFIX as Pn, AwsClients as Pr, requireConfigObject as Pt, clearOnUpdateRemoval as Q, partitionSensitiveEnv as Qn, ResourceTimeoutError as Qr, STATE_SOURCED_CROSS_GENERATION_RULES as Qt, gray as R, isCrossRegionRedirect as Rn, AssetError as Rr, s3BucketArn as Rt, isFinalSnapshotError as S, exportNamesCarriedFrom as Sn, findLargeInlineResources as Sr, parameterTypeMayLoseSecretIdentity as St, makeCanonicalizePropertiesFn as T, shouldRetainResource as Tn, displaySafe as Tr, normalizeAwsTagsToCfn as Tt, collectPublishedOutputNames as U, buildDenyExternalAccessPolicy as Un, DeployCancelledError as Ur, applyRoleArnIfSet as Ut, yellow as V, validateAssetBucketName as Vn, CrossAccountSecretRefusalError as Vr, s3BucketRegionalDomainName as Vt, exportAliasCollisionScrubWarning as W, describeAwsFailure as Wn, DynamicReferenceRegionAmbiguousError as Wr, DiffCalculator as Wt, IAMRoleProvider as X, formatDockerLoginError as Xn, PartialFailureError as Xr, DagBuilder as Xt, getCurrentResourceSecrets as Y, dockerSpawnEnvWithSensitive as Yn, NestedStackChildDirectDestroyError as Yr, withRetry as Yt, collectInlinePolicyNamesManagedBySiblings as Z, getDockerCmd as Zn, ProvisioningError as Zr, TemplateParser as Zt, ATOMIC_FINAL_SNAPSHOT_TYPES as _, buildLockContentionMessage as _n, stateBucketExistenceConfirmed as _r, carriesDynamicReference as _t, DeploymentEventsStore as a, isCdkdError as ai, isSingleDynamicReferenceToken as an, getDockerImageBySourceHash as ar, endCommandInterruptScope as at, ccRoutedFinalSnapshotError as b, CUSTOM_RESOURCE_RESPONSE_PREFIX as bn, CFN_TEMPLATE_URL_LIMIT as br, getAccountInfo as bt, replayFailedOperations as c, isMarkedNonRetryable as ci, recordMaskOnlyValue as cn, getDefaultStateBucketName as cr, startInterruptWatch as ct, updatePartialReason as d, isTransientServerError as di, scrubResourceRecord as dn, resolveAutoAssetStorage as dr, UNSPECIFIED_SKIP_REASON as dt, StackHasActiveImportsError as ei, TEMPLATE_SOURCED_RULES as en, runDockerForeground as er, wouldReturnToSdkProvider as et, withResourceDeadline as f, markNonRetryable as fi, LockManager as fn, resolveCaptureObservedState as fr, deleteIndeterminateGuards as ft, bindingSkippedOutputs as g, buildForceUnlockCommand as gn, resolveUseCdkBootstrapAssets as gr, IntrinsicFunctionResolver as gt, computeImplicitDeleteEdges as h, __exportAll as hi, UNRENDERABLE as hn, resolveStateBucketWithDefaultAndSource as hr, isTerminationProtectionPropagationError as ht, DeploymentEventsReader as i, formatError as ii, errorCauseChain as in, AssetManifestLoader as ir, beginCommandInterruptScope as it, WARM_THROUGHPUT_MEMBERS as j, loadPublishableAssetManifest as jn, processStackMessages as jr, readConfigString as jt, isStatefulRecreateTargetSync as k, buildAssetRedirectMap as kn, derivePartitionAndUrlSuffix as kr, configBooleanRefusal as kt, replayRollback as l, isRetryableTransientError as li, recoverMaskedOutput as ln, getLegacyStateBucketName as lr, CloudControlProvider as lt, IMPLICIT_DELETE_DEPENDENCIES as m, retryClassificationText as mi, rebuildClientForBucketRegion as mn, resolveStateBucketWithDefault as mr, disableInstanceApiTermination as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, StateError as ni, createSecretMasker as nn, escapeRegExp$1 as nr, maskDeep as nt, planFailedOps as o, normalizeAwsError as oi, maskSecretsInError as on, Synthesizer as or, interruptWatchListenerCount as ot, maskingRetryLogger as p, markRedactedCause as pi, S3StateBackend as pn, resolveSkipPrefix as pr, deleteSkipReason as pt, secretSafeKeyDisplay as q, describeDockerExecFailure as qn, LocalStartServiceError as qr, findSilentDropProperties as qt, DeployEngine as r, SynthesisError as ri, dynamicReferenceTokens as rn, stripControlChars as rr, maskerOrIdentity as rt, planRollback as s, withErrorHandling as si, maskSecretsInText as sn, synthesisStatusMessage as sr, isInterruptedWaitError as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, StackTerminationProtectionError as ti, carriesSecretMask as tn, runDockerStreaming as tr, createMaskedRetryLogger as tt, updatePartialMessage as u, isThrottlingError as ui, redactSecretsForState as un, resolveApp as ur, slowCcOperationTimeoutMs as ut, PRE_DELETE_SNAPSHOT_TYPES as v, forceQuitRecoveryClause as vn, warnDeprecatedNoPrefixCliFlag as vr, cfnRefValueFromPhysicalId as vt, unsupportedFinalSnapshotError as w, importableOutputs as wn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as wr, WAFv2WebACLProvider as wt, createPreDeleteFinalSnapshot as x, DEFAULT_STATE_PREFIX as xn, MIGRATE_TMP_PREFIX as xr, isUnboundTemplateParameter as xt, buildFinalSnapshotIdentifier as y, shellQuote as yn, CFN_TEMPLATE_BODY_LIMIT as yr, coerceParameterTypedValue as yt, green as z, parseBootstrapMarker as zn, CdkdError as zr, s3BucketDomainName as zt };
|
|
37991
|
+
//# sourceMappingURL=deploy-engine-CfxcC3q3.js.map
|