@go-to-k/cdkd 0.290.32 → 0.290.34
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-BuEvX2d9.js → asg-provider-BTxYWlwu.js} +2 -2
- package/dist/{asg-provider-BuEvX2d9.js.map → asg-provider-BTxYWlwu.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-CfOsVRQB.js → deploy-engine-efiVJ-rX.js} +597 -71
- package/dist/deploy-engine-efiVJ-rX.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-DdmCUgOP.js → program-Dh1J573K.js} +474 -51
- package/dist/{program-DdmCUgOP.js.map → program-Dh1J573K.js.map} +1 -1
- package/dist/{version-F41ektvu.js → version-CpyoQ3PO.js} +2 -2
- package/dist/{version-F41ektvu.js.map → version-CpyoQ3PO.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-CfOsVRQB.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as isExportAliasCollision, $n as maskSecretsInText, $r as stripControlChars, $t as withSharedDrainBudget, A as makeCanonicalizePropertiesFn, Ai as AwsClients, An as refuseMalformedResourcesForDestroy, Ar as AssetModeResolver, At as getAccountInfo, B as isWarmThroughputDecrease, Bi as DynamicReferenceRegionAmbiguousError, Bn as TemplateParser, Br as buildDenyExternalAccessPolicy, Bt as assertRegionMatch, C as PRE_DELETE_SNAPSHOT_TYPES, Ci as PARTITION_TABLE, Cn as malformedStateRefusalMessage, Cr as AssetPublisher, Ct as disableInstanceApiTermination, D as isFinalSnapshotError, Di as processStackMessages, Dn as refuseMalformedOutputs, Dr as createAssetRedirectResolver, Dt as carriesFetchableDynamicReference, E as createPreDeleteFinalSnapshot, Ei as AssemblyReader, En as refuseMalformedNestedChildOutputs, Er as buildAssetRedirectMap, Et as carriesDynamicReference, F as isStatefulRecreateTargetForReplace, Fi as CdkdError, Fn as findActionableSilentDrops, Fr as isCrossRegionRedirect, Ft as refStateLookupFromResource, G as cyan, Gi as MalformedProducerRecordRefusalError, Gn as carriesSecretMask, Gr as dockerSpawnEnvWithSensitive, Gt as configStringRefusal, H as toFiniteNumber, Hi as LocalInvokeBuildError$1, Hn as STATE_SOURCED_CROSS_GENERATION_RULES, Hr as describeDockerCapturedOutput, Ht as coerceCfnInteger, I as isStatefulRecreateTargetSync, In as findSilentDropProperties, Ir as parseBootstrapMarker, It as isSettledInstanceState, J as red, Ji as ProvisioningError, Jn as errorCauseChain, Jr as partitionSensitiveEnv, Jt as requireConfigArray, K as gray, Ki as NestedStackChildDirectDestroyError, Kn as createSecretMasker, Kr as formatDockerLoginError, Kt as readConfigString, L as renderStatefulReason, Li as CrossAccountSecretRefusalError, Ln as describeTypeWithThrottleRetry, Lr as readBootstrapMarkerBody, Lt as WAFv2WebACLProvider, M as makeSiblingClaimReader, Mi as resetAwsClients, Mn as repairMalformedOutputsForReadOnly, Mr as assertAssetBucketRegion, Mt as isNamelessDynamicReferenceError, N as planOrphanAdoption, Ni as setAwsClients, Nn as repairMalformedResourcePropertiesForReadOnly, Nr as ensureAssetStorage, Nt as isUnboundTemplateParameter, O as refusesFinalSnapshot, On as refuseMalformedOutputsForDestroy, Or as loadPublishableAssetManifest, Ot as cfnRefValueFromPhysicalId, P as MULTI_REGION_RECREATE_BLOCKED_TYPES, Pn as repairMalformedResourcesForReadOnly, Pr as getBootstrapMarkerKey, Pt as parameterTypeMayLoseSecretIdentity, Q as exportAliasCollisionScrubWarning, Qi as StackTerminationProtectionError, Qn as maskSecretsInError, Qr as escapeRegExp, Qt as producerRegionsFromState, R as WARM_THROUGHPUT_MEMBERS, Rn as withRetry, Rr as validateAssetBucketName, Rt as normalizeAwsTagsToCfn, S as ATOMIC_FINAL_SNAPSHOT_TYPES, Si as expectedOwnerParam, Sn as malformedResourcesWarning, Sr as shouldRetainResource, St as deleteSkipReason, T as ccRoutedFinalSnapshotError, Ti as derivePartitionAndUrlSuffix, Tn as refuseDivergentRecordRegionForDestroy, Tr as WorkGraph, Tt as IntrinsicFunctionResolver, U as formatResourceLine, Ui as LocalStartServiceError, Un as STATE_SOURCED_READBACK_RULES, Ur as describeDockerExecFailure, Ut as configBooleanRefusal, V as toCfnInteger, Vi as IntrinsicResolutionRefusalError, Vn as STATE_SOURCED_BASELINE_RULES, Vr as buildDockerImage, Vt as coerceCfnBoolean, W as bold, Wn as TEMPLATE_SOURCED_RULES, Wr as describeDockerFailure, Wt as configIntegerRefusal, X as collectDeclaredOutputNames, Xi as ResourceUpdateNotSupportedError, Xn as isSingleDynamicReferenceToken, Xr as runDockerForeground, Xt as requireConfigString, Y as yellow, Yi as ResourceTimeoutError, Yn as identityKeyFor, Yr as redactDockerArgvValues, Yt as requireConfigObject, Z as collectPublishedOutputNames, Zi as StackHasActiveImportsError, Zn as markSameGenerationBag, Zr as runDockerStreaming, Zt as classifyReplaySecretRegion, _ as bagHoldsSecretExpression, _i as MIGRATE_TMP_PREFIX, _n as malformedLocalOutputsWarning, _r as importableOutputKeys, _t as CloudControlProvider, a as DeploymentEventsStore, aa as isMarkedNonRetryable, ai as getLegacyStateBucketName, an as applyRoleArnIfSet, ar as S3StateBackend, at as collectInlinePolicyNamesManagedBySiblings, b as mergeNoChangeOutputs, bi as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, bn as malformedRenderedContainersWarning, br as orphansAfterRollback, bt as UNSPECIFIED_SKIP_REASON, c as replayFailedOperations, ca as isTransientServerError, ci as resolveCaptureObservedState, cn as callerEnvCredentials, cr as buildForceUnlockCommand, ct as wouldReturnToSdkProvider, d as updatePartialMessage, da as retryClassificationText, di as resolveStateBucketWithDefaultAndSource, dn as INTRINSIC_KEYS, dr as formatLockExpiry, dt as maskerOrIdentity, ea as SynthesisError, ei as AssetManifestLoader, en as s3BucketArn, er as recordMaskOnlyValue, et as secretBearing, f as updatePartialReason, fa as describeAwsFailure, fi as resolveUseCdkBootstrapAssets, fn as STATE_RESOURCES_MALFORMED, fr as shellQuote, ft as beginCommandInterruptScope, g as computeImplicitDeleteEdges, gi as CFN_TEMPLATE_URL_LIMIT, gn as malformedExportSourceWarning, gr as hasReadableExportSet, gt as startInterruptWatch, h as IMPLICIT_DELETE_DEPENDENCIES, hi as CFN_TEMPLATE_BODY_LIMIT, hn as malformedExportNamesWarning, hr as exportNamesCarriedFrom, ht as isInterruptedWaitError, i as DeploymentEventsReader, ia as withErrorHandling, ii as getDefaultStateBucketName, in as s3BucketWebsiteUrl, ir as LockManager, it as IAMRoleProvider, j as extractDeploymentEventError, ji as getAwsClients, jn as refuseMalformedState, jr as BOOTSTRAP_MARKER_PREFIX, jt as isImpossibleEmptyStoredAttribute, k as unsupportedFinalSnapshotError, ki as resolveBucketRegion, kn as refuseMalformedResourcePropertiesForOrphan, kr as rewriteTemplateAssetReferences, kt as coerceParameterTypedValue, l as replayRollback, la as markNonRetryable, li as resolveSkipPrefix, ln as envCredentialsAreAssumedRole, lr as buildLockContentionMessage, lt as createMaskedRetryLogger, m as maskingRetryLogger, mi as warnDeprecatedNoPrefixCliFlag, mn as hasReadableResources, mr as DEFAULT_STATE_PREFIX, mt as interruptWatchListenerCount, n as DEFAULT_RESOURCE_WARN_AFTER_MS, ni as Synthesizer, nn as s3BucketDualStackDomainName, nr as redactSecretsForState, nt as secretSafeKeyDisplay, o as planFailedOps, oa as isRetryableTransientError, oi as resolveApp, on as AWS_CREDENTIAL_ENV_KEYS, or as rebuildClientForBucketRegion, ot as clearOnUpdateRemoval, p as withResourceDeadline, pa as safeStringify, pi as stateBucketExistenceConfirmed, pn as hasReadableOutputs, pr as CUSTOM_RESOURCE_RESPONSE_PREFIX, pt as endCommandInterruptScope, q as green, qi as PartialFailureError, qn as dynamicReferenceTokens, qr as getDockerCmd, qt as replayWarn, r as DeployEngine, ra as normalizeAwsError, ri as synthesisStatusMessage, rn as s3BucketRegionalDomainName, rr as scrubResourceRecord, rt as getCurrentResourceSecrets, s as planRollback, sa as isThrottlingError, si as resolveAutoAssetStorage, sn as applyCallerIdentityCredentials, sr as UNREPRODUCIBLE_LOCK_CLAUSE, st as ProviderRegistry, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as getDockerImageBySourceHash, tn as s3BucketDomainName, tr as recoverMaskedOutput, tt as secretBearingStateKeyWarning, u as resolveReplacementOldType, ua as markRedactedCause, ui as resolveStateBucketWithDefault, un as DiffCalculator, ur as forceQuitRecoveryClause, ut as maskDeep, v as isSecretBearingReferenceString, vi as findLargeInlineResources, vn as malformedOutputsRefusalMessage, vr as importableOutputs, vt as isWaitAbandonedError, w as buildFinalSnapshotIdentifier, wi as canonicalizeRegion, wn as producerRecordKey, wr as stringifyValue, wt as isTerminationProtectionPropagationError, x as bindingSkippedOutputs, xi as decodeListingKey, xn as malformedResourcePropertiesWarning, xr as orphansCarriedFrom, xt as deleteIndeterminateGuards, y as keptWholeReasonText, yi as uploadCfnTemplate, yn as malformedOutputsWarning, yr as isReadableBag, yt as slowCcOperationTimeoutMs, z as coerceWarmThroughput$1, zi as DeployCancelledError, zn as DagBuilder, zr as validateContainerRepoName, zt as resolveExplicitPhysicalId } from "./deploy-engine-efiVJ-rX.js";
|
|
2
2
|
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, _ as withSkipPrefix, b as SECRET_REF_MAX_CODE_POINTS, 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, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS } from "./logger-BX4pJk0T.js";
|
|
3
3
|
import { n as awsClientDefaults } from "./aws-client-defaults-BoIxyCir.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-CpyoQ3PO.js";
|
|
5
|
+
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-BTxYWlwu.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";
|
|
@@ -2758,16 +2758,13 @@ const EMPTY_ALLOW_SET$1 = /* @__PURE__ */ new Set();
|
|
|
2758
2758
|
* is spelled once, or the note could name ids the validator does not refuse.
|
|
2759
2759
|
*
|
|
2760
2760
|
* The two are NOT the same set, and deliberately so. The refusal fires when
|
|
2761
|
-
* EITHER the state record or the template says nested, because
|
|
2762
|
-
*
|
|
2763
|
-
*
|
|
2764
|
-
*
|
|
2765
|
-
*
|
|
2766
|
-
*
|
|
2767
|
-
*
|
|
2768
|
-
* template contains. Which type governs was argued from plausibility twice and
|
|
2769
|
-
* settled by reading `analyzer/diff-calculator.ts` and `deploy-engine.ts`'s
|
|
2770
|
-
* `oldDeleteProvider`; do not re-derive it from either name. Other modules spell it
|
|
2761
|
+
* EITHER the state record or the template says nested, because a recreate has
|
|
2762
|
+
* two halves with two types (issue #2668): the STATE's type routes the delete
|
|
2763
|
+
* of the old resource — `NestedStackProvider` derives `<parent>~<logicalId>`
|
|
2764
|
+
* from it — and the TEMPLATE's type routes the create, which for a nested row
|
|
2765
|
+
* is a whole child-stack deploy. Either half alone is outside what a recreate
|
|
2766
|
+
* is built for. The note keys on the TEMPLATE alone, because it is telling the
|
|
2767
|
+
* user what their current template contains. Other modules spell it
|
|
2771
2768
|
* for themselves rather than sharing one export: the only EXPORTED copy lives
|
|
2772
2769
|
* in `src/cli/commands/retire-cfn-stack.ts`, and importing a CLI command module
|
|
2773
2770
|
* from the deployment layer would invert the dependency direction. (No count of
|
|
@@ -3382,6 +3379,297 @@ function analyzePinCcApiReachability(pinnedLogicalIds, stacks) {
|
|
|
3382
3379
|
};
|
|
3383
3380
|
}
|
|
3384
3381
|
|
|
3382
|
+
//#endregion
|
|
3383
|
+
//#region src/utils/nested-template-cycle.ts
|
|
3384
|
+
/**
|
|
3385
|
+
* Static, READ-ONLY validation of a nested-template tree, run BEFORE anything
|
|
3386
|
+
* is deployed from it (issue go-to-k/cdkd#3247).
|
|
3387
|
+
*
|
|
3388
|
+
* `NestedStackProvider` follows `Metadata['aws:asset:path']` from one nested
|
|
3389
|
+
* template to the next and runs a whole child `DeployEngine` per level. A
|
|
3390
|
+
* relative path that resolves back onto the same nesting chain therefore
|
|
3391
|
+
* DEPLOYS every level of the cycle until S3's key limit stops it. Threading an
|
|
3392
|
+
* ancestor chain through the provider's recursion would only refuse at the
|
|
3393
|
+
* first repeat, by which point every level above it is already provisioned.
|
|
3394
|
+
* Everything the walk follows is on disk before the first child engine exists,
|
|
3395
|
+
* so the whole subtree is validated up front instead.
|
|
3396
|
+
*
|
|
3397
|
+
* Two properties are copied from the `cdkd diff --recursive` guard (issue
|
|
3398
|
+
* go-to-k/cdkd#3239), because re-deriving them gets them wrong:
|
|
3399
|
+
*
|
|
3400
|
+
* - The refused shape is a repeat along ONE root-to-node chain, never a repeat
|
|
3401
|
+
* anywhere in the tree: two sibling rows may name the same child template,
|
|
3402
|
+
* and that diamond is a legitimate assembly.
|
|
3403
|
+
* - It is a refusal, not a depth cap. A cyclic assembly has no correct deploy.
|
|
3404
|
+
*
|
|
3405
|
+
* `Condition`s are deliberately NOT evaluated: a row whose condition would be
|
|
3406
|
+
* false at deploy time still counts. A self-including template that terminates
|
|
3407
|
+
* through a condition is refused here exactly as the diff walk refuses it, so
|
|
3408
|
+
* the two commands agree about which assemblies are well-formed.
|
|
3409
|
+
*
|
|
3410
|
+
* A leaf apart from `display-safe.ts`, so any layer may import it.
|
|
3411
|
+
*/
|
|
3412
|
+
const NESTED_STACK_TYPE$1 = "AWS::CloudFormation::Stack";
|
|
3413
|
+
/**
|
|
3414
|
+
* Same predicate `NestedStackProvider` applies per level. The `startsWith('/')`
|
|
3415
|
+
* arm covers a POSIX-style absolute path consumed on Windows, where
|
|
3416
|
+
* `path.isAbsolute` alone answers `false`.
|
|
3417
|
+
*/
|
|
3418
|
+
function isAbsoluteAssetPath(p) {
|
|
3419
|
+
return path.isAbsolute(p) || p.startsWith("/");
|
|
3420
|
+
}
|
|
3421
|
+
/**
|
|
3422
|
+
* What "the same template" means ON A CHAIN: the file's name inside its REAL
|
|
3423
|
+
* directory.
|
|
3424
|
+
*
|
|
3425
|
+
* The directory is `realpath`ed because a symlinked DIRECTORY (`d -> .`) makes
|
|
3426
|
+
* every level's joined path a new, longer string (`d/a.json`, `d/d/a.json`,
|
|
3427
|
+
* ...) for one and the same file, which a comparison of resolved path strings
|
|
3428
|
+
* never sees repeat. There are finitely many (real directory, name) pairs, so
|
|
3429
|
+
* an endless descent must repeat one, however it is spelled.
|
|
3430
|
+
*
|
|
3431
|
+
* It is NOT what decides a template's children. Those resolve LEXICALLY
|
|
3432
|
+
* against the directory the template was reached in (`path.join` folds `d/..`
|
|
3433
|
+
* without following the link), so one identity reached through two spellings
|
|
3434
|
+
* can have two different subtrees. That is why the clean-subtree memo in
|
|
3435
|
+
* `findNestedTemplateTreeDefect` is keyed on the lexical path instead, and why
|
|
3436
|
+
* an identity repeat is a conservative refusal rather than an exact one: with
|
|
3437
|
+
* a symlinked directory AND a `..` row it can refuse a tree that would have
|
|
3438
|
+
* terminated. Such an assembly is hand-built by definition.
|
|
3439
|
+
*
|
|
3440
|
+
* Falls back to `path.resolve` for a directory that does not exist; the walk
|
|
3441
|
+
* stops there anyway.
|
|
3442
|
+
*/
|
|
3443
|
+
function templateIdentity(templatePath) {
|
|
3444
|
+
const dir = path.dirname(templatePath);
|
|
3445
|
+
let realDir;
|
|
3446
|
+
try {
|
|
3447
|
+
realDir = fs.realpathSync(dir);
|
|
3448
|
+
} catch {
|
|
3449
|
+
realDir = path.resolve(dir);
|
|
3450
|
+
}
|
|
3451
|
+
return path.join(realDir, path.basename(templatePath));
|
|
3452
|
+
}
|
|
3453
|
+
/**
|
|
3454
|
+
* The nested-stack rows of a parsed template that name a child template file.
|
|
3455
|
+
*
|
|
3456
|
+
* THE ONE SPELLING, shared by this module's walk and by
|
|
3457
|
+
* `NestedStackProvider.indexGrandchildTemplates`, because the walk is only a
|
|
3458
|
+
* guard if it follows exactly the rows the deploy follows. Two hand-written
|
|
3459
|
+
* copies disagreed on an ARRAY-valued `Resources`: `Object.entries` indexes an
|
|
3460
|
+
* array as `'0'`, `'1'`, ..., the deploy followed those rows, and a walk that
|
|
3461
|
+
* skipped them accepted a cyclic tree. So this takes whatever `Object.entries`
|
|
3462
|
+
* takes, and does not pre-judge the container's shape.
|
|
3463
|
+
*/
|
|
3464
|
+
function listNestedTemplateRows(template) {
|
|
3465
|
+
if (template === null || typeof template !== "object") return [];
|
|
3466
|
+
const resources = template.Resources;
|
|
3467
|
+
if (resources === null || resources === void 0) return [];
|
|
3468
|
+
const rows = [];
|
|
3469
|
+
for (const [logicalId, resource] of Object.entries(resources)) {
|
|
3470
|
+
const row = resource;
|
|
3471
|
+
if (row?.Type !== NESTED_STACK_TYPE$1) continue;
|
|
3472
|
+
const assetPath = row.Metadata?.["aws:asset:path"];
|
|
3473
|
+
if (typeof assetPath !== "string" || assetPath.length === 0) continue;
|
|
3474
|
+
rows.push({
|
|
3475
|
+
logicalId,
|
|
3476
|
+
assetPath
|
|
3477
|
+
});
|
|
3478
|
+
}
|
|
3479
|
+
return rows;
|
|
3480
|
+
}
|
|
3481
|
+
/**
|
|
3482
|
+
* The rows of the template FILE at `templatePath`. Unreadable or unparseable
|
|
3483
|
+
* input yields `undefined`: reporting that is the job of the site that
|
|
3484
|
+
* actually loads the template, with its own message, and a walk that cannot
|
|
3485
|
+
* descend cannot loop.
|
|
3486
|
+
*/
|
|
3487
|
+
function readNestedRows(templatePath) {
|
|
3488
|
+
let parsed;
|
|
3489
|
+
try {
|
|
3490
|
+
parsed = JSON.parse(fs.readFileSync(templatePath, "utf-8"));
|
|
3491
|
+
} catch {
|
|
3492
|
+
return;
|
|
3493
|
+
}
|
|
3494
|
+
return listNestedTemplateRows(parsed);
|
|
3495
|
+
}
|
|
3496
|
+
/**
|
|
3497
|
+
* Deeper than this cannot deploy at all, so it is refused rather than walked.
|
|
3498
|
+
* A child's state key is `cdkd/<root>~<id>~<id>.../<region>/state.json`; each
|
|
3499
|
+
* level adds at least two bytes (`~` plus a one-character logical id) and S3
|
|
3500
|
+
* caps a key at 1024 bytes. The bound exists so a hostile chain of tens of
|
|
3501
|
+
* thousands of DISTINCT templates is a readable refusal instead of a
|
|
3502
|
+
* `RangeError` from the recursion below. It is not a cycle heuristic: a cycle
|
|
3503
|
+
* is refused at its first repeat, long before this.
|
|
3504
|
+
*/
|
|
3505
|
+
const MAX_NESTING_DEPTH = 512;
|
|
3506
|
+
/**
|
|
3507
|
+
* Rows the walk will follow before it refuses the tree as too large.
|
|
3508
|
+
*
|
|
3509
|
+
* The clean-subtree memo is keyed on the LEXICAL path (it has to be, see
|
|
3510
|
+
* `templateIdentity`), and symlinked directories give one file arbitrarily
|
|
3511
|
+
* many lexical spellings: with `d1 -> .` and `d2 -> .`, rows naming
|
|
3512
|
+
* `t.json`, `d1/t.json` and `d2/t.json` at every level multiply the spellings
|
|
3513
|
+
* per level while no chain ever repeats an identity. Such a walk is finite but
|
|
3514
|
+
* exponential, so it needs a budget rather than a better key. Far above any
|
|
3515
|
+
* real assembly: a CDK app's nested templates number in the tens, and a memo
|
|
3516
|
+
* hit costs one row, not a subtree.
|
|
3517
|
+
*/
|
|
3518
|
+
const MAX_ROWS_FOLLOWED = 1e4;
|
|
3519
|
+
/**
|
|
3520
|
+
* Walk every nested template reachable from `nestedTemplates` (logical id ->
|
|
3521
|
+
* template file path, the shape `AssemblyReader` and the provider's own
|
|
3522
|
+
* per-level index produce) and return the first defect, or `undefined` when
|
|
3523
|
+
* the tree is well-formed.
|
|
3524
|
+
*
|
|
3525
|
+
* `ancestorTemplatePaths` seeds the chain with templates ABOVE the entry rows,
|
|
3526
|
+
* for a caller that knows them. Detection does not depend on it: a cycle
|
|
3527
|
+
* through an unseeded ancestor is followed once around and refused when it
|
|
3528
|
+
* re-enters the first template the walk did see.
|
|
3529
|
+
*/
|
|
3530
|
+
function findNestedTemplateTreeDefect(nestedTemplates, ancestorTemplatePaths = []) {
|
|
3531
|
+
const onChain = /* @__PURE__ */ new Set();
|
|
3532
|
+
for (const p of ancestorTemplatePaths) onChain.add(templateIdentity(p));
|
|
3533
|
+
const clean = /* @__PURE__ */ new Set();
|
|
3534
|
+
const chain = [];
|
|
3535
|
+
let rowsFollowed = 0;
|
|
3536
|
+
const visit = (logicalId, templatePath) => {
|
|
3537
|
+
if (typeof templatePath !== "string") return void 0;
|
|
3538
|
+
const identity = templateIdentity(templatePath);
|
|
3539
|
+
const lexical = path.resolve(templatePath);
|
|
3540
|
+
chain.push({
|
|
3541
|
+
logicalId,
|
|
3542
|
+
templatePath: identity
|
|
3543
|
+
});
|
|
3544
|
+
try {
|
|
3545
|
+
if (onChain.has(identity)) return {
|
|
3546
|
+
kind: "cycle",
|
|
3547
|
+
chain: [...chain]
|
|
3548
|
+
};
|
|
3549
|
+
if (chain.length > 512) return {
|
|
3550
|
+
kind: "too-deep",
|
|
3551
|
+
chain: [...chain]
|
|
3552
|
+
};
|
|
3553
|
+
if (++rowsFollowed > 1e4) return {
|
|
3554
|
+
kind: "too-large",
|
|
3555
|
+
chain: [...chain]
|
|
3556
|
+
};
|
|
3557
|
+
if (clean.has(lexical)) return void 0;
|
|
3558
|
+
const rows = readNestedRows(templatePath);
|
|
3559
|
+
if (rows === void 0) return void 0;
|
|
3560
|
+
onChain.add(identity);
|
|
3561
|
+
try {
|
|
3562
|
+
const dir = path.dirname(templatePath);
|
|
3563
|
+
for (const row of rows) {
|
|
3564
|
+
if (isAbsoluteAssetPath(row.assetPath)) return {
|
|
3565
|
+
kind: "absolute-path",
|
|
3566
|
+
chain: [...chain],
|
|
3567
|
+
logicalId: row.logicalId,
|
|
3568
|
+
assetPath: row.assetPath
|
|
3569
|
+
};
|
|
3570
|
+
const defect = visit(row.logicalId, path.join(dir, row.assetPath));
|
|
3571
|
+
if (defect) return defect;
|
|
3572
|
+
}
|
|
3573
|
+
} finally {
|
|
3574
|
+
onChain.delete(identity);
|
|
3575
|
+
}
|
|
3576
|
+
clean.add(lexical);
|
|
3577
|
+
return;
|
|
3578
|
+
} finally {
|
|
3579
|
+
chain.pop();
|
|
3580
|
+
}
|
|
3581
|
+
};
|
|
3582
|
+
for (const [logicalId, templatePath] of Object.entries(nestedTemplates)) {
|
|
3583
|
+
const defect = visit(logicalId, templatePath);
|
|
3584
|
+
if (defect) return defect;
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
/** Hops rendered in full before the middle of a long chain is elided. */
|
|
3588
|
+
const MAX_RENDERED_HOPS = 8;
|
|
3589
|
+
/**
|
|
3590
|
+
* `'A' (/x/a.json) -> 'B' (/x/b.json)`. Every interpolation goes through
|
|
3591
|
+
* `displaySafe`: this text exists FOR a hand-modified assembly, so a logical id
|
|
3592
|
+
* (a template key) and a path (derived from `aws:asset:path`) are both
|
|
3593
|
+
* attacker-controlled, and a bare `Error` message is not sanitized downstream.
|
|
3594
|
+
* A long chain keeps both ends, since the entry row and the closing row are
|
|
3595
|
+
* the two a reader needs.
|
|
3596
|
+
*/
|
|
3597
|
+
function renderChain(chain) {
|
|
3598
|
+
const hop = (h) => `'${displaySafe(h.logicalId)}' (${displaySafe(h.templatePath)})`;
|
|
3599
|
+
if (chain.length <= MAX_RENDERED_HOPS) return chain.map(hop).join(" -> ");
|
|
3600
|
+
const keep = MAX_RENDERED_HOPS / 2;
|
|
3601
|
+
return [
|
|
3602
|
+
...chain.slice(0, keep).map(hop),
|
|
3603
|
+
`... ${chain.length - 8} more ...`,
|
|
3604
|
+
...chain.slice(-4).map(hop)
|
|
3605
|
+
].join(" -> ");
|
|
3606
|
+
}
|
|
3607
|
+
/**
|
|
3608
|
+
* The refusal text for a defect found under `stackName`. `action` completes
|
|
3609
|
+
* "Refusing to ..." so each caller states what it declined to do.
|
|
3610
|
+
*/
|
|
3611
|
+
function renderNestedTemplateTreeDefect(defect, stackName, action) {
|
|
3612
|
+
const provenance = `CDK emits an acyclic nested template tree with relative asset paths, so this indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to ${action}.`;
|
|
3613
|
+
const owner = (chain) => {
|
|
3614
|
+
const ids = chain.slice(0, -1).map((h) => h.logicalId);
|
|
3615
|
+
const keep = MAX_RENDERED_HOPS / 2;
|
|
3616
|
+
const shown = ids.length <= MAX_RENDERED_HOPS ? ids : [
|
|
3617
|
+
...ids.slice(0, keep),
|
|
3618
|
+
`...${ids.length - 8} more...`,
|
|
3619
|
+
...ids.slice(-4)
|
|
3620
|
+
];
|
|
3621
|
+
return displaySafe([stackName, ...shown].join("~"));
|
|
3622
|
+
};
|
|
3623
|
+
if (defect.kind === "cycle") {
|
|
3624
|
+
const closing = defect.chain[defect.chain.length - 1];
|
|
3625
|
+
return `The nested template tree under stack '${displaySafe(stackName)}' contains a cycle: ${renderChain(defect.chain)}. Nested stack '${displaySafe(closing.logicalId)}' (declared in stack '${owner(defect.chain)}') resolves to a template that is already on that nesting chain, so its Metadata['aws:asset:path'] closes a cycle. ${provenance}`;
|
|
3626
|
+
}
|
|
3627
|
+
if (defect.kind === "too-large") return `The nested template tree under stack '${displaySafe(stackName)}' has more than ${MAX_ROWS_FOLLOWED} nested-stack rows to follow (the walk stopped at ${renderChain(defect.chain)}). That is far beyond any CDK-generated assembly. Symlinked directories can give one template file many paths, which multiplies the tree without ever repeating on one chain. Refusing to ${action}.`;
|
|
3628
|
+
if (defect.kind === "too-deep") return `The nested template tree under stack '${displaySafe(stackName)}' nests more than ${512} levels deep: ${renderChain(defect.chain)}. No tree that deep can deploy, because each level lengthens the child's state key and S3 caps a key at 1024 bytes. ${provenance}`;
|
|
3629
|
+
return `The nested template tree under stack '${displaySafe(stackName)}' has nested stack '${displaySafe(defect.logicalId)}' (reached through ${renderChain(defect.chain)}) with Metadata['aws:asset:path']='${displaySafe(defect.assetPath)}' which is absolute. ${provenance}`;
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3632
|
+
//#endregion
|
|
3633
|
+
//#region src/cli/commands/nested-template-preflight.ts
|
|
3634
|
+
/**
|
|
3635
|
+
* Refuse a malformed nested-template tree (a cycle, an absolute
|
|
3636
|
+
* `aws:asset:path` below the top level, a chain too deep or a tree too large
|
|
3637
|
+
* to deploy) for every SELECTED stack, before `cdkd deploy` does anything a
|
|
3638
|
+
* refusal would have to undo (issue go-to-k/cdkd#3449).
|
|
3639
|
+
*
|
|
3640
|
+
* `NestedStackProvider` runs the same walk per nested-stack row
|
|
3641
|
+
* (go-to-k/cdkd#3247), which is the earliest point the provider owns: by then
|
|
3642
|
+
* the stack's assets are published, its lock is held, and any root resource
|
|
3643
|
+
* that does not depend on the nested row may already be created and must be
|
|
3644
|
+
* rolled back. Everything the walk reads is on disk as soon as synth returns,
|
|
3645
|
+
* so the CLI asks first and the deploy never starts. The provider call stays
|
|
3646
|
+
* as the guard for a context this command did not build.
|
|
3647
|
+
*
|
|
3648
|
+
* Read-only and synchronous: a few file reads per nested template, none at all
|
|
3649
|
+
* for a stack without nested-stack rows. One walk covers all of a stack's
|
|
3650
|
+
* top-level rows, so the walker's row budget is per STACK here and per row in
|
|
3651
|
+
* the provider; no CDK-generated assembly comes near either.
|
|
3652
|
+
*
|
|
3653
|
+
* The root template's own path is not seeded into the walk (`StackInfo` does
|
|
3654
|
+
* not carry it). Detection does not depend on the seed: a child that points
|
|
3655
|
+
* back at the root template is followed once around and refused when the walk
|
|
3656
|
+
* re-enters the first nested template it saw.
|
|
3657
|
+
*
|
|
3658
|
+
* `SynthesisError` because the defect is in the synthesized assembly, like the
|
|
3659
|
+
* absolute-path refusal `AssemblyReader` raises for a top-level row.
|
|
3660
|
+
* `markNonRetryable` because the message carries template-controlled text the
|
|
3661
|
+
* substring-matching retry classifier must never read, wherever the error ends
|
|
3662
|
+
* up being caught.
|
|
3663
|
+
*/
|
|
3664
|
+
function refuseMalformedNestedTemplateTrees(stacks) {
|
|
3665
|
+
for (const stack of stacks) {
|
|
3666
|
+
if (!stack.nestedTemplates) continue;
|
|
3667
|
+
const defect = findNestedTemplateTreeDefect(stack.nestedTemplates);
|
|
3668
|
+
if (!defect) continue;
|
|
3669
|
+
throw markNonRetryable(new SynthesisError(renderNestedTemplateTreeDefect(defect, stack.stackName, "start the deploy; nothing has been published or provisioned")));
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3385
3673
|
//#endregion
|
|
3386
3674
|
//#region src/cli/commands/confirm-prompt.ts
|
|
3387
3675
|
/**
|
|
@@ -26356,7 +26644,8 @@ var SSMParameterProvider = class {
|
|
|
26356
26644
|
* question cdkd can answer correctly — and would hand `cdkd drift` a baseline
|
|
26357
26645
|
* AWS does not hold. Dropping the `Arn` instead degrades to the resolver's LOUD
|
|
26358
26646
|
* `*Arn` shape-guard failure, which is exactly the pre-fix behavior, and the
|
|
26359
|
-
* resource's next UPDATE re-records it
|
|
26647
|
+
* resource's next UPDATE re-records it (as does the deploy engine's re-read
|
|
26648
|
+
* on a `Fn::GetAtt` miss, issue #1852). A loud missing value beats a quiet
|
|
26360
26649
|
* wrong one — the same reasoning the `fabricated`-account arm above applies.
|
|
26361
26650
|
*
|
|
26362
26651
|
* Two honest bounds on that "next update re-records it". `getAccountInfo`
|
|
@@ -41121,10 +41410,14 @@ var RDSProvider = class {
|
|
|
41121
41410
|
async importDBInstance(input) {
|
|
41122
41411
|
const explicit = resolveExplicitPhysicalId(input, "DBInstanceIdentifier");
|
|
41123
41412
|
if (explicit) try {
|
|
41124
|
-
await this.getClient().send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: explicit }));
|
|
41413
|
+
const described = (await this.getClient().send(new DescribeDBInstancesCommand({ DBInstanceIdentifier: explicit }))).DBInstances?.[0];
|
|
41125
41414
|
return {
|
|
41126
41415
|
physicalId: explicit,
|
|
41127
|
-
attributes: {
|
|
41416
|
+
attributes: definedAttributes({
|
|
41417
|
+
"Endpoint.Address": described?.Endpoint?.Address,
|
|
41418
|
+
"Endpoint.Port": stringifyIfAssigned(described?.Endpoint?.Port),
|
|
41419
|
+
Arn: described?.DBInstanceArn
|
|
41420
|
+
})
|
|
41128
41421
|
};
|
|
41129
41422
|
} catch (err) {
|
|
41130
41423
|
if (err.name === "DBInstanceNotFoundFault") return null;
|
|
@@ -42714,10 +43007,14 @@ var DocDBProvider = class {
|
|
|
42714
43007
|
async importDBInstance(input) {
|
|
42715
43008
|
const explicit = resolveExplicitPhysicalId(input, "DBInstanceIdentifier");
|
|
42716
43009
|
if (explicit) try {
|
|
42717
|
-
await this.getClient().send(new DescribeDBInstancesCommand$1({ DBInstanceIdentifier: explicit }));
|
|
43010
|
+
const described = (await this.getClient().send(new DescribeDBInstancesCommand$1({ DBInstanceIdentifier: explicit }))).DBInstances?.[0];
|
|
42718
43011
|
return {
|
|
42719
43012
|
physicalId: explicit,
|
|
42720
|
-
attributes: {
|
|
43013
|
+
attributes: definedAttributes({
|
|
43014
|
+
"Endpoint.Address": described?.Endpoint?.Address,
|
|
43015
|
+
"Endpoint.Port": stringifyIfAssigned(described?.Endpoint?.Port),
|
|
43016
|
+
Arn: described?.DBInstanceArn
|
|
43017
|
+
})
|
|
42721
43018
|
};
|
|
42722
43019
|
} catch (err) {
|
|
42723
43020
|
if (err.name === "DBInstanceNotFoundFault") return null;
|
|
@@ -43357,10 +43654,14 @@ var NeptuneProvider = class {
|
|
|
43357
43654
|
async importDBInstance(input) {
|
|
43358
43655
|
const explicit = resolveExplicitPhysicalId(input, "DBInstanceIdentifier");
|
|
43359
43656
|
if (explicit) try {
|
|
43360
|
-
await this.getClient().send(new DescribeDBInstancesCommand$2({ DBInstanceIdentifier: explicit }));
|
|
43657
|
+
const described = (await this.getClient().send(new DescribeDBInstancesCommand$2({ DBInstanceIdentifier: explicit }))).DBInstances?.[0];
|
|
43361
43658
|
return {
|
|
43362
43659
|
physicalId: explicit,
|
|
43363
|
-
attributes: {
|
|
43660
|
+
attributes: definedAttributes({
|
|
43661
|
+
"Endpoint.Address": described?.Endpoint?.Address,
|
|
43662
|
+
"Endpoint.Port": stringifyIfAssigned(described?.Endpoint?.Port),
|
|
43663
|
+
Arn: described?.DBInstanceArn
|
|
43664
|
+
})
|
|
43364
43665
|
};
|
|
43365
43666
|
} catch (err) {
|
|
43366
43667
|
if (err.name === "DBInstanceNotFoundFault") return null;
|
|
@@ -48076,7 +48377,7 @@ var AppSyncProvider = class {
|
|
|
48076
48377
|
return await this.buildAppSyncArn(suffix, regionOverride);
|
|
48077
48378
|
} catch (error) {
|
|
48078
48379
|
if (!omitArnOnFailure) throw error;
|
|
48079
|
-
this.logger.warn(`Imported ${resourceType} (${physicalId}) but its ARN attribute was NOT recorded: ${describeAwsFailure(error).detail}. Ref will resolve to the compound physical id, and Fn::GetAtt on the ARN will fail, until the resource's next update heals the record.`);
|
|
48380
|
+
this.logger.warn(`Imported ${resourceType} (${physicalId}) but its ARN attribute was NOT recorded: ${describeAwsFailure(error).detail}. Ref will resolve to the compound physical id, and Fn::GetAtt on the ARN will fail, until the resource's next update — or a later deploy that needs the attribute and re-reads it from AWS — heals the record.`);
|
|
48080
48381
|
return;
|
|
48081
48382
|
}
|
|
48082
48383
|
};
|
|
@@ -48164,7 +48465,7 @@ var AppSyncProvider = class {
|
|
|
48164
48465
|
try {
|
|
48165
48466
|
return await this.childRefAttributes(resourceType, physicalId, region, true) ?? {};
|
|
48166
48467
|
} catch (error) {
|
|
48167
|
-
this.logger.warn(`Imported ${resourceType} (${physicalId}) but could not build its ARN attribute: ${describeAwsFailure(error).detail}. Ref will resolve to the compound physical id, and Fn::GetAtt on the ARN will fail, until the resource's next update heals the record.`);
|
|
48468
|
+
this.logger.warn(`Imported ${resourceType} (${physicalId}) but could not build its ARN attribute: ${describeAwsFailure(error).detail}. Ref will resolve to the compound physical id, and Fn::GetAtt on the ARN will fail, until the resource's next update — or a later deploy that needs the attribute and re-reads it from AWS — heals the record.`);
|
|
48168
48469
|
return {};
|
|
48169
48470
|
}
|
|
48170
48471
|
}
|
|
@@ -61118,6 +61419,64 @@ var S3VectorsProvider = class {
|
|
|
61118
61419
|
//#endregion
|
|
61119
61420
|
//#region src/provisioning/providers/s3-directory-bucket-provider.ts
|
|
61120
61421
|
/**
|
|
61422
|
+
* What `delete()` throws for a non-empty bucket with no auto-empty opt-in
|
|
61423
|
+
* (issue [#1344](https://github.com/go-to-k/cdkd/issues/1344)), with the bucket
|
|
61424
|
+
* name rendered safely (issue
|
|
61425
|
+
* [#3270](https://github.com/go-to-k/cdkd/issues/3270)).
|
|
61426
|
+
*
|
|
61427
|
+
* `bucketName` on the delete path is the STATE-borne physical id, which for
|
|
61428
|
+
* this type is the TEMPLATE-chosen name, and this message is THROWN from
|
|
61429
|
+
* `delete()` — so it reaches the operator's terminal AND the durable
|
|
61430
|
+
* `deployments/*.jsonl` store. It used to interpolate the name BARE into
|
|
61431
|
+
* `aws s3 rm s3://<name> --recursive`, so a name carrying `;`, a backtick,
|
|
61432
|
+
* `$(...)` or a newline APPENDED a command to a recursive delete when pasted.
|
|
61433
|
+
*
|
|
61434
|
+
* The command goes through the shared `renderDisableCommand`
|
|
61435
|
+
* (`displaySafe(asciiOnly)`, then `shellQuote`, then SUPPRESS the whole command
|
|
61436
|
+
* when sanitizing CHANGED the value, since a command naming the sanitized name
|
|
61437
|
+
* would empty a DIFFERENT bucket). Three things are specific to this site:
|
|
61438
|
+
*
|
|
61439
|
+
* - **The quoted unit is the ASSEMBLED `s3://<name>` argument**, not the name:
|
|
61440
|
+
* `aws s3 rm` has no `--bucket <name>` form, and quoting only the name would
|
|
61441
|
+
* put a quote in the middle of a word. `shellQuote` leaves `:` and `/` bare,
|
|
61442
|
+
* so a clean name renders exactly as before.
|
|
61443
|
+
* - **The NAME is compared against its own sanitized form HERE, before the
|
|
61444
|
+
* shared renderer sees it.** The renderer's changed-by-sanitizing test runs
|
|
61445
|
+
* over the assembled argument, where the `s3://` prefix shields a LEADING
|
|
61446
|
+
* space of the name from the sanitizer's trim — so without this check a
|
|
61447
|
+
* padded name would be shown in a command while the prose beside it named
|
|
61448
|
+
* the trimmed one.
|
|
61449
|
+
* - **A name containing `/` is suppressed here**, which the shared renderer
|
|
61450
|
+
* cannot know to do: `s3://a/b` addresses the prefix `b` in bucket `a`, a
|
|
61451
|
+
* DIFFERENT bucket, and both characters pass sanitizing and quoting
|
|
61452
|
+
* unchanged. An empty name is suppressed for the same reason (`s3://` names
|
|
61453
|
+
* no bucket at all).
|
|
61454
|
+
* - **No `maskSecrets` is passed.** `DeleteContext` carries no masker by
|
|
61455
|
+
* contract (issue [#2007](https://github.com/go-to-k/cdkd/issues/2007) tracks
|
|
61456
|
+
* the threading), and an identity masker would claim a check that never ran.
|
|
61457
|
+
*
|
|
61458
|
+
* The PROSE name goes through `displayIdent`, because this message is
|
|
61459
|
+
* persisted: a control byte or a newline in it would otherwise forge lines in
|
|
61460
|
+
* the events store even with the command suppressed. A clean name is a plain
|
|
61461
|
+
* identifier, which `displayIdent` renders unchanged.
|
|
61462
|
+
*
|
|
61463
|
+
* The caller marks the refusal NON-RETRYABLE: it is a deterministic verdict,
|
|
61464
|
+
* and `delete()` wraps it in a message carrying the LOGICAL id, which the
|
|
61465
|
+
* destroy runner and the deploy engine's replacement delete classify as
|
|
61466
|
+
* "already deleted" by SUBSTRING (`NotFoundException` there, a bare `NotFound`
|
|
61467
|
+
* here). A logical id containing the needle would otherwise have its non-empty
|
|
61468
|
+
* bucket read as gone; both classifiers skip a marked error.
|
|
61469
|
+
*/
|
|
61470
|
+
function notEmptyRefusalMessage(bucketName) {
|
|
61471
|
+
const command = bucketName !== "" && !bucketName.includes("/") && displaySafe(bucketName, { asciiOnly: true }) === bucketName ? renderDisableCommand({
|
|
61472
|
+
before: "aws s3 rm",
|
|
61473
|
+
identifier: `s3://${bucketName}`,
|
|
61474
|
+
after: "--recursive"
|
|
61475
|
+
}) : "";
|
|
61476
|
+
const remedy = command ? `Delete all objects first (e.g. ${command}) and destroy again.` : "Delete all objects first, via the console, and destroy again: the bucket name cdkd recorded cannot be reproduced safely on a command line, so any command shown here would act on a different bucket.";
|
|
61477
|
+
return `bucket ${displayIdent(bucketName)} is not empty. Matching CloudFormation, cdkd does not delete a non-empty directory bucket without an explicit opt-in. ${remedy}`;
|
|
61478
|
+
}
|
|
61479
|
+
/**
|
|
61121
61480
|
* SDK Provider for AWS::S3Express::DirectoryBucket
|
|
61122
61481
|
*
|
|
61123
61482
|
* Uses S3 SDK directly for S3 Express Directory Bucket operations.
|
|
@@ -61362,7 +61721,7 @@ var S3DirectoryBucketProvider = class {
|
|
|
61362
61721
|
} catch (error) {
|
|
61363
61722
|
const msg = describeAwsFailure(error).detail;
|
|
61364
61723
|
if (msg.includes("not empty") || msg.includes("BucketNotEmpty")) {
|
|
61365
|
-
if (!allowAutoEmpty) throw new Error(
|
|
61724
|
+
if (!allowAutoEmpty) throw markNonRetryable(new Error(notEmptyRefusalMessage(bucketName)));
|
|
61366
61725
|
this.logger.info(`Directory bucket ${bucketName} not empty (attempt ${attempt}/${maxAttempts}), emptying (auto-delete opt-in present)...`);
|
|
61367
61726
|
await this.emptyBucket(bucketName);
|
|
61368
61727
|
continue;
|
|
@@ -63497,7 +63856,7 @@ function nestedStackChildFailureMessage(childStackName, errorCount, alsoSkippedC
|
|
|
63497
63856
|
* `path.isAbsolute()` alone. Exported for unit testing.
|
|
63498
63857
|
*/
|
|
63499
63858
|
function isAbsoluteCrossPlatform$1(p) {
|
|
63500
|
-
return
|
|
63859
|
+
return isAbsoluteAssetPath(p);
|
|
63501
63860
|
}
|
|
63502
63861
|
/**
|
|
63503
63862
|
* Provider for `AWS::CloudFormation::Stack` — cdkd's recursive nested-stack
|
|
@@ -63584,14 +63943,14 @@ var NestedStackProvider = class {
|
|
|
63584
63943
|
const ctx = this.requireContext();
|
|
63585
63944
|
this.requireDeployContext(ctx, "create");
|
|
63586
63945
|
const childTemplatePath = ctx.nestedTemplates[logicalId];
|
|
63587
|
-
if (!childTemplatePath) throw new Error(`Nested template file not found for AWS::CloudFormation::Stack '${logicalId}' under parent '${ctx.parentStackName}'. Verify the synth output emits Metadata['aws:asset:path'] on this resource (CDK 2.x cdk.NestedStack does so by default).`);
|
|
63588
|
-
|
|
63946
|
+
if (!childTemplatePath) throw new Error(`Nested template file not found for AWS::CloudFormation::Stack '${displaySafe(logicalId)}' under parent '${displaySafe(ctx.parentStackName)}'. Verify the synth output emits Metadata['aws:asset:path'] on this resource (CDK 2.x cdk.NestedStack does so by default).`);
|
|
63947
|
+
this.refuseMalformedNestedTemplateTree(ctx, logicalId, childTemplatePath);
|
|
63948
|
+
const { template: childTemplate, grandchildTemplates } = this.readChildTemplate(childTemplatePath, ctx.assetRedirect);
|
|
63589
63949
|
const childStackName = this.deriveChildStackName(ctx.parentStackName, logicalId);
|
|
63590
63950
|
const childRegion = ctx.parentRegion;
|
|
63591
63951
|
const childParameters = this.extractParameters(properties);
|
|
63592
|
-
const grandchildTemplates = this.indexGrandchildTemplates(childTemplate, childTemplatePath);
|
|
63593
63952
|
const resourceCount = Object.keys(childTemplate.Resources ?? {}).length;
|
|
63594
|
-
this.logger.info(`Deploying nested stack ${childStackName} (logicalId=${logicalId}, ${resourceCount} resource(s))`);
|
|
63953
|
+
this.logger.info(`Deploying nested stack ${displaySafe(childStackName)} (logicalId=${displaySafe(logicalId)}, ${resourceCount} resource(s))`);
|
|
63595
63954
|
await this.runChildDeploy(ctx, logicalId, childStackName, childRegion, childTemplate, childParameters, grandchildTemplates);
|
|
63596
63955
|
const childOutputs = await this.readChildOutputsAsAttributes(ctx, childStackName, childRegion);
|
|
63597
63956
|
return {
|
|
@@ -63604,14 +63963,14 @@ var NestedStackProvider = class {
|
|
|
63604
63963
|
const ctx = this.requireContext();
|
|
63605
63964
|
this.requireDeployContext(ctx, "update");
|
|
63606
63965
|
const childTemplatePath = ctx.nestedTemplates[logicalId];
|
|
63607
|
-
if (!childTemplatePath) throw new Error(`Nested template file not found for AWS::CloudFormation::Stack '${logicalId}' on update.`);
|
|
63608
|
-
|
|
63966
|
+
if (!childTemplatePath) throw new Error(`Nested template file not found for AWS::CloudFormation::Stack '${displaySafe(logicalId)}' on update.`);
|
|
63967
|
+
this.refuseMalformedNestedTemplateTree(ctx, logicalId, childTemplatePath);
|
|
63968
|
+
const { template: childTemplate, grandchildTemplates } = this.readChildTemplate(childTemplatePath, ctx.assetRedirect);
|
|
63609
63969
|
const childStackName = this.deriveChildStackName(ctx.parentStackName, logicalId);
|
|
63610
63970
|
const childRegion = ctx.parentRegion;
|
|
63611
63971
|
const childParameters = this.extractParameters(properties);
|
|
63612
|
-
const grandchildTemplates = this.indexGrandchildTemplates(childTemplate, childTemplatePath);
|
|
63613
63972
|
const resourceCount = Object.keys(childTemplate.Resources ?? {}).length;
|
|
63614
|
-
this.logger.info(`Updating nested stack ${childStackName} (logicalId=${logicalId}, ${resourceCount} resource(s))`);
|
|
63973
|
+
this.logger.info(`Updating nested stack ${displaySafe(childStackName)} (logicalId=${displaySafe(logicalId)}, ${resourceCount} resource(s))`);
|
|
63615
63974
|
await this.runChildDeploy(ctx, logicalId, childStackName, childRegion, childTemplate, childParameters, grandchildTemplates);
|
|
63616
63975
|
const updatedOutputs = await this.readChildOutputsAsAttributes(ctx, childStackName, childRegion);
|
|
63617
63976
|
return {
|
|
@@ -63627,12 +63986,12 @@ var NestedStackProvider = class {
|
|
|
63627
63986
|
const childRegion = ctx.parentRegion;
|
|
63628
63987
|
const childStateData = await ctx.stateBackend.getState(childStackName, childRegion);
|
|
63629
63988
|
if (!childStateData) {
|
|
63630
|
-
this.logger.debug(`Nested stack ${childStackName} has no state — treating delete as idempotent success.`);
|
|
63989
|
+
this.logger.debug(`Nested stack ${displaySafe(childStackName)} has no state — treating delete as idempotent success.`);
|
|
63631
63990
|
return;
|
|
63632
63991
|
}
|
|
63633
63992
|
refuseMalformedResourcesForDestroy(childStateData.state, childStackName, childRegion);
|
|
63634
63993
|
const resourceCount = Object.keys(childStateData.state.resources).length;
|
|
63635
|
-
this.logger.info(`Destroying nested stack ${childStackName} (logicalId=${logicalId}, ${resourceCount} resource(s))`);
|
|
63994
|
+
this.logger.info(`Destroying nested stack ${displaySafe(childStackName)} (logicalId=${displaySafe(logicalId)}, ${resourceCount} resource(s))`);
|
|
63636
63995
|
const childCtx = {
|
|
63637
63996
|
...ctx,
|
|
63638
63997
|
parentStackName: childStackName,
|
|
@@ -63668,12 +64027,12 @@ var NestedStackProvider = class {
|
|
|
63668
64027
|
if (childResult.interrupted) causes.push("was interrupted");
|
|
63669
64028
|
return {
|
|
63670
64029
|
outcome: "skipped",
|
|
63671
|
-
reason: `nested stack ${childStackName} ${causes.join(" and ")}`
|
|
64030
|
+
reason: `nested stack ${displaySafe(childStackName)} ${causes.join(" and ")}`
|
|
63672
64031
|
};
|
|
63673
64032
|
}
|
|
63674
64033
|
}
|
|
63675
64034
|
async getAttribute(_physicalId, _resourceType, attributeName) {
|
|
63676
|
-
throw new Error(`AWS::CloudFormation::Stack: attribute '${attributeName}' is not in the recorded Outputs map. Only 'Outputs.<Key>' references to declared Output names on the child template are supported.`);
|
|
64035
|
+
throw new Error(`AWS::CloudFormation::Stack: attribute '${displaySafe(attributeName)}' is not in the recorded Outputs map. Only 'Outputs.<Key>' references to declared Output names on the child template are supported.`);
|
|
63677
64036
|
}
|
|
63678
64037
|
async runChildDeploy(parentCtx, logicalId, childStackName, childRegion, childTemplate, childParameters, grandchildTemplates) {
|
|
63679
64038
|
const inheritedSecrets = getCurrentResourceSecrets();
|
|
@@ -63724,7 +64083,7 @@ var NestedStackProvider = class {
|
|
|
63724
64083
|
*/
|
|
63725
64084
|
async readChildOutputsAsAttributes(ctx, childStackName, childRegion) {
|
|
63726
64085
|
const childStateData = await ctx.stateBackend.getState(childStackName, childRegion);
|
|
63727
|
-
if (!childStateData) throw new Error(`Child stack state '${childStackName}' not found after deploy — NestedStackProvider invariant violated.`);
|
|
64086
|
+
if (!childStateData) throw new Error(`Child stack state '${displaySafe(childStackName)}' not found after deploy — NestedStackProvider invariant violated.`);
|
|
63728
64087
|
refuseMalformedNestedChildOutputs(childStateData.state, childStackName, childRegion);
|
|
63729
64088
|
const attributes = this.buildOutputsAttributes(childStateData.state.outputs ?? {});
|
|
63730
64089
|
const noEchoAttributeNames = [];
|
|
@@ -63749,6 +64108,40 @@ var NestedStackProvider = class {
|
|
|
63749
64108
|
requireDeployContext(ctx, op) {
|
|
63750
64109
|
if (!ctx.nestedTemplates || !ctx.dagBuilder || !ctx.diffCalculator) throw new Error(`NestedStackProvider.${op}: deploy-mode context fields (nestedTemplates / dagBuilder / diffCalculator) are missing. This usually means a destroy-mode entry point called into create/update by mistake.`);
|
|
63751
64110
|
}
|
|
64111
|
+
/**
|
|
64112
|
+
* Validate the WHOLE nested-template subtree under this row before the first
|
|
64113
|
+
* child engine is built (issue go-to-k/cdkd#3247).
|
|
64114
|
+
*
|
|
64115
|
+
* Up front rather than as an ancestor chain threaded through the recursion,
|
|
64116
|
+
* because here a level is not a read, it is a DEPLOY: refusing at the first
|
|
64117
|
+
* repeat would leave every level above it provisioned, with a state record
|
|
64118
|
+
* each, for the rollback to unwind. Every file the recursion would follow is
|
|
64119
|
+
* already on disk, so the top-most row's call sees the entire cycle and
|
|
64120
|
+
* nothing below it ever starts. Deeper levels re-run the same walk over
|
|
64121
|
+
* their own (already proven) subtree; it is a few file reads next to a
|
|
64122
|
+
* deploy, and it keeps the guard independent of who built the context.
|
|
64123
|
+
*
|
|
64124
|
+
* This is the earliest point THIS FILE owns. The parent stack's other
|
|
64125
|
+
* resources may already be in flight when its nested-stack row is
|
|
64126
|
+
* dispatched; they are unwound by the parent's ordinary rollback. No level of
|
|
64127
|
+
* the cyclic tree is deployed.
|
|
64128
|
+
*
|
|
64129
|
+
* It also reports an absolute `aws:asset:path` at ANY depth, for the same
|
|
64130
|
+
* reason: `indexGrandchildTemplates` refuses one too, but only on reaching
|
|
64131
|
+
* that level, after the levels above it deployed. That check stays as the
|
|
64132
|
+
* per-level backstop.
|
|
64133
|
+
*
|
|
64134
|
+
* `markNonRetryable`: the verdict is computed from files no retry changes,
|
|
64135
|
+
* while the message carries template-controlled text (logical ids, paths)
|
|
64136
|
+
* that the SUBSTRING-matching retry classifier would otherwise read. Latent
|
|
64137
|
+
* while `disableOuterRetry` is set, and declared anyway, like the sibling
|
|
64138
|
+
* marks in this file.
|
|
64139
|
+
*/
|
|
64140
|
+
refuseMalformedNestedTemplateTree(ctx, logicalId, childTemplatePath) {
|
|
64141
|
+
const defect = findNestedTemplateTreeDefect({ [logicalId]: childTemplatePath });
|
|
64142
|
+
if (!defect) return;
|
|
64143
|
+
throw markNonRetryable(/* @__PURE__ */ new Error(`NestedStackProvider: ${renderNestedTemplateTreeDefect(defect, ctx.parentStackName, "deploy any level of it")}`));
|
|
64144
|
+
}
|
|
63752
64145
|
deriveChildStackName(parentStackName, nestedLogicalId) {
|
|
63753
64146
|
return `${parentStackName}~${nestedLogicalId}`;
|
|
63754
64147
|
}
|
|
@@ -63793,35 +64186,50 @@ var NestedStackProvider = class {
|
|
|
63793
64186
|
* points at `[2]` rather than at the whole parameter.
|
|
63794
64187
|
*/
|
|
63795
64188
|
refuseNonScalarParameter(k, offender, where) {
|
|
63796
|
-
throw new Error(`NestedStackProvider: child Parameter '${k}'${where} resolved to a non-scalar value (type=${offender === null ? "null" : typeof offender}). Parameters must be scalars (string / number / boolean), or an ARRAY of them from a list-typed parameter, by the time they reach the provider — an unresolved intrinsic here means IntrinsicFunctionResolver upstream did not handle the value, which is a bug. Surface the unresolved input rather than silently coercing to '[object Object]'.`);
|
|
64189
|
+
throw new Error(`NestedStackProvider: child Parameter '${displaySafe(k)}'${where} resolved to a non-scalar value (type=${offender === null ? "null" : typeof offender}). Parameters must be scalars (string / number / boolean), or an ARRAY of them from a list-typed parameter, by the time they reach the provider — an unresolved intrinsic here means IntrinsicFunctionResolver upstream did not handle the value, which is a bug. Surface the unresolved input rather than silently coercing to '[object Object]'.`);
|
|
63797
64190
|
}
|
|
64191
|
+
/**
|
|
64192
|
+
* The child template as the child engine deploys it, plus the grandchild
|
|
64193
|
+
* template paths indexed from it.
|
|
64194
|
+
*
|
|
64195
|
+
* The index is taken BEFORE the asset-reference rewrite, and returning both
|
|
64196
|
+
* from here is what keeps that order from being undone by a caller. The
|
|
64197
|
+
* rewrite walks every string in the template, `Metadata['aws:asset:path']`
|
|
64198
|
+
* included, and a path segment that spells a bootstrap bucket name is
|
|
64199
|
+
* rewritten like any other occurrence. `aws:asset:path` is a LOCAL file
|
|
64200
|
+
* path, never an asset-storage reference; indexing after the rewrite made
|
|
64201
|
+
* the deploy follow a different file than the one
|
|
64202
|
+
* `refuseMalformedNestedTemplateTree` had validated from the raw file, which
|
|
64203
|
+
* is a way around that guard (issue go-to-k/cdkd#3247).
|
|
64204
|
+
*/
|
|
63798
64205
|
readChildTemplate(templatePath, assetRedirect) {
|
|
63799
64206
|
let raw;
|
|
63800
64207
|
try {
|
|
63801
64208
|
raw = fs.readFileSync(templatePath, "utf-8");
|
|
63802
64209
|
} catch (err) {
|
|
63803
|
-
throw new Error(`Failed to read nested template at ${templatePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
64210
|
+
throw new Error(`Failed to read nested template at ${displaySafe(templatePath)}: ${displaySafe(err instanceof Error ? err.message : String(err))}`);
|
|
63804
64211
|
}
|
|
63805
64212
|
let template;
|
|
63806
64213
|
try {
|
|
63807
64214
|
template = JSON.parse(raw);
|
|
63808
64215
|
} catch (err) {
|
|
63809
|
-
throw new Error(`Failed to parse nested template at ${templatePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
64216
|
+
throw new Error(`Failed to parse nested template at ${displaySafe(templatePath)}: ${displaySafe(err instanceof Error ? err.message : String(err))}`);
|
|
63810
64217
|
}
|
|
64218
|
+
const grandchildTemplates = this.indexGrandchildTemplates(template, templatePath);
|
|
63811
64219
|
if (assetRedirect) {
|
|
63812
64220
|
const n = rewriteTemplateAssetReferences(template, assetRedirect);
|
|
63813
|
-
if (n > 0) this.logger.debug(`Rewrote ${n} asset reference(s) to cdkd asset storage in nested template ${templatePath}`);
|
|
64221
|
+
if (n > 0) this.logger.debug(`Rewrote ${n} asset reference(s) to cdkd asset storage in nested template ${displaySafe(templatePath)}`);
|
|
63814
64222
|
}
|
|
63815
|
-
return
|
|
64223
|
+
return {
|
|
64224
|
+
template,
|
|
64225
|
+
grandchildTemplates
|
|
64226
|
+
};
|
|
63816
64227
|
}
|
|
63817
64228
|
indexGrandchildTemplates(childTemplate, childTemplatePath) {
|
|
63818
64229
|
const dir = path.dirname(childTemplatePath);
|
|
63819
|
-
const result =
|
|
63820
|
-
for (const
|
|
63821
|
-
if (
|
|
63822
|
-
const assetPath = resource.Metadata?.["aws:asset:path"];
|
|
63823
|
-
if (typeof assetPath !== "string" || assetPath.length === 0) continue;
|
|
63824
|
-
if (isAbsoluteCrossPlatform$1(assetPath)) throw new Error(`NestedStackProvider: nested-stack '${grandLogicalId}' has Metadata['aws:asset:path']='${assetPath}' which is absolute. CDK emits relative asset paths for nested templates; an absolute path indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to load.`);
|
|
64230
|
+
const result = Object.create(null);
|
|
64231
|
+
for (const { logicalId: grandLogicalId, assetPath } of listNestedTemplateRows(childTemplate)) {
|
|
64232
|
+
if (isAbsoluteCrossPlatform$1(assetPath)) throw markNonRetryable(/* @__PURE__ */ new Error(`NestedStackProvider: nested-stack '${displaySafe(grandLogicalId)}' has Metadata['aws:asset:path']='${displaySafe(assetPath)}' which is absolute. CDK emits relative asset paths for nested templates; an absolute path indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to load.`));
|
|
63825
64233
|
result[grandLogicalId] = path.join(dir, assetPath);
|
|
63826
64234
|
}
|
|
63827
64235
|
return result;
|
|
@@ -64873,6 +65281,7 @@ async function deployCommand(stacks, options) {
|
|
|
64873
65281
|
strict: options.strict === true,
|
|
64874
65282
|
ignoreErrors: options.ignoreErrors === true
|
|
64875
65283
|
});
|
|
65284
|
+
refuseMalformedNestedTemplateTrees(targetStacks);
|
|
64876
65285
|
await synthesizer.expandMacrosForStacks(targetStacks, synthOptions);
|
|
64877
65286
|
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
64878
65287
|
const stsClient = new STSClient({
|
|
@@ -81497,6 +81906,15 @@ function safeRoleArn(value) {
|
|
|
81497
81906
|
* the Snapshot label would otherwise promise a final snapshot the run is about
|
|
81498
81907
|
* to skip — a data-loss-relevant lie in the one preview the user reads.
|
|
81499
81908
|
*/
|
|
81909
|
+
/**
|
|
81910
|
+
* The type(s) a reversed replacement touches (issue #2668): `NEW -> OLD` when
|
|
81911
|
+
* the replacement changed the resource's `Type` — the replay deletes the first
|
|
81912
|
+
* and re-creates the second — and the single type otherwise.
|
|
81913
|
+
*/
|
|
81914
|
+
function replacementTypes(op) {
|
|
81915
|
+
const routing = resolveReplacementOldType(op);
|
|
81916
|
+
return routing.ok && routing.oldType !== op.resourceType ? `${safe(op.resourceType)} -> ${safe(routing.oldType)}` : safe(op.resourceType);
|
|
81917
|
+
}
|
|
81500
81918
|
function actionLabel(item, skipFinalSnapshot) {
|
|
81501
81919
|
const { op, action, replacement } = item;
|
|
81502
81920
|
const rep = replacement ? " [replacement occurred, best-effort revert]" : "";
|
|
@@ -81506,11 +81924,15 @@ function actionLabel(item, skipFinalSnapshot) {
|
|
|
81506
81924
|
case "orphan-retain": return ` - orphan ${safe(op.logicalId)} (${safe(op.resourceType)}) [DeletionPolicy Retain — left in AWS]`;
|
|
81507
81925
|
case "orphan-flag": return ` - orphan ${safe(op.logicalId)} (${safe(op.resourceType)}) [--orphan]`;
|
|
81508
81926
|
case "revert": return ` - revert ${safe(op.logicalId)} (${safe(op.resourceType)})${rep}`;
|
|
81509
|
-
case "reverse-replacement": return item.retainsNewResource ? ` - reverse-replace ${safe(op.logicalId)} (${
|
|
81510
|
-
case "reverse-replacement-readopt": return item.retainsNewResource ? ` - reverse-replace ${safe(op.logicalId)} (${
|
|
81927
|
+
case "reverse-replacement": return item.retainsNewResource ? ` - reverse-replace ${safe(op.logicalId)} (${replacementTypes(op)}) [re-create old resource; new one RETAINED (UpdateReplacePolicy: Retain) and left untracked — REFUSED instead if the re-create collides with the name the retained resource still holds]` : ` - reverse-replace ${safe(op.logicalId)} (${replacementTypes(op)}) [re-create old resource, delete new]`;
|
|
81928
|
+
case "reverse-replacement-readopt": return item.retainsNewResource ? ` - reverse-replace ${safe(op.logicalId)} (${replacementTypes(op)}) [re-adopt retained old resource; new one RETAINED (UpdateReplacePolicy: Retain) and left untracked]` : ` - reverse-replace ${safe(op.logicalId)} (${replacementTypes(op)}) [delete new, re-adopt retained old resource]`;
|
|
81511
81929
|
case "unrecoverable-delete": return ` - (cannot restore) ${safe(op.logicalId)} (${safe(op.resourceType)}) — was DELETED, unrecoverable`;
|
|
81512
81930
|
case "skip-mismatch": return ` - skip ${safe(op.logicalId)} (${safe(op.resourceType)}) — physical id changed, needs manual attention`;
|
|
81513
81931
|
case "skip-absent": return ` - skip ${safe(op.logicalId)} (${safe(op.resourceType)}) — no longer in state`;
|
|
81932
|
+
case "refuse-replacement-routing": {
|
|
81933
|
+
const routing = resolveReplacementOldType(op);
|
|
81934
|
+
return ` - (REFUSED) ${safe(op.logicalId)} (${safe(op.resourceType)}) — cannot reverse the replacement: ${routing.ok ? "its old type could not be routed" : routing.reason}`;
|
|
81935
|
+
}
|
|
81514
81936
|
case "skip-already-done": return ` - skip ${safe(op.logicalId)} (${safe(op.resourceType)}) — already reverted`;
|
|
81515
81937
|
}
|
|
81516
81938
|
}
|
|
@@ -81530,6 +81952,7 @@ function failedActionLabel(item, skipFinalSnapshot) {
|
|
|
81530
81952
|
case "skip-failed-unknown": return ` - skip ${safe(op.logicalId)} (${safe(op.resourceType)}) — failed CREATE recorded no physical id`;
|
|
81531
81953
|
case "skip-failed-noop": return ` - skip ${safe(op.logicalId)} (${safe(op.resourceType)}) — failed ${safe(op.changeType)} left nothing to revert`;
|
|
81532
81954
|
case "skip-failed-absent": return ` - skip ${safe(op.logicalId)} (${safe(op.resourceType)}) — no previous state available`;
|
|
81955
|
+
case "skip-failed-type-change": return ` - skip ${safe(op.logicalId)} (${safe(op.previousState?.resourceType)} -> ${safe(op.resourceType)}) — failed Type change is a replacement, no in-place revert exists`;
|
|
81533
81956
|
}
|
|
81534
81957
|
}
|
|
81535
81958
|
/**
|
|
@@ -95615,4 +96038,4 @@ function buildProgram() {
|
|
|
95615
96038
|
|
|
95616
96039
|
//#endregion
|
|
95617
96040
|
export { buildProgram };
|
|
95618
|
-
//# sourceMappingURL=program-
|
|
96041
|
+
//# sourceMappingURL=program-Dh1J573K.js.map
|