@go-to-k/cdkd 0.290.3 → 0.290.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-RLnpBIKj.js → asg-provider-DU7mN-cS.js} +2 -2
- package/dist/{asg-provider-RLnpBIKj.js.map → asg-provider-DU7mN-cS.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-0Wg4MwP2.js → deploy-engine-WFO9o9M0.js} +271 -54
- package/dist/deploy-engine-WFO9o9M0.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-CoRRfEe_.js → program-D30EPjOn.js} +306 -105
- package/dist/{program-CoRRfEe_.js.map → program-D30EPjOn.js.map} +1 -1
- package/dist/{version-BTao4oxo.js → version-BBIkp8ja.js} +2 -2
- package/dist/{version-BTao4oxo.js.map → version-BBIkp8ja.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-0Wg4MwP2.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $ as secretSafeKeyDisplay, $n as
|
|
1
|
+
import { $ as secretSafeKeyDisplay, $n as isCrossRegionRedirect, $r as CdkdError, $t as applyRoleArnIfSet, A as planOrphanAdoption, An as forceQuitRecoveryClause, Ar as resolveStateBucketWithDefaultAndSource, At as isSettledInstanceState, B as formatResourceLine, Bn as shouldRetainResource, Br as expectedOwnerParam, Bt as readConfigString, C as createPreDeleteFinalSnapshot, Ci as isRetryableTransientError, Cn as redactSecretsForState, Cr as getDefaultStateBucketName, Ct as cfnRefValueFromPhysicalId, D as makeCanonicalizePropertiesFn, Di as markRedactedCause, Dn as rebuildClientForBucketRegion, Dr as resolveCaptureObservedState, Dt as isUnboundTemplateParameter, E as unsupportedFinalSnapshotError, Ei as markNonRetryable, En as S3StateBackend, Er as resolveAutoAssetStorage, Et as isImpossibleEmptyStoredAttribute, F as WARM_THROUGHPUT_MEMBERS, Fn as exportNamesCarriedFrom, Fr as CFN_TEMPLATE_URL_LIMIT, Ft as coerceCfnBoolean, G as red, Gn as createAssetRedirectResolver, Gr as processStackMessages, Gt as classifyReplaySecretRegion, H as cyan, Hn as stringifyValue, Hr as canonicalizeRegion, Ht as requireConfigArray, I as coerceWarmThroughput$1, In as importableOutputKeys, Ir as MIGRATE_TMP_PREFIX, It as coerceCfnInteger, J as collectPublishedOutputNames, Jn as AssetModeResolver, Jr as AwsClients, Jt as s3BucketArn, K as yellow, Kn as loadPublishableAssetManifest, Kt as producerRegionsFromState, L as isWarmThroughputDecrease, Ln as importableOutputs, Lr as findLargeInlineResources, Lt as configBooleanRefusal, M as isStatefulRecreateTargetForReplace, Mn as shellQuote, Mr as stateBucketExistenceConfirmed, Mt as normalizeAwsTagsToCfn, N as isStatefulRecreateTargetSync, Nn as CUSTOM_RESOURCE_RESPONSE_PREFIX, Nr as warnDeprecatedNoPrefixCliFlag, Nt as resolveExplicitPhysicalId, O as extractDeploymentEventError, Oi as retryClassificationText, On as buildForceUnlockCommand, Or as resolveSkipPrefix, Ot as parameterTypeMayLoseSecretIdentity, P as renderStatefulReason, Pn as DEFAULT_STATE_PREFIX, Pr as CFN_TEMPLATE_BODY_LIMIT, Pt as assertRegionMatch, Q as secretBearingStateKeyWarning, Qn as getBootstrapMarkerKey, Qt as s3BucketWebsiteUrl, R as toCfnInteger, Rn as orphansAfterRollback, Rr as uploadCfnTemplate, Rt as configIntegerRefusal, S as ccRoutedFinalSnapshotError, Si as isMarkedNonRetryable, Sn as recoverMaskedOutput, Sr as synthesisStatusMessage, St as carriesDynamicReference, T as refusesFinalSnapshot, Ti as isTransientServerError, Tn as LockManager, Tr as resolveApp, Tt as getAccountInfo, U as gray, Un as WorkGraph, Ur as derivePartitionAndUrlSuffix, Ut as requireConfigObject, V as bold, Vn as AssetPublisher, Vr as PARTITION_TABLE, Vt as replayWarn, W as green, Wn as buildAssetRedirectMap, Wr as AssemblyReader, Wt as requireConfigString, X as isExportAliasCollision, Xn as assertAssetBucketRegion, Xr as resetAwsClients, Xt as s3BucketDualStackDomainName, Y as exportAliasCollisionScrubWarning, Yn as BOOTSTRAP_MARKER_PREFIX, Yr as getAwsClients, Yt as s3BucketDomainName, Z as secretBearing, Zn as ensureAssetStorage, Zr as setAwsClients, Zt as s3BucketRegionalDomainName, _ as isSecretBearingReferenceString, _n as isSingleDynamicReferenceToken, _r as escapeRegExp, _t as deleteIndeterminateGuards, a as DeploymentEventsStore, ai as IntrinsicResolutionRefusalError, an as withRetry, ar as describeAwsFailure, at as wouldReturnToSdkProvider, b as PRE_DELETE_SNAPSHOT_TYPES, bi as normalizeAwsError, bn as maskSecretsInText, br as getDockerImageBySourceHash, bt as isTerminationProtectionPropagationError, c as replayFailedOperations, cn as STATE_SOURCED_BASELINE_RULES, cr as describeDockerExecFailure, ct as maskerOrIdentity, d as updatePartialReason, di as ProvisioningError, dn as TEMPLATE_SOURCED_RULES, dr as formatDockerLoginError, dt as interruptWatchListenerCount, en as DiffCalculator, er as parseBootstrapMarker, et as getCurrentResourceSecrets, f as withResourceDeadline, fi as ResourceTimeoutError, fn as carriesSecretMask, fr as getDockerCmd, ft as isInterruptedWaitError, g as bagHoldsSecretExpression, gn as identityKeyFor, gr as runDockerStreaming, gt as UNSPECIFIED_SKIP_REASON, h as computeImplicitDeleteEdges, hi as StackTerminationProtectionError, hn as errorCauseChain, hr as runDockerForeground, ht as slowCcOperationTimeoutMs, i as DeploymentEventsReader, ii as DynamicReferenceRegionAmbiguousError, in as describeTypeWithThrottleRetry, ir as buildDenyExternalAccessPolicy, it as ProviderRegistry, j as MULTI_REGION_RECREATE_BLOCKED_TYPES, jn as formatLockExpiry, jr as resolveUseCdkBootstrapAssets, jt as WAFv2WebACLProvider, k as makeSiblingClaimReader, kn as buildLockContentionMessage, kr as resolveStateBucketWithDefault, kt as refStateLookupFromResource, l as replayRollback, li as NestedStackChildDirectDestroyError, ln as STATE_SOURCED_CROSS_GENERATION_RULES, lr as describeDockerFailure, lt as beginCommandInterruptScope, m as IMPLICIT_DELETE_DEPENDENCIES, mi as StackHasActiveImportsError, mn as dynamicReferenceTokens, mr as redactDockerArgvValues, mt as CloudControlProvider, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as findActionableSilentDrops, nr as validateAssetBucketName, nt as collectInlinePolicyNamesManagedBySiblings, o as planFailedOps, oi as LocalInvokeBuildError$1, on as DagBuilder, or as buildDockerImage, ot as createMaskedRetryLogger, p as maskingRetryLogger, pi as ResourceUpdateNotSupportedError, pn as createSecretMasker, pr as partitionSensitiveEnv, pt as startInterruptWatch, q as collectDeclaredOutputNames, qn as rewriteTemplateAssetReferences, qr as resolveBucketRegion, qt as withSharedDrainBudget, r as DeployEngine, ri as DeployCancelledError, rn as findSilentDropProperties, rr as validateContainerRepoName, rt as clearOnUpdateRemoval, s as planRollback, si as LocalStartServiceError, sn as TemplateParser, sr as describeDockerCapturedOutput, st as maskDeep, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as CrossAccountSecretRefusalError, tn as INTRINSIC_KEYS, tr as readBootstrapMarkerBody, tt as IAMRoleProvider, u as updatePartialMessage, ui as PartialFailureError, un as STATE_SOURCED_READBACK_RULES, ur as dockerSpawnEnvWithSensitive, ut as endCommandInterruptScope, v as bindingSkippedOutputs, vn as markSameGenerationBag, vr as stripControlChars, vt as deleteSkipReason, w as isFinalSnapshotError, wi as isThrottlingError, wn as scrubResourceRecord, wr as getLegacyStateBucketName, wt as coerceParameterTypedValue, x as buildFinalSnapshotIdentifier, xi as withErrorHandling, xn as recordMaskOnlyValue, xr as Synthesizer, xt as IntrinsicFunctionResolver, y as ATOMIC_FINAL_SNAPSHOT_TYPES, yn as maskSecretsInError, yr as AssetManifestLoader, yt as disableInstanceApiTermination, z as toFiniteNumber, zn as orphansCarriedFrom, zr as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, zt as configStringRefusal } from "./deploy-engine-WFO9o9M0.js";
|
|
2
2
|
import { S as truncateCodePoints, _ as withSkipPrefix, b as displayIdent, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, x as displaySafe, y as UNRENDERABLE } from "./logger-th1myfgh.js";
|
|
3
3
|
import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
|
|
4
|
-
import { t as getCdkdVersion } from "./version-
|
|
5
|
-
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as getCdkdVersion } from "./version-BBIkp8ja.js";
|
|
5
|
+
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-DU7mN-cS.js";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
8
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, NotFound, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -18953,7 +18953,7 @@ var DynamoDBTableProvider = class {
|
|
|
18953
18953
|
const warn = (message) => this.logger.warn(maskSecrets(message));
|
|
18954
18954
|
const coerced = coerceWarmThroughput(value);
|
|
18955
18955
|
if (coerced === void 0) return void 0;
|
|
18956
|
-
if (coerced.dropped.length > 0) warn(`${scope}: WarmThroughput member(s) ${coerced.dropped.join(", ")} in ${JSON.stringify(maskLeafValue$1(value, maskSecrets))} are not a number DynamoDB accepts, so they were dropped from the request, which leaves ${JSON.stringify(maskLeafValue$1(coerced.spec, maskSecrets))}. Check for an unresolved intrinsic or a
|
|
18956
|
+
if (coerced.dropped.length > 0) warn(`${scope}: WarmThroughput member(s) ${coerced.dropped.join(", ")} in ${JSON.stringify(maskLeafValue$1(value, maskSecrets))} are not a number DynamoDB accepts, so they were dropped from the request, which leaves ${JSON.stringify(maskLeafValue$1(coerced.spec, maskSecrets))}. Check for an unresolved intrinsic or a spelling CloudFormation would reject: an Integer here is an optional sign and decimal digits only (no surrounding whitespace, hex, exponent or decimal point).`);
|
|
18957
18957
|
return coerced.spec;
|
|
18958
18958
|
}
|
|
18959
18959
|
/**
|
|
@@ -20726,10 +20726,10 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
20726
20726
|
const watch = startInterruptWatch(`DynamoDB auto-scaling for ${tableName}`);
|
|
20727
20727
|
try {
|
|
20728
20728
|
if (newEnabled) {
|
|
20729
|
-
const minCapacity =
|
|
20730
|
-
const maxCapacity =
|
|
20731
|
-
if (
|
|
20732
|
-
warn(`Cannot apply auto-scaling diff on ${tableName} (${dimension}): MinCapacity / MaxCapacity must be
|
|
20729
|
+
const minCapacity = coerceCfnInteger(newSettings["MinCapacity"] ?? 0);
|
|
20730
|
+
const maxCapacity = coerceCfnInteger(newSettings["MaxCapacity"] ?? 0);
|
|
20731
|
+
if (minCapacity === void 0 || maxCapacity === void 0) {
|
|
20732
|
+
warn(`Cannot apply auto-scaling diff on ${tableName} (${dimension}): MinCapacity / MaxCapacity must be integers CloudFormation accepts (an optional sign and decimal digits; no surrounding whitespace, hex, exponent or decimal point), got ${maskSecrets(String(newSettings["MinCapacity"]))} / ${maskSecrets(String(newSettings["MaxCapacity"]))}`);
|
|
20733
20733
|
return;
|
|
20734
20734
|
}
|
|
20735
20735
|
try {
|
|
@@ -20761,8 +20761,16 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
20761
20761
|
PredefinedMetricSpecification: { PredefinedMetricType: metricType },
|
|
20762
20762
|
TargetValue: targetValue
|
|
20763
20763
|
};
|
|
20764
|
-
|
|
20765
|
-
|
|
20764
|
+
for (const cooldown of ["ScaleInCooldown", "ScaleOutCooldown"]) {
|
|
20765
|
+
const raw = tttCfg[cooldown];
|
|
20766
|
+
if (raw === void 0) continue;
|
|
20767
|
+
const seconds = coerceCfnInteger(raw);
|
|
20768
|
+
if (seconds === void 0) {
|
|
20769
|
+
warn(`Auto-scaling policy on ${tableName} (${dimension}): TargetTrackingScalingPolicyConfiguration.${cooldown} is not an integer CloudFormation accepts (got ${JSON.stringify(maskDeep(raw, maskSecrets))?.slice(0, 80)}), so it is omitted and the service default applies.`);
|
|
20770
|
+
continue;
|
|
20771
|
+
}
|
|
20772
|
+
targetTrackingConfig[cooldown] = seconds;
|
|
20773
|
+
}
|
|
20766
20774
|
if (tttCfg["DisableScaleIn"] !== void 0) targetTrackingConfig["DisableScaleIn"] = Boolean(tttCfg["DisableScaleIn"]);
|
|
20767
20775
|
try {
|
|
20768
20776
|
await withRetry(() => asClient.send(new PutScalingPolicyCommand({
|
|
@@ -22145,13 +22153,13 @@ function warmThroughputDiagnostic(raw, coercion, indexName) {
|
|
|
22145
22153
|
return {
|
|
22146
22154
|
...scope,
|
|
22147
22155
|
...indexName !== void 0 && { indexName },
|
|
22148
|
-
message: `WarmThroughput.${dropped} did not resolve to a number and was DROPPED; ${kept} is still sent. AWS types these as numbers, so a quoted "12000" is fine but an unresolved intrinsic or an object is not. The dropped member keeps its current AWS value until the template supplies a usable one.`
|
|
22156
|
+
message: `WarmThroughput.${dropped} did not resolve to a number and was DROPPED; ${kept} is still sent. AWS types these as numbers, so a quoted "12000" is fine but an unresolved intrinsic or an object is not. The dropped member keeps its current AWS value until the template supplies a usable one. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way.`
|
|
22149
22157
|
};
|
|
22150
22158
|
}
|
|
22151
22159
|
return {
|
|
22152
22160
|
...scope,
|
|
22153
22161
|
...indexName !== void 0 && { indexName },
|
|
22154
|
-
message: `WarmThroughput is declared but no member resolved to a number${dropped ? ` (${dropped})` : ""}, so the whole block is REFUSED and not sent. Forwarding it would fail as an opaque AWS validation error naming neither cdkd nor the property. Set WarmThroughput.ReadUnitsPerSecond / WriteUnitsPerSecond to numbers, or remove the block. Nothing else about the index is affected.`
|
|
22162
|
+
message: `WarmThroughput is declared but no member resolved to a number${dropped ? ` (${dropped})` : ""}, so the whole block is REFUSED and not sent. Forwarding it would fail as an opaque AWS validation error naming neither cdkd nor the property. Set WarmThroughput.ReadUnitsPerSecond / WriteUnitsPerSecond to numbers, or remove the block. Nothing else about the index is affected. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way.`
|
|
22155
22163
|
};
|
|
22156
22164
|
}
|
|
22157
22165
|
/**
|
|
@@ -22165,9 +22173,10 @@ function warmThroughputDiagnostic(raw, coercion, indexName) {
|
|
|
22165
22173
|
* the pre-fix code forwarded such a block VERBATIM. Three defects followed, and fixing any subset leaves a
|
|
22166
22174
|
* live one, which is why this helper does all three at once:
|
|
22167
22175
|
*
|
|
22168
|
-
* 1. **Coercion.** The verbatim forward was the ONE path that skipped
|
|
22169
|
-
*
|
|
22170
|
-
*
|
|
22176
|
+
* 1. **Coercion.** The verbatim forward was the ONE path that skipped the
|
|
22177
|
+
* numeric reader (`coerceCfnInteger` since issue #3135), so a
|
|
22178
|
+
* stringly-typed CFn `"5"` reached the SDK unnormalized while every
|
|
22179
|
+
* derived value was a number.
|
|
22171
22180
|
* 2. **Per-member merge.** Whole-block replacement meant a PARTIAL explicit
|
|
22172
22181
|
* block SUPPRESSED valid derived siblings — an explicit
|
|
22173
22182
|
* `{MaxReadRequestUnits: 41}` discarded a perfectly good
|
|
@@ -22194,16 +22203,17 @@ function mergeExplicitThroughputBlock(explicit, derived, members, diagnostics, c
|
|
|
22194
22203
|
const out = {};
|
|
22195
22204
|
for (const member of members) {
|
|
22196
22205
|
if (explicit && explicit[member] !== void 0) {
|
|
22197
|
-
const coerced =
|
|
22206
|
+
const coerced = coerceCfnInteger(explicit[member]);
|
|
22198
22207
|
if (coerced !== void 0) {
|
|
22199
22208
|
out[member] = coerced;
|
|
22200
22209
|
continue;
|
|
22201
22210
|
}
|
|
22211
|
+
const standIn = derived?.[member];
|
|
22202
22212
|
diagnostics?.push({
|
|
22203
22213
|
kind: "unresolved-member",
|
|
22204
22214
|
...context.indexName !== void 0 && { indexName: context.indexName },
|
|
22205
22215
|
member,
|
|
22206
|
-
message: `${context.blockName}.${member} is present but did not resolve to a number (${JSON.stringify(maskDeep(explicit[member], maskSecrets))?.slice(0, 80)}), so it was ignored. This is usually an unresolved intrinsic; resolve it to a literal
|
|
22216
|
+
message: `${context.blockName}.${member} is present but did not resolve to a number (${JSON.stringify(maskDeep(explicit[member], maskSecrets))?.slice(0, 80)}), so ` + (typeof standIn === "number" ? `it was ignored and ${standIn} was sent instead. ` : `it was ignored. `) + "This is usually an unresolved intrinsic; resolve it to a literal. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way."
|
|
22207
22217
|
});
|
|
22208
22218
|
}
|
|
22209
22219
|
const derivedValue = derived?.[member];
|
|
@@ -22227,12 +22237,12 @@ function mergeExplicitThroughputBlock(explicit, derived, members, diagnostics, c
|
|
|
22227
22237
|
function reportUnresolvedRawMember(rawValue, diagnostics, context, maskSecrets = (text) => text) {
|
|
22228
22238
|
if (!diagnostics) return;
|
|
22229
22239
|
if (rawValue === void 0) return;
|
|
22230
|
-
if (
|
|
22240
|
+
if (coerceCfnInteger(rawValue) !== void 0) return;
|
|
22231
22241
|
diagnostics.push({
|
|
22232
22242
|
kind: "unresolved-member",
|
|
22233
22243
|
...context.indexName !== void 0 && { indexName: context.indexName },
|
|
22234
22244
|
member: context.member,
|
|
22235
|
-
message: `${context.blockName}.${context.member} is declared but did not resolve to a number (${JSON.stringify(maskDeep(rawValue, maskSecrets))?.slice(0, 80)}), so no throughput value was sent for it and the live AWS setting was left unchanged. This is usually an unresolved intrinsic; resolve it to a literal to apply the limit, or remove the property entirely to clear it.`
|
|
22245
|
+
message: `${context.blockName}.${context.member} is declared but did not resolve to a number (${JSON.stringify(maskDeep(rawValue, maskSecrets))?.slice(0, 80)}), so no throughput value was sent for it and the live AWS setting was left unchanged. This is usually an unresolved intrinsic; resolve it to a literal to apply the limit, or remove the property entirely to clear it. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way.`
|
|
22236
22246
|
});
|
|
22237
22247
|
}
|
|
22238
22248
|
/**
|
|
@@ -22320,7 +22330,7 @@ function reportUnresolvedProvisionedCapacity(rawSettings, side, source, outcome,
|
|
|
22320
22330
|
kind: "unresolved-member",
|
|
22321
22331
|
...context.indexName !== void 0 && { indexName: context.indexName },
|
|
22322
22332
|
member: blame.relPath ? blame.relPath.slice(blame.relPath.lastIndexOf(".") + 1) : context.blockName.slice(context.blockName.lastIndexOf(".") + 1),
|
|
22323
|
-
message: `${path} is declared but did not resolve to a number (${JSON.stringify(maskDeep(blame.value, maskSecrets))?.slice(0, 80)}), ${consequence}. This is usually an unresolved intrinsic; resolve it to a literal to apply the capacity you declared.`
|
|
22333
|
+
message: `${path} is declared but did not resolve to a number (${JSON.stringify(maskDeep(blame.value, maskSecrets))?.slice(0, 80)}), ${consequence}. This is usually an unresolved intrinsic; resolve it to a literal to apply the capacity you declared. CloudFormation types this member as an Integer and accepts only an optional sign and decimal digits (no surrounding whitespace, hex, exponent or decimal point), so a spelling it would reject is treated the same way.`
|
|
22324
22334
|
});
|
|
22325
22335
|
}
|
|
22326
22336
|
/**
|
|
@@ -22347,7 +22357,7 @@ function reportUnresolvedProvisionedCapacity(rawSettings, side, source, outcome,
|
|
|
22347
22357
|
*/
|
|
22348
22358
|
function deriveWriteCapacityUnits(settings, source = "min") {
|
|
22349
22359
|
if (!settings) return void 0;
|
|
22350
|
-
const literal =
|
|
22360
|
+
const literal = coerceCfnInteger(settings["WriteCapacityUnits"]);
|
|
22351
22361
|
if (literal !== void 0) return literal;
|
|
22352
22362
|
const autoScaling = asRecord$1(settings["WriteCapacityAutoScalingSettings"]);
|
|
22353
22363
|
if (!autoScaling) return void 0;
|
|
@@ -22363,7 +22373,7 @@ function deriveWriteCapacityUnits(settings, source = "min") {
|
|
|
22363
22373
|
*/
|
|
22364
22374
|
function deriveReadCapacityUnits(settings, source = "min") {
|
|
22365
22375
|
if (!settings) return void 0;
|
|
22366
|
-
const literal =
|
|
22376
|
+
const literal = coerceCfnInteger(settings["ReadCapacityUnits"]);
|
|
22367
22377
|
if (literal !== void 0) return literal;
|
|
22368
22378
|
const autoScaling = asRecord$1(settings["ReadCapacityAutoScalingSettings"]);
|
|
22369
22379
|
if (!autoScaling) return void 0;
|
|
@@ -22371,9 +22381,9 @@ function deriveReadCapacityUnits(settings, source = "min") {
|
|
|
22371
22381
|
}
|
|
22372
22382
|
/** Resolve a `*CapacityAutoScalingSettings` block to one number. */
|
|
22373
22383
|
function pickAutoScalingCapacity(autoScaling, source) {
|
|
22374
|
-
const min =
|
|
22375
|
-
if (source === "min") return min ??
|
|
22376
|
-
return
|
|
22384
|
+
const min = coerceCfnInteger(autoScaling["MinCapacity"]);
|
|
22385
|
+
if (source === "min") return min ?? coerceCfnInteger(autoScaling["SeedCapacity"]);
|
|
22386
|
+
return coerceCfnInteger(autoScaling["SeedCapacity"]) ?? min;
|
|
22377
22387
|
}
|
|
22378
22388
|
/**
|
|
22379
22389
|
* Translate the CFn `AWS::DynamoDB::GlobalTable` `GlobalSecondaryIndexes[]`
|
|
@@ -22470,7 +22480,7 @@ function toSdkGlobalSecondaryIndexes(properties, region, billingMode, source = "
|
|
|
22470
22480
|
const rawWrite = gsi["WriteProvisionedThroughputSettings"];
|
|
22471
22481
|
const derivedRead = deriveReadCapacityUnits(asRecord$1(rawLocalRead), source) ?? deriveReadCapacityUnits(asRecord$1(rawGsiRead), source);
|
|
22472
22482
|
const derivedWrite = deriveWriteCapacityUnits(asRecord$1(rawWrite), source);
|
|
22473
|
-
const explicitCovers = (member) => explicitProvisioned !== void 0 &&
|
|
22483
|
+
const explicitCovers = (member) => explicitProvisioned !== void 0 && coerceCfnInteger(explicitProvisioned[member]) !== void 0;
|
|
22474
22484
|
if (derivedRead === void 0 && !explicitCovers("ReadCapacityUnits")) {
|
|
22475
22485
|
const fromReplica = rawLocalRead !== void 0;
|
|
22476
22486
|
reportUnresolvedProvisionedCapacity(fromReplica ? rawLocalRead : rawGsiRead, "Read", source, { appliedUnits: DEFAULT_CAPACITY_UNITS }, diagnostics, {
|
|
@@ -22503,8 +22513,8 @@ function toSdkGlobalSecondaryIndexes(properties, region, billingMode, source = "
|
|
|
22503
22513
|
blockName: rawReadLocal !== void 0 ? "Replicas[local].GlobalSecondaryIndexes[].ReadOnDemandThroughputSettings" : "ReadOnDemandThroughputSettings",
|
|
22504
22514
|
member: "MaxReadRequestUnits"
|
|
22505
22515
|
}, maskSecrets);
|
|
22506
|
-
const maxWrite =
|
|
22507
|
-
const maxRead =
|
|
22516
|
+
const maxWrite = coerceCfnInteger(rawWrite);
|
|
22517
|
+
const maxRead = coerceCfnInteger(rawReadLocal) ?? coerceCfnInteger(rawReadGsi);
|
|
22508
22518
|
const merged = mergeExplicitThroughputBlock(explicitOnDemand, {
|
|
22509
22519
|
...maxRead !== void 0 && { MaxReadRequestUnits: maxRead },
|
|
22510
22520
|
...maxWrite !== void 0 && { MaxWriteRequestUnits: maxWrite }
|
|
@@ -22797,7 +22807,7 @@ function isUnresolvedIntrinsicBlock(value) {
|
|
|
22797
22807
|
* The per-GSI reset needs to know "did the template DROP this member", and the
|
|
22798
22808
|
* only faithful answer is whether the CFn key is THERE — not whether its value
|
|
22799
22809
|
* coerced. `toSdkGlobalSecondaryIndexes` runs every value through
|
|
22800
|
-
*
|
|
22810
|
+
* `coerceCfnInteger`, which returns `undefined` for a present-but-
|
|
22801
22811
|
* unparseable value (an unresolved `{Ref: …}`, `''`, an object). Deciding
|
|
22802
22812
|
* "dropped" from that collapsed view sent the `-1` reset for a member the
|
|
22803
22813
|
* template was actively trying to SET, silently clearing the ceiling — the same
|
|
@@ -22914,8 +22924,8 @@ function collectUncomparableCapacityGsiNames(properties, region) {
|
|
|
22914
22924
|
continue;
|
|
22915
22925
|
}
|
|
22916
22926
|
const explicitProvisioned = asRecord$1(gsi["ProvisionedThroughput"]);
|
|
22917
|
-
const derivedRead =
|
|
22918
|
-
const derivedWrite =
|
|
22927
|
+
const derivedRead = coerceCfnInteger(explicitProvisioned?.["ReadCapacityUnits"]) ?? deriveReadCapacityUnits(asRecord$1(readBlocks[0])) ?? deriveReadCapacityUnits(asRecord$1(readBlocks[1]));
|
|
22928
|
+
const derivedWrite = coerceCfnInteger(explicitProvisioned?.["WriteCapacityUnits"]) ?? deriveWriteCapacityUnits(asRecord$1(writeBlock));
|
|
22919
22929
|
if (derivedRead === void 0 || derivedWrite === void 0) out.add(name);
|
|
22920
22930
|
}
|
|
22921
22931
|
return out;
|
|
@@ -23068,7 +23078,7 @@ function toSdkReplicaThroughputOverrides(cfn, billingMode, diagnostics, context,
|
|
|
23068
23078
|
if (billingMode === "PROVISIONED") {
|
|
23069
23079
|
const rawRead = cfn["ReadProvisionedThroughputSettings"];
|
|
23070
23080
|
const readCapacity = deriveReadCapacityUnits(asRecord$1(rawRead));
|
|
23071
|
-
if (readCapacity === void 0 &&
|
|
23081
|
+
if (readCapacity === void 0 && coerceCfnInteger(explicitProvisioned?.["ReadCapacityUnits"]) === void 0) reportUnresolvedProvisionedCapacity(rawRead, "Read", "min", { suppressed: "so no ProvisionedThroughputOverride was sent and the replica keeps the source table's read capacity" }, diagnostics, {
|
|
23072
23082
|
...context,
|
|
23073
23083
|
blockName: "ReadProvisionedThroughputSettings"
|
|
23074
23084
|
}, maskSecrets);
|
|
@@ -23084,7 +23094,7 @@ function toSdkReplicaThroughputOverrides(cfn, billingMode, diagnostics, context,
|
|
|
23084
23094
|
blockName: "ReadOnDemandThroughputSettings",
|
|
23085
23095
|
member: "MaxReadRequestUnits"
|
|
23086
23096
|
}, maskSecrets);
|
|
23087
|
-
const maxReadRequestUnits =
|
|
23097
|
+
const maxReadRequestUnits = coerceCfnInteger(rawMaxRead);
|
|
23088
23098
|
const merged = mergeExplicitThroughputBlock(explicitOnDemand, maxReadRequestUnits !== void 0 ? { MaxReadRequestUnits: maxReadRequestUnits } : void 0, ["MaxReadRequestUnits"], diagnostics, {
|
|
23089
23099
|
...context,
|
|
23090
23100
|
blockName: "OnDemandThroughputOverride"
|
|
@@ -23127,11 +23137,11 @@ function collectTableOnDemandCeilings(properties, region, diagnostics, maskSecre
|
|
|
23127
23137
|
return {
|
|
23128
23138
|
read: {
|
|
23129
23139
|
declared: rawRead !== void 0 || isUnresolvedIntrinsicBlock(readBlock),
|
|
23130
|
-
value:
|
|
23140
|
+
value: coerceCfnInteger(rawRead)
|
|
23131
23141
|
},
|
|
23132
23142
|
write: {
|
|
23133
23143
|
declared: rawWrite !== void 0 || isUnresolvedIntrinsicBlock(writeBlock),
|
|
23134
|
-
value:
|
|
23144
|
+
value: coerceCfnInteger(rawWrite)
|
|
23135
23145
|
}
|
|
23136
23146
|
};
|
|
23137
23147
|
}
|
|
@@ -26555,6 +26565,19 @@ const SG_INGRESS_IP_PROTOCOL_DEFAULT = "-1";
|
|
|
26555
26565
|
/** The refusal path both the create guard and the canonicalizer report under. */
|
|
26556
26566
|
const SG_INGRESS_IP_PROTOCOL_PATH = "AWS::EC2::SecurityGroupIngress IpProtocol";
|
|
26557
26567
|
/**
|
|
26568
|
+
* Bounded eventual-consistency retry for the `DescribeSecurityGroups`
|
|
26569
|
+
* read-back (`readSecurityGroupVpcId`, issue #3097): an answer
|
|
26570
|
+
* `isNotFoundError` classifies as not-found is retried, everything else omits
|
|
26571
|
+
* the attribute at once. Five ATTEMPTS (four retries) a second apart cover the
|
|
26572
|
+
* propagation window EC2 documents for its read APIs;
|
|
26573
|
+
* `securityGroupReadbackDelays.sleep` is the unit-test seam (the pattern
|
|
26574
|
+
* `deleteTableRetryDelays` uses), and the fallback is the provider's own
|
|
26575
|
+
* `sleep`, so the class has one timer to fake.
|
|
26576
|
+
*/
|
|
26577
|
+
const SG_READBACK_NOT_FOUND_ATTEMPTS = 5;
|
|
26578
|
+
const SG_READBACK_RETRY_DELAY_MS = 1e3;
|
|
26579
|
+
const securityGroupReadbackDelays = {};
|
|
26580
|
+
/**
|
|
26558
26581
|
* Page ceiling for {@link EC2Provider.lookupIngressRuleId}'s
|
|
26559
26582
|
* `DescribeSecurityGroupRules` walk (issue #1761).
|
|
26560
26583
|
*
|
|
@@ -27808,10 +27831,10 @@ var EC2Provider = class {
|
|
|
27808
27831
|
this.logger.debug(`Successfully created SecurityGroup ${logicalId}: ${groupId}`);
|
|
27809
27832
|
return {
|
|
27810
27833
|
physicalId: groupId,
|
|
27811
|
-
attributes: {
|
|
27834
|
+
attributes: definedAttributes({
|
|
27812
27835
|
GroupId: groupId,
|
|
27813
|
-
VpcId:
|
|
27814
|
-
}
|
|
27836
|
+
VpcId: await this.readSecurityGroupVpcId(groupId)
|
|
27837
|
+
})
|
|
27815
27838
|
};
|
|
27816
27839
|
} catch (error) {
|
|
27817
27840
|
const cause = error instanceof Error ? error : void 0;
|
|
@@ -27901,10 +27924,10 @@ var EC2Provider = class {
|
|
|
27901
27924
|
return {
|
|
27902
27925
|
physicalId,
|
|
27903
27926
|
wasReplaced: false,
|
|
27904
|
-
attributes: {
|
|
27927
|
+
attributes: definedAttributes({
|
|
27905
27928
|
GroupId: physicalId,
|
|
27906
|
-
VpcId:
|
|
27907
|
-
}
|
|
27929
|
+
VpcId: await this.readSecurityGroupVpcId(physicalId)
|
|
27930
|
+
})
|
|
27908
27931
|
};
|
|
27909
27932
|
} catch (error) {
|
|
27910
27933
|
const cause = error instanceof Error ? error : void 0;
|
|
@@ -27968,17 +27991,72 @@ var EC2Provider = class {
|
|
|
27968
27991
|
}
|
|
27969
27992
|
}));
|
|
27970
27993
|
}
|
|
27971
|
-
|
|
27972
|
-
|
|
27973
|
-
|
|
27974
|
-
|
|
27975
|
-
|
|
27976
|
-
|
|
27977
|
-
|
|
27978
|
-
|
|
27994
|
+
/**
|
|
27995
|
+
* The `VpcId` a security group's `attributes` map records, read back from
|
|
27996
|
+
* `DescribeSecurityGroups` on the group id (issue #3097).
|
|
27997
|
+
*
|
|
27998
|
+
* The two `AWS::EC2::SecurityGroup` result maps used to copy the TEMPLATE's
|
|
27999
|
+
* own `VpcId` property with a `?? ''` fallback — the last two sites issue
|
|
28000
|
+
* #3077 left on that shape. A `CfnSecurityGroup` declared without `VpcId`
|
|
28001
|
+
* lands in the account's default VPC, so the template has nothing to copy
|
|
28002
|
+
* and the record held `''` where CloudFormation answers the default VPC's
|
|
28003
|
+
* id. The read-back answers both shapes the same way: `CreateSecurityGroup`
|
|
28004
|
+
* returns only the group id, and the describe reports `VpcId` for an
|
|
28005
|
+
* explicit VPC and for the default one alike.
|
|
28006
|
+
*
|
|
28007
|
+
* Best-effort by design: the group is already created (or updated) and
|
|
28008
|
+
* wired when this runs, so a failed or empty read must not fail the
|
|
28009
|
+
* resource. It answers `undefined` then, which `definedAttributes` turns
|
|
28010
|
+
* into an ABSENT key — never `''` — and the resolver's
|
|
28011
|
+
* `AWS::EC2::SecurityGroup` `VpcId` arm re-reads it live on the next
|
|
28012
|
+
* `Fn::GetAtt`. The error CLASS (and HTTP status when the SDK attached one)
|
|
28013
|
+
* is what the debug line names at its head; AWS's own text follows it on the
|
|
28014
|
+
* same debug line, since a denied describe quotes the caller's account, role
|
|
28015
|
+
* and session (`.claude/rules/provider-resource-identity.md`).
|
|
28016
|
+
*
|
|
28017
|
+
* EC2's read API is eventually consistent: a `DescribeSecurityGroups` issued
|
|
28018
|
+
* right after `CreateSecurityGroup` can answer `InvalidGroup.NotFound` for a
|
|
28019
|
+
* group the write path already accepted tags and rules for (#3139 review).
|
|
28020
|
+
* An answer `isNotFoundError` classifies as not-found (a substring
|
|
28021
|
+
* predicate — on this API that is `InvalidGroup.NotFound`) is therefore
|
|
28022
|
+
* retried a bounded number of times (`SG_READBACK_NOT_FOUND_ATTEMPTS`,
|
|
28023
|
+
* `SG_READBACK_RETRY_DELAY_MS` apart) before the key is omitted; every
|
|
28024
|
+
* other failure, and an EMPTY answer, omits at once. On the update path a
|
|
28025
|
+
* group deleted out of band takes the same bounded wait and then omits —
|
|
28026
|
+
* its own rule / tag calls MAY already have failed, but an update whose
|
|
28027
|
+
* bags agree issues none, and the rollback replay / `drift --revert` reach
|
|
28028
|
+
* `updateSecurityGroup` too, so this read can be the first call to touch
|
|
28029
|
+
* it. `getSecurityGroupAttribute` (the orphan rewriter's live fetch) shares
|
|
28030
|
+
* the wait. The sleep itself is inside the best-effort boundary: a
|
|
28031
|
+
* rejecting sleep (only a test seam can reject) omits like a failed read.
|
|
28032
|
+
*/
|
|
28033
|
+
async readSecurityGroupVpcId(groupId) {
|
|
28034
|
+
for (let attempt = 1;; attempt++) try {
|
|
28035
|
+
const vpcId = (await this.ec2Client.send(new DescribeSecurityGroupsCommand({ GroupIds: [groupId] })))?.SecurityGroups?.[0]?.VpcId;
|
|
28036
|
+
if (vpcId === void 0) this.logger.debug(`DescribeSecurityGroups reported no VpcId for SecurityGroup ${groupId}; omitting the attribute`);
|
|
28037
|
+
return vpcId;
|
|
28038
|
+
} catch (error) {
|
|
28039
|
+
const name = error instanceof Error && error.name ? error.name : "Error";
|
|
28040
|
+
const status = error?.$metadata?.httpStatusCode;
|
|
28041
|
+
const errorClass = typeof status === "number" ? `${name}, HTTP ${status}` : name;
|
|
28042
|
+
if (this.isNotFoundError(error) && attempt < 5) {
|
|
28043
|
+
this.logger.debug(`DescribeSecurityGroups for SecurityGroup ${groupId} answered not-found (${errorClass}) on attempt ${attempt}/${5}; retrying in ${SG_READBACK_RETRY_DELAY_MS}ms (eventual consistency)`);
|
|
28044
|
+
try {
|
|
28045
|
+
await (securityGroupReadbackDelays.sleep ?? ((ms) => this.sleep(ms)))(SG_READBACK_RETRY_DELAY_MS);
|
|
28046
|
+
} catch {
|
|
28047
|
+
this.logger.debug(`DescribeSecurityGroups retry sleep for SecurityGroup ${groupId} rejected; omitting the VpcId attribute`);
|
|
28048
|
+
return;
|
|
28049
|
+
}
|
|
28050
|
+
continue;
|
|
28051
|
+
}
|
|
28052
|
+
this.logger.debug(`DescribeSecurityGroups for SecurityGroup ${groupId} failed (${errorClass}); omitting the VpcId attribute: ${error instanceof Error ? error.message : String(error)}`);
|
|
27979
28053
|
return;
|
|
27980
28054
|
}
|
|
27981
28055
|
}
|
|
28056
|
+
async getSecurityGroupAttribute(physicalId, attributeName) {
|
|
28057
|
+
if (attributeName === "GroupId") return physicalId;
|
|
28058
|
+
if (attributeName === "VpcId") return this.readSecurityGroupVpcId(physicalId);
|
|
28059
|
+
}
|
|
27982
28060
|
/**
|
|
27983
28061
|
* @param onUnusableProtocol When supplied, a malformed `IpProtocol` WARNS and
|
|
27984
28062
|
* defaults instead of throwing. Passed by `updateSecurityGroupIngress`,
|
|
@@ -29671,10 +29749,17 @@ var EC2Provider = class {
|
|
|
29671
29749
|
physicalId,
|
|
29672
29750
|
attributes: {}
|
|
29673
29751
|
} : null;
|
|
29674
|
-
case "AWS::EC2::SecurityGroup":
|
|
29675
|
-
physicalId
|
|
29676
|
-
|
|
29677
|
-
|
|
29752
|
+
case "AWS::EC2::SecurityGroup": {
|
|
29753
|
+
const group = (await this.ec2Client.send(new DescribeSecurityGroupsCommand({ GroupIds: [physicalId] }))).SecurityGroups?.[0];
|
|
29754
|
+
if (!group) return null;
|
|
29755
|
+
return {
|
|
29756
|
+
physicalId,
|
|
29757
|
+
attributes: definedAttributes({
|
|
29758
|
+
GroupId: physicalId,
|
|
29759
|
+
VpcId: group.VpcId
|
|
29760
|
+
})
|
|
29761
|
+
};
|
|
29762
|
+
}
|
|
29678
29763
|
case "AWS::EC2::SecurityGroupIngress": {
|
|
29679
29764
|
if (!SG_RULE_ID_PATTERN$1.test(physicalId)) return null;
|
|
29680
29765
|
const rule = (await this.ec2Client.send(new DescribeSecurityGroupRulesCommand({ SecurityGroupRuleIds: [physicalId] }))).SecurityGroupRules?.[0];
|
|
@@ -63959,8 +64044,9 @@ const UNSUBSTITUTED_SUB_PLACEHOLDER = /\$\{[^}]*\}/;
|
|
|
63959
64044
|
*
|
|
63960
64045
|
* 1. `undefined` — the SAME signal the deploy side keys on. `resolve` returns it
|
|
63961
64046
|
* WITHOUT throwing for a constructible-but-unknown attribute
|
|
63962
|
-
* (`AWS::DynamoDB::Table.StreamArn`, `AWS::IAM::Policy.PolicyId`,
|
|
63963
|
-
* `AWS::EC2::SecurityGroup.VpcId
|
|
64047
|
+
* (`AWS::DynamoDB::Table.StreamArn`, `AWS::IAM::Policy.PolicyId`, ...;
|
|
64048
|
+
* `AWS::EC2::SecurityGroup.VpcId` left this list in #3097 — it now reads
|
|
64049
|
+
* the group live and REFUSES, which the catch below records). `JSON.stringify` drops an
|
|
63964
64050
|
* `undefined`-valued key, so state can never hold one and such an output
|
|
63965
64051
|
* would otherwise report a PERMANENT phantom `ADD` printing `new: undefined`.
|
|
63966
64052
|
* 2. A symbol — `Ref: AWS::NoValue` resolves to a sentinel symbol, which
|
|
@@ -68661,6 +68747,87 @@ function createScrubCommand() {
|
|
|
68661
68747
|
return cmd;
|
|
68662
68748
|
}
|
|
68663
68749
|
|
|
68750
|
+
//#endregion
|
|
68751
|
+
//#region src/utils/own-keys.ts
|
|
68752
|
+
/**
|
|
68753
|
+
* Own-key helpers for the bags that reach cdkd through `JSON.parse` — a
|
|
68754
|
+
* `state.json` record, a Cloud Control `Properties` document, a template — and
|
|
68755
|
+
* for the raw SDK readbacks compared against them.
|
|
68756
|
+
*
|
|
68757
|
+
* `JSON.parse` yields a key literally named `__proto__` as an ORDINARY own
|
|
68758
|
+
* data property, and every comparator / rebuild walk over such a bag has to
|
|
68759
|
+
* keep treating it as one. Two idioms silently do not:
|
|
68760
|
+
*
|
|
68761
|
+
* - **Rebuilding onto a `{}` literal.** `out[k] = v` with `k === '__proto__'`
|
|
68762
|
+
* runs `Object.prototype`'s accessor, SETS the node's prototype and DROPS
|
|
68763
|
+
* the key — on BOTH comparison sides when the walk is a canonicalizer, so a
|
|
68764
|
+
* drift at that key is invisible (issue
|
|
68765
|
+
* [#3121](https://github.com/go-to-k/cdkd/issues/3121)), and on the WRITE
|
|
68766
|
+
* path the member vanishes from the payload (issue
|
|
68767
|
+
* [#2899](https://github.com/go-to-k/cdkd/issues/2899)). Rebuild onto
|
|
68768
|
+
* {@link nullPrototypeRecord} (no accessor there, so plain assignment
|
|
68769
|
+
* defines an own key) or, onto an existing plain object, define the key
|
|
68770
|
+
* with {@link defineOwnKey}.
|
|
68771
|
+
* - **`key in bag` membership.** `in` reads the prototype chain, so a key
|
|
68772
|
+
* named `constructor` / `toString` answers `true` for every plain object
|
|
68773
|
+
* and the recursion then walks `Object`'s function. {@link hasOwnKey} asks
|
|
68774
|
+
* the bag alone; {@link ownValue} is the matching read.
|
|
68775
|
+
*
|
|
68776
|
+
* And ONE shape test every such walk needs FIRST: {@link hasPlainPrototype}.
|
|
68777
|
+
* A non-plain object (`Date`, `Uint8Array`, `Map`, a class instance) has no
|
|
68778
|
+
* own enumerable keys, so a key-walk over one FABRICATES `{}` (or an index
|
|
68779
|
+
* map) and any two of them compare equal. Such a value is returned by
|
|
68780
|
+
* IDENTITY and compared as itself.
|
|
68781
|
+
*
|
|
68782
|
+
* Extracted from `src/cli/commands/drift.ts` (issue #2899, swept by PR #3124)
|
|
68783
|
+
* so the analyzer-side canonicalizers (#3121) share ONE spelling rather than a
|
|
68784
|
+
* fourth copy. A LEAF module, deliberately, and it must stay one: a value
|
|
68785
|
+
* import from a module other suites `vi.mock` wholesale reds those suites
|
|
68786
|
+
* with a missing-export failure (which is why `secret-redaction.ts` keeps its
|
|
68787
|
+
* own `hasPlainPrototype` — several drift suites mock that module).
|
|
68788
|
+
*/
|
|
68789
|
+
/** Own-key membership: `in` reads the prototype chain, which a `JSON.parse`d bag never means. */
|
|
68790
|
+
function hasOwnKey(target, key) {
|
|
68791
|
+
return Object.prototype.hasOwnProperty.call(target, key);
|
|
68792
|
+
}
|
|
68793
|
+
/** An own-key read: `undefined` for an inherited name (`__proto__`, `constructor`, ...). */
|
|
68794
|
+
function ownValue(target, key) {
|
|
68795
|
+
return hasOwnKey(target, key) ? target[key] : void 0;
|
|
68796
|
+
}
|
|
68797
|
+
/** An ordinary (writable, enumerable, configurable) own data property — never the prototype. */
|
|
68798
|
+
function defineOwnKey(target, key, value) {
|
|
68799
|
+
Object.defineProperty(target, key, {
|
|
68800
|
+
value,
|
|
68801
|
+
writable: true,
|
|
68802
|
+
enumerable: true,
|
|
68803
|
+
configurable: true
|
|
68804
|
+
});
|
|
68805
|
+
}
|
|
68806
|
+
/**
|
|
68807
|
+
* Does this value carry an ORDINARY object prototype?
|
|
68808
|
+
*
|
|
68809
|
+
* `typeof v === 'object'` admits `Date` / `Map` / `Set` / `Uint8Array` / class
|
|
68810
|
+
* instances, whose own enumerable keys are `[]` — so a key-walk over one
|
|
68811
|
+
* reports "no contradiction" between two values that actually differ, and a
|
|
68812
|
+
* rebuild over one fabricates `{}`. A NULL prototype counts as plain: it is
|
|
68813
|
+
* what {@link nullPrototypeRecord} yields, and a walk must be able to re-walk
|
|
68814
|
+
* its own output.
|
|
68815
|
+
*/
|
|
68816
|
+
function hasPlainPrototype(value) {
|
|
68817
|
+
const proto = Object.getPrototypeOf(value);
|
|
68818
|
+
return proto === Object.prototype || proto === null;
|
|
68819
|
+
}
|
|
68820
|
+
/**
|
|
68821
|
+
* A fresh rebuild target with NO prototype, so `out['__proto__'] = v` defines
|
|
68822
|
+
* an own key instead of setting the prototype. `JSON.stringify`,
|
|
68823
|
+
* `Object.keys` / `entries` and `structuredClone` all read it like a plain
|
|
68824
|
+
* object; only `instanceof Object` and a direct `.hasOwnProperty()` method call
|
|
68825
|
+
* do not, and no consumer on the drift path uses either.
|
|
68826
|
+
*/
|
|
68827
|
+
function nullPrototypeRecord() {
|
|
68828
|
+
return Object.create(null);
|
|
68829
|
+
}
|
|
68830
|
+
|
|
68664
68831
|
//#endregion
|
|
68665
68832
|
//#region src/analyzer/drift-normalize.ts
|
|
68666
68833
|
/**
|
|
@@ -68693,6 +68860,24 @@ function createScrubCommand() {
|
|
|
68693
68860
|
* `properties`-fallback baseline (a resource deployed before observed-capture,
|
|
68694
68861
|
* whose baseline is the user's TEMPLATE order). Sorting only the AWS read side
|
|
68695
68862
|
* would manufacture drift on exactly that path.
|
|
68863
|
+
*
|
|
68864
|
+
* Every object arm below returns a NON-PLAIN object (a `Date` the raw SDK
|
|
68865
|
+
* readback carries, a `Uint8Array`) BY IDENTITY and rebuilds a plain one onto
|
|
68866
|
+
* a NULL-prototype record (issue
|
|
68867
|
+
* [#3121](https://github.com/go-to-k/cdkd/issues/3121), the analyzer-side
|
|
68868
|
+
* population of the class PR #3124 closed in `drift.ts`). A `{}` literal
|
|
68869
|
+
* rebuild had two consequences, both on the COMPARISON copies: an own
|
|
68870
|
+
* `__proto__` key (what `JSON.parse` yields from `state.json`) became the
|
|
68871
|
+
* rebuilt node's prototype and was dropped on BOTH sides, so a drift at that
|
|
68872
|
+
* key was invisible; and `Object.entries(new Date())` is `[]`, so a `Date`
|
|
68873
|
+
* member was flattened to `{}` before `calculateResourceDrift` could see it
|
|
68874
|
+
* (phantom drift against an ISO-string baseline, and `--accept` persisted the
|
|
68875
|
+
* `{}`). The identity return is what makes the second visible to the
|
|
68876
|
+
* comparator; the null-prototype target is what keeps the first an own key.
|
|
68877
|
+
* One layer LATER the same flattening survives in `secret-redaction.ts`'s
|
|
68878
|
+
* value walk, which `--accept` runs over a resource that RECORDS a secret
|
|
68879
|
+
* (issue #2427) -- so the ISO string reaches the baseline only where that
|
|
68880
|
+
* walk does not run.
|
|
68696
68881
|
*/
|
|
68697
68882
|
function canonicalizeTagListsDeep(v) {
|
|
68698
68883
|
if (Array.isArray(v)) {
|
|
@@ -68706,7 +68891,8 @@ function canonicalizeTagListsDeep(v) {
|
|
|
68706
68891
|
return mapped;
|
|
68707
68892
|
}
|
|
68708
68893
|
if (v && typeof v === "object") {
|
|
68709
|
-
|
|
68894
|
+
if (!hasPlainPrototype(v)) return v;
|
|
68895
|
+
const out = nullPrototypeRecord();
|
|
68710
68896
|
for (const [k, val] of Object.entries(v)) out[k] = canonicalizeTagListsDeep(val);
|
|
68711
68897
|
return out;
|
|
68712
68898
|
}
|
|
@@ -68721,7 +68907,8 @@ function canonicalizeIdArraysDeep(v) {
|
|
|
68721
68907
|
return mapped;
|
|
68722
68908
|
}
|
|
68723
68909
|
if (v && typeof v === "object") {
|
|
68724
|
-
|
|
68910
|
+
if (!hasPlainPrototype(v)) return v;
|
|
68911
|
+
const out = nullPrototypeRecord();
|
|
68725
68912
|
for (const [k, val] of Object.entries(v)) out[k] = canonicalizeIdArraysDeep(val);
|
|
68726
68913
|
return out;
|
|
68727
68914
|
}
|
|
@@ -68878,7 +69065,8 @@ function walkUnordered(v, path, paths) {
|
|
|
68878
69065
|
return mapped;
|
|
68879
69066
|
}
|
|
68880
69067
|
if (v && typeof v === "object") {
|
|
68881
|
-
|
|
69068
|
+
if (!hasPlainPrototype(v)) return v;
|
|
69069
|
+
const out = nullPrototypeRecord();
|
|
68882
69070
|
for (const [k, val] of Object.entries(v)) out[k] = walkUnordered(val, path === "" ? k : `${path}.${k}`, paths);
|
|
68883
69071
|
return out;
|
|
68884
69072
|
}
|
|
@@ -69021,14 +69209,16 @@ function isIgnoredPath(path, ignorePaths) {
|
|
|
69021
69209
|
* common case of "no drift" allocates nothing.
|
|
69022
69210
|
*/
|
|
69023
69211
|
function diffAt(path, stateValue, awsValue, out, ignorePaths, unionWalkObjects) {
|
|
69024
|
-
|
|
69212
|
+
const stateForm = jsonForm(stateValue);
|
|
69213
|
+
const awsForm = jsonForm(awsValue);
|
|
69214
|
+
if (deepEqual(stateForm, awsForm)) return;
|
|
69025
69215
|
if (typeof stateValue === "string" && stateValue.includes("{{resolve:")) return;
|
|
69026
|
-
if (isPlainObject(
|
|
69027
|
-
const keys = unionWalkObjects ? /* @__PURE__ */ new Set([...Object.keys(
|
|
69216
|
+
if (isPlainObject(stateForm) && isPlainObject(awsForm) && !Array.isArray(stateForm) && !Array.isArray(awsForm)) {
|
|
69217
|
+
const keys = unionWalkObjects ? /* @__PURE__ */ new Set([...Object.keys(stateForm), ...Object.keys(awsForm)]) : Object.keys(stateForm);
|
|
69028
69218
|
for (const key of keys) {
|
|
69029
69219
|
const childPath = `${path}.${key}`;
|
|
69030
69220
|
if (isIgnoredPath(childPath, ignorePaths)) continue;
|
|
69031
|
-
diffAt(childPath,
|
|
69221
|
+
diffAt(childPath, stateForm[key], awsForm[key], out, ignorePaths, unionWalkObjects);
|
|
69032
69222
|
}
|
|
69033
69223
|
return;
|
|
69034
69224
|
}
|
|
@@ -69039,11 +69229,39 @@ function diffAt(path, stateValue, awsValue, out, ignorePaths, unionWalkObjects)
|
|
|
69039
69229
|
});
|
|
69040
69230
|
}
|
|
69041
69231
|
/**
|
|
69232
|
+
* The form `JSON.stringify` would serialize a value as, for the ONE shape the
|
|
69233
|
+
* comparator's "JSON-roundtrip equality" contract did not cover: a NON-PLAIN
|
|
69234
|
+
* object carrying `toJSON` (a `Date`, which serializes as its ISO string; a
|
|
69235
|
+
* `Buffer`, whose prototype `toJSON` yields `{type, data}`). Every other value
|
|
69236
|
+
* is returned untouched. What the comparator then does with an untouched
|
|
69237
|
+
* non-plain object is what it does with a plain one -- it walks the OWN keys
|
|
69238
|
+
* (`deepEqual` below, and `diffAt`'s descend gate, test `typeof === 'object'`
|
|
69239
|
+
* and nothing about the prototype) -- so a `Uint8Array` compares by its
|
|
69240
|
+
* index keys (the persisted `{"0":1,"1":2}` equals the readback typed array,
|
|
69241
|
+
* the convergence this fold exists for) and a keyless `Map` / class instance
|
|
69242
|
+
* compares equal to `{}` and to any other keyless one. That last case is
|
|
69243
|
+
* PRE-EXISTING and is not what the canonicalizers' identity return protects:
|
|
69244
|
+
* they keep a non-plain node from being REBUILT as `{}`, and this fold keeps
|
|
69245
|
+
* the JSON-carrying ones from drifting forever against their persisted form;
|
|
69246
|
+
* neither adds a prototype guard here, because a bare `hasPlainPrototype`
|
|
69247
|
+
* gate would break the typed-array convergence (PR #3148 delta review).
|
|
69248
|
+
*/
|
|
69249
|
+
function jsonForm(value) {
|
|
69250
|
+
if (value === null || typeof value !== "object" || hasPlainPrototype(value)) return value;
|
|
69251
|
+
const toJSON = value.toJSON;
|
|
69252
|
+
return typeof toJSON === "function" ? toJSON.call(value) : value;
|
|
69253
|
+
}
|
|
69254
|
+
/**
|
|
69042
69255
|
* Structural equality used by the drift comparator. Identical to a
|
|
69043
69256
|
* plain `JSON.stringify`-roundtrip equality except it tolerates
|
|
69044
69257
|
* undefined-vs-missing-key gaps the same way (both serialize away).
|
|
69258
|
+
* `Date`s (and anything else non-plain with `toJSON`) are compared in their
|
|
69259
|
+
* JSON form via {@link jsonForm}, so the persisted ISO string of an accepted
|
|
69260
|
+
* readback equals the `Date` the next readback returns.
|
|
69045
69261
|
*/
|
|
69046
69262
|
function deepEqual(a, b) {
|
|
69263
|
+
a = jsonForm(a);
|
|
69264
|
+
b = jsonForm(b);
|
|
69047
69265
|
if (a === b) return true;
|
|
69048
69266
|
if (a === null || b === null || a === void 0 || b === void 0) return a === b;
|
|
69049
69267
|
if (typeof a !== typeof b) return false;
|
|
@@ -69126,6 +69344,12 @@ function isPlainObject(value) {
|
|
|
69126
69344
|
* and manufacturing a different phantom drift, so the pass is scoped to the
|
|
69127
69345
|
* object shape — which is what `AWS::S3::BucketPolicy` and the other
|
|
69128
69346
|
* `PolicyDocument`-typed properties carry in cdkd state.
|
|
69347
|
+
*
|
|
69348
|
+
* The two rewrite walks rebuild onto a NULL-prototype record and return a
|
|
69349
|
+
* non-plain object by identity, for the reason `drift-normalize.ts`'s header
|
|
69350
|
+
* gives (issue #3121): they run on the comparison copies of BOTH sides, so a
|
|
69351
|
+
* `{}` literal rebuild dropped an own `__proto__` key symmetrically and
|
|
69352
|
+
* flattened a `Date` to `{}` before the comparator saw it.
|
|
69129
69353
|
*/
|
|
69130
69354
|
/**
|
|
69131
69355
|
* An IAM unique id as it appears in a rendered policy. The prefix set is
|
|
@@ -69215,7 +69439,8 @@ function collectPrincipalForms(value) {
|
|
|
69215
69439
|
function rewritePrincipalUniqueIds(value, arnByUniqueId) {
|
|
69216
69440
|
if (Array.isArray(value)) return value.map((el) => rewritePrincipalUniqueIds(el, arnByUniqueId));
|
|
69217
69441
|
if (!value || typeof value !== "object") return value;
|
|
69218
|
-
|
|
69442
|
+
if (!hasPlainPrototype(value)) return value;
|
|
69443
|
+
const out = nullPrototypeRecord();
|
|
69219
69444
|
for (const [key, member] of Object.entries(value)) out[key] = key === "Principal" || key === "NotPrincipal" ? rewritePrincipalValue(member, arnByUniqueId) : rewritePrincipalUniqueIds(member, arnByUniqueId);
|
|
69220
69445
|
return out;
|
|
69221
69446
|
}
|
|
@@ -69224,7 +69449,8 @@ function rewritePrincipalValue(value, arnByUniqueId) {
|
|
|
69224
69449
|
if (typeof value === "string") return swap(value);
|
|
69225
69450
|
if (Array.isArray(value)) return value.map(swap);
|
|
69226
69451
|
if (!value || typeof value !== "object") return value;
|
|
69227
|
-
|
|
69452
|
+
if (!hasPlainPrototype(value)) return value;
|
|
69453
|
+
const out = nullPrototypeRecord();
|
|
69228
69454
|
for (const [key, member] of Object.entries(value)) {
|
|
69229
69455
|
if (key !== "AWS") {
|
|
69230
69456
|
out[key] = rewritePrincipalUniqueIds(member, arnByUniqueId);
|
|
@@ -69482,7 +69708,12 @@ const CC_API_FALLBACK_DENY_LIST = {
|
|
|
69482
69708
|
*
|
|
69483
69709
|
* Strips are shallow only — fields are removed if they appear at any
|
|
69484
69710
|
* depth (recursive walk) but the values themselves are not modified.
|
|
69485
|
-
* Arrays are walked element-wise.
|
|
69711
|
+
* Arrays are walked element-wise. The rebuild target is a NULL-prototype
|
|
69712
|
+
* record and a non-plain object is returned by identity, for the reason
|
|
69713
|
+
* `drift-normalize.ts`'s header gives (issue #3121): the walk feeds the
|
|
69714
|
+
* comparison copy, so a `{}` literal rebuild dropped an own `__proto__` key
|
|
69715
|
+
* (which the `JSON.parse`d `Properties` document can carry) and flattened a
|
|
69716
|
+
* non-plain member to `{}`.
|
|
69486
69717
|
*/
|
|
69487
69718
|
/**
|
|
69488
69719
|
* Field names AWS attaches to nearly every resource. These are NOT user
|
|
@@ -69538,7 +69769,8 @@ function stripWalk(value) {
|
|
|
69538
69769
|
if (value === null || value === void 0) return value;
|
|
69539
69770
|
if (Array.isArray(value)) return value.map(stripWalk);
|
|
69540
69771
|
if (typeof value === "object") {
|
|
69541
|
-
|
|
69772
|
+
if (!hasPlainPrototype(value)) return value;
|
|
69773
|
+
const out = nullPrototypeRecord();
|
|
69542
69774
|
for (const [key, child] of Object.entries(value)) {
|
|
69543
69775
|
if (ALWAYS_STRIPPED_FIELDS.has(key)) continue;
|
|
69544
69776
|
out[key] = stripWalk(child);
|
|
@@ -70949,10 +71181,10 @@ function buildReadCurrentStateContext(state, excludedLogicalId) {
|
|
|
70949
71181
|
* below would then compare against the accepted one. `undefined` is the answer
|
|
70950
71182
|
* "the bag has nothing here", which is what an absent segment means.
|
|
70951
71183
|
*
|
|
70952
|
-
* Exported, like `setAtPath`, as a test seam
|
|
70953
|
-
*
|
|
70954
|
-
*
|
|
70955
|
-
* the
|
|
71184
|
+
* Exported, like `setAtPath`, as a test seam: the own-key rule is pinned on
|
|
71185
|
+
* the helpers directly, and since issue #3121 (the comparison chain's
|
|
71186
|
+
* normalisers keep the key as an own key on both sides) a top-level
|
|
71187
|
+
* `__proto__` drift also reaches both through the command.
|
|
70956
71188
|
*/
|
|
70957
71189
|
function getAtPath(source, path) {
|
|
70958
71190
|
if (path.length === 0) return source;
|
|
@@ -70991,23 +71223,6 @@ function setAtPath(target, path, value) {
|
|
|
70991
71223
|
}
|
|
70992
71224
|
defineOwnKey(cursor, segments[segments.length - 1], value);
|
|
70993
71225
|
}
|
|
70994
|
-
/** Own-key membership: `in` reads the prototype chain, which a `JSON.parse`d bag never means. */
|
|
70995
|
-
function hasOwnKey(target, key) {
|
|
70996
|
-
return Object.prototype.hasOwnProperty.call(target, key);
|
|
70997
|
-
}
|
|
70998
|
-
/** An own-key read: `undefined` for an inherited name (`__proto__`, `constructor`, ...). */
|
|
70999
|
-
function ownValue(target, key) {
|
|
71000
|
-
return hasOwnKey(target, key) ? target[key] : void 0;
|
|
71001
|
-
}
|
|
71002
|
-
/** An ordinary (writable, enumerable, configurable) own data property — never the prototype. */
|
|
71003
|
-
function defineOwnKey(target, key, value) {
|
|
71004
|
-
Object.defineProperty(target, key, {
|
|
71005
|
-
value,
|
|
71006
|
-
writable: true,
|
|
71007
|
-
enumerable: true,
|
|
71008
|
-
configurable: true
|
|
71009
|
-
});
|
|
71010
|
-
}
|
|
71011
71226
|
/**
|
|
71012
71227
|
* `--accept`: state ← AWS.
|
|
71013
71228
|
*
|
|
@@ -71365,22 +71580,6 @@ function isPlainRecord(value) {
|
|
|
71365
71580
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
71366
71581
|
}
|
|
71367
71582
|
/**
|
|
71368
|
-
* Does this value carry an ORDINARY object prototype?
|
|
71369
|
-
*
|
|
71370
|
-
* {@link isPlainRecord} admits `Date` / `Map` / `Set` / class instances, whose
|
|
71371
|
-
* own enumerable keys are `[]` — so a key-walk over one reports "no
|
|
71372
|
-
* contradiction" between two values that actually differ. The redaction walk's
|
|
71373
|
-
* `deepEqualJsonValue` carries the same guard for the same reason
|
|
71374
|
-
* (`secret-redaction.ts`, issue #2869): a `Date` anchor corroborates NOTHING.
|
|
71375
|
-
* Kept file-local rather than imported because `secret-redaction.js` is
|
|
71376
|
-
* `vi.mock`ed by several drift suites, and a value import from it reds them
|
|
71377
|
-
* with a missing-export failure.
|
|
71378
|
-
*/
|
|
71379
|
-
function hasPlainPrototype(value) {
|
|
71380
|
-
const proto = Object.getPrototypeOf(value);
|
|
71381
|
-
return proto === Object.prototype || proto === null;
|
|
71382
|
-
}
|
|
71383
|
-
/**
|
|
71384
71583
|
* Merge the AWS-current value with the revert baseline, KEEPING every path the
|
|
71385
71584
|
* baseline does not declare (issue
|
|
71386
71585
|
* [#1626](https://github.com/go-to-k/cdkd/issues/1626) items 2 + 3).
|
|
@@ -80886,7 +81085,9 @@ var AttributeFetcher = class {
|
|
|
80886
81085
|
ok: false,
|
|
80887
81086
|
reason
|
|
80888
81087
|
};
|
|
80889
|
-
const
|
|
81088
|
+
const orphan = this.orphans[orphanLogicalId];
|
|
81089
|
+
const stored = orphan.attributes?.[attribute];
|
|
81090
|
+
const cached = isImpossibleEmptyStoredAttribute(orphan.resourceType, attribute, stored) ? void 0 : stored;
|
|
80890
81091
|
if (cached === void 0) {
|
|
80891
81092
|
this.logger.warn(`--force: state.attributes also lacks '${orphanLogicalId}.${attribute}'; leaving the original intrinsic in place.`);
|
|
80892
81093
|
return {
|
|
@@ -92457,4 +92658,4 @@ function buildProgram() {
|
|
|
92457
92658
|
|
|
92458
92659
|
//#endregion
|
|
92459
92660
|
export { buildProgram };
|
|
92460
|
-
//# sourceMappingURL=program-
|
|
92661
|
+
//# sourceMappingURL=program-D30EPjOn.js.map
|