@go-to-k/cdkd 0.280.26 → 0.280.28
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-BJsjMRaA.js → asg-provider-C3rBE_Ni.js} +2 -2
- package/dist/{asg-provider-BJsjMRaA.js.map → asg-provider-C3rBE_Ni.js.map} +1 -1
- package/dist/cli.js +3 -3
- package/dist/{deploy-engine-Sb8KbTlA.js → deploy-engine-Dkep5i7x.js} +174 -5
- package/dist/deploy-engine-Dkep5i7x.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-Sb8KbTlA.js.map +0 -1
|
@@ -7985,9 +7985,43 @@ function isIamPropagationError(message) {
|
|
|
7985
7985
|
* and the rollback executor's reverse-replacement) — everywhere else it is a
|
|
7986
7986
|
* genuine conflict that must fail fast. Shared by those sites' collision
|
|
7987
7987
|
* detection + retry filters so a signature extension lands in one place.
|
|
7988
|
+
*
|
|
7989
|
+
* The optional `s` is load-bearing, not defensive spelling (issue #1625):
|
|
7990
|
+
* Lambda's `CreateFunction` raises `ResourceConflictException: Function
|
|
7991
|
+
* already exist: <name>` — SINGULAR — so the `already exists` form missed it
|
|
7992
|
+
* entirely and NO Lambda function could take the collision path. The
|
|
7993
|
+
* consequence was not a cosmetic message: a property-driven replacement of a
|
|
7994
|
+
* Lambda (dropping `DurableConfig`, changing `TenancyConfig`) create-firsts
|
|
7995
|
+
* into its own still-live name, the raw `ResourceConflictException` escaped
|
|
7996
|
+
* instead of the actionable `NAMED_REPLACEMENT_COLLISION` error, and
|
|
7997
|
+
* `cdkd deploy --replace`'s delete-first fallback never fired — so the
|
|
7998
|
+
* replacement was unperformable by any flag. Verified against real AWS
|
|
7999
|
+
* (us-east-1, 2026-08-12) by creating one function name twice.
|
|
8000
|
+
*
|
|
8001
|
+
* Two fences keep the widened arm from crediting a NON-collision, which
|
|
8002
|
+
* matters because the sites that consult it react DESTRUCTIVELY (the
|
|
8003
|
+
* `--replace` delete-first fallback deletes the old resource):
|
|
8004
|
+
* - `\b` after `exists?` rejects a participle ("already existed as a draft");
|
|
8005
|
+
* - the lookbehind rejects a NEGATED or MODAL phrase — "the bucket does NOT
|
|
8006
|
+
* already exist", "the destination bucket MUST already exist" — which the
|
|
8007
|
+
* bare pattern matched. The modal form is the one that bites: a create
|
|
8008
|
+
* rejected for a missing PREREQUISITE would be reported to the user as a
|
|
8009
|
+
* name collision pointing at `--replace`, and following that advice
|
|
8010
|
+
* deletes the live old resource before the re-create fails again for the
|
|
8011
|
+
* same reason, leaving it absent from AWS with state still recording it.
|
|
8012
|
+
* The error-CODE arm stays exact (`AlreadyExists`): the singular
|
|
8013
|
+
* `AlreadyExist` is not an AWS code spelling, and loosening it would match
|
|
8014
|
+
* inside unrelated identifiers.
|
|
8015
|
+
*
|
|
8016
|
+
* Classification stays MESSAGE-based rather than moving to the exception
|
|
8017
|
+
* NAME, and that is load-bearing here rather than inherited: Lambda raises
|
|
8018
|
+
* `ResourceConflictException` for a function in a PENDING state too (see
|
|
8019
|
+
* `lambda-function-provider.ts`), so keying on the name would classify a
|
|
8020
|
+
* transient state conflict as a collision and delete a live function under
|
|
8021
|
+
* `--replace`.
|
|
7988
8022
|
*/
|
|
7989
8023
|
function isNameCollisionError(message) {
|
|
7990
|
-
return /already exists/i.test(message) || message.includes("AlreadyExists");
|
|
8024
|
+
return /(?<!\b(?:must|not|should|may|cannot)\s)already exists?\b/i.test(message) || message.includes("AlreadyExists");
|
|
7991
8025
|
}
|
|
7992
8026
|
/**
|
|
7993
8027
|
* Match the SQS same-name re-creation cooldown: after `DeleteQueue`, creating
|
|
@@ -12836,7 +12870,7 @@ var CloudControlProvider = class {
|
|
|
12836
12870
|
if (context?.finalSnapshotIdentifier !== void 0) throw new ProvisioningError(`${logicalId} (${resourceType}) requires a final snapshot (DeletionPolicy: Snapshot), but the Cloud Control API delete route has no final-snapshot parameter. Re-run with --skip-final-snapshot after snapshotting manually, or retain the resource.`, resourceType, logicalId, physicalId);
|
|
12837
12871
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
12838
12872
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
12839
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
12873
|
+
const { ASGProvider } = await import("./asg-provider-C3rBE_Ni.js").then((n) => n.n);
|
|
12840
12874
|
await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
12841
12875
|
return;
|
|
12842
12876
|
}
|
|
@@ -16440,6 +16474,111 @@ function findActionableSilentDrops(resourceType, templateProperties, allowedKeys
|
|
|
16440
16474
|
return drops.filter(({ property }) => !allowedKeys.has(`${resourceType}:${property}`));
|
|
16441
16475
|
}
|
|
16442
16476
|
|
|
16477
|
+
//#endregion
|
|
16478
|
+
//#region src/provisioning/mutually-exclusive-properties.ts
|
|
16479
|
+
/**
|
|
16480
|
+
* The rule table, keyed by CFn resource type.
|
|
16481
|
+
*
|
|
16482
|
+
* Deliberately seeded with the ONE combination this repo has verified end to
|
|
16483
|
+
* end (`AWS::EC2::Route`, refused by `EC2Provider.createRoute` since #1566 and
|
|
16484
|
+
* normalized on both diff sides since #1591). The mechanism is general; the
|
|
16485
|
+
* data is not speculative. See "Adding a rule" above.
|
|
16486
|
+
*/
|
|
16487
|
+
const MUTUALLY_EXCLUSIVE_PROPERTIES = /* @__PURE__ */ new Map([["AWS::EC2::Route", [{
|
|
16488
|
+
properties: [
|
|
16489
|
+
"DestinationCidrBlock",
|
|
16490
|
+
"DestinationIpv6CidrBlock",
|
|
16491
|
+
"DestinationPrefixListId"
|
|
16492
|
+
],
|
|
16493
|
+
rationale: "CloudFormation and the EC2 CreateRoute API accept exactly one destination per route.",
|
|
16494
|
+
firstDeclaredWins: true
|
|
16495
|
+
}]]]);
|
|
16496
|
+
/**
|
|
16497
|
+
* True for a single-key object whose key is `Ref` or `Fn::*` — the shape of an
|
|
16498
|
+
* unresolved CloudFormation intrinsic. Mirrors `isIntrinsicShaped` in
|
|
16499
|
+
* {@link ./create-only-properties}; kept local because the two modules answer
|
|
16500
|
+
* different questions about the shape and should not drift into one another's
|
|
16501
|
+
* behavior by accident.
|
|
16502
|
+
*/
|
|
16503
|
+
function isUnresolvedIntrinsic(value) {
|
|
16504
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
16505
|
+
const keys = Object.keys(value);
|
|
16506
|
+
return keys.length === 1 && (keys[0] === "Ref" || keys[0].startsWith("Fn::"));
|
|
16507
|
+
}
|
|
16508
|
+
/**
|
|
16509
|
+
* True when `value` is unconditionally present in the template.
|
|
16510
|
+
*
|
|
16511
|
+
* The predicate is `Boolean`, not a hand-listed `undefined | null | ''` set,
|
|
16512
|
+
* matching `narrowRouteDestinations`' truthiness narrowing in
|
|
16513
|
+
* `ec2-provider.ts`: the property bag is `unknown`-valued at runtime, so an
|
|
16514
|
+
* unquoted YAML `0` must be skipped here exactly as the provider's `||` chain
|
|
16515
|
+
* skips it — otherwise pre-flight refuses a template the provider would have
|
|
16516
|
+
* accepted. An unresolved intrinsic is truthy but its resolved presence is
|
|
16517
|
+
* NOT yet known, so it is excluded ahead of the truthiness test.
|
|
16518
|
+
*/
|
|
16519
|
+
function isDeclared(value) {
|
|
16520
|
+
if (isUnresolvedIntrinsic(value)) return false;
|
|
16521
|
+
return Boolean(value);
|
|
16522
|
+
}
|
|
16523
|
+
/**
|
|
16524
|
+
* Find every mutually-exclusive rule this resource violates.
|
|
16525
|
+
*
|
|
16526
|
+
* Returns `[]` for a type with no rules, an absent property bag, and any
|
|
16527
|
+
* combination that is only reached through an unresolved intrinsic.
|
|
16528
|
+
*/
|
|
16529
|
+
function findMutuallyExclusiveViolations(resourceType, templateProperties) {
|
|
16530
|
+
if (!templateProperties) return [];
|
|
16531
|
+
const rules = MUTUALLY_EXCLUSIVE_PROPERTIES.get(resourceType);
|
|
16532
|
+
if (!rules) return [];
|
|
16533
|
+
return findViolationsForRules(resourceType, rules, templateProperties);
|
|
16534
|
+
}
|
|
16535
|
+
/**
|
|
16536
|
+
* The table-free core of {@link findMutuallyExclusiveViolations}, taking the
|
|
16537
|
+
* rules explicitly.
|
|
16538
|
+
*
|
|
16539
|
+
* Exported so the multi-rule and multi-violation paths are reachable from
|
|
16540
|
+
* tests: the shipped table has a single type with a single rule, so those
|
|
16541
|
+
* branches would otherwise be dead code that no assertion can exercise.
|
|
16542
|
+
*/
|
|
16543
|
+
function findViolationsForRules(resourceType, rules, templateProperties) {
|
|
16544
|
+
const violations = [];
|
|
16545
|
+
for (const rule of rules) {
|
|
16546
|
+
const declared = rule.properties.filter((property) => isDeclared(templateProperties[property]));
|
|
16547
|
+
if (declared.length <= 1) continue;
|
|
16548
|
+
const winnerCertain = !rule.properties.slice(0, rule.properties.indexOf(declared[0])).some((property) => isUnresolvedIntrinsic(templateProperties[property]));
|
|
16549
|
+
violations.push({
|
|
16550
|
+
resourceType,
|
|
16551
|
+
rule,
|
|
16552
|
+
declared,
|
|
16553
|
+
winnerCertain
|
|
16554
|
+
});
|
|
16555
|
+
}
|
|
16556
|
+
return violations;
|
|
16557
|
+
}
|
|
16558
|
+
/**
|
|
16559
|
+
* Render one violation as a per-resource error line.
|
|
16560
|
+
*
|
|
16561
|
+
* For a `firstDeclaredWins` rule the message names the key that would actually
|
|
16562
|
+
* be SENT, which is what makes the remedy actionable.
|
|
16563
|
+
*
|
|
16564
|
+
* It deliberately stops short of promising the deployed resource is unaffected.
|
|
16565
|
+
* Pre-flight has no state, so it cannot know which destination the LIVE
|
|
16566
|
+
* resource was created from: for a route deployed with only
|
|
16567
|
+
* `DestinationIpv6CidrBlock` whose template later GAINS
|
|
16568
|
+
* `DestinationCidrBlock`, deleting the IPv6 key makes the CIDR key the sole
|
|
16569
|
+
* destination — a create-only change that REPLACES the route. Saying "removing
|
|
16570
|
+
* the others leaves the resource unchanged" would be false exactly there.
|
|
16571
|
+
*
|
|
16572
|
+
* The sentence is omitted entirely when
|
|
16573
|
+
* {@link MutuallyExclusiveViolation.winnerCertain} is false — naming a winner
|
|
16574
|
+
* the raw template does not determine would be a confident falsehood.
|
|
16575
|
+
*/
|
|
16576
|
+
function buildMutuallyExclusiveMessage(logicalId, violation) {
|
|
16577
|
+
const { resourceType, rule, declared, winnerCertain } = violation;
|
|
16578
|
+
const winnerNote = rule.firstDeclaredWins && winnerCertain ? ` cdkd would send only ${declared[0]}; ${declared.slice(1).join(" / ")} would be dropped. Deleting the dropped keys changes nothing cdkd sends — but if the LIVE resource was created from one of them, making ${declared[0]} the sole value is a create-only change that REPLACES it.` : "";
|
|
16579
|
+
return ` - ${logicalId} (${resourceType}) declares ${declared.join(" and ")}\n ${rule.rationale}${winnerNote}\n Declare at most one of: ${rule.properties.join(" / ")}`;
|
|
16580
|
+
}
|
|
16581
|
+
|
|
16443
16582
|
//#endregion
|
|
16444
16583
|
//#region src/provisioning/provider-registry.ts
|
|
16445
16584
|
/**
|
|
@@ -16738,11 +16877,41 @@ var ProviderRegistry = class {
|
|
|
16738
16877
|
* the property check is a no-op (`findSilentDropProperties` returns `[]`
|
|
16739
16878
|
* for non-Tier-1 / unknown types).
|
|
16740
16879
|
*
|
|
16880
|
+
* Since issue [#1634](https://github.com/go-to-k/cdkd/issues/1634) this
|
|
16881
|
+
* ALSO runs the mutually-exclusive-property check
|
|
16882
|
+
* ({@link validateMutuallyExclusiveProperties}), which throws BEFORE any
|
|
16883
|
+
* routing decision is logged — a template CloudFormation itself rejects
|
|
16884
|
+
* should not first produce a page of routing chatter.
|
|
16885
|
+
*
|
|
16741
16886
|
* @see findAutoRouteHits for the pure-functional pre-deploy plan-builder
|
|
16742
16887
|
* that returns the same information without logging.
|
|
16743
16888
|
*/
|
|
16744
16889
|
validateResourceProperties(resources) {
|
|
16745
|
-
|
|
16890
|
+
const materialized = [...resources];
|
|
16891
|
+
this.validateMutuallyExclusiveProperties(materialized);
|
|
16892
|
+
this.reportSilentDropDecisions(materialized);
|
|
16893
|
+
}
|
|
16894
|
+
/**
|
|
16895
|
+
* Reject a template that declares two or more MUTUALLY EXCLUSIVE top-level
|
|
16896
|
+
* properties on one resource (issue
|
|
16897
|
+
* [#1634](https://github.com/go-to-k/cdkd/issues/1634)).
|
|
16898
|
+
*
|
|
16899
|
+
* Aggregated into ONE error listing every offending resource, mirroring
|
|
16900
|
+
* {@link validateResourceTypes} — a template with three bad routes should
|
|
16901
|
+
* report three, not fail three deploys in a row. There is deliberately no
|
|
16902
|
+
* `--allow-*` escape hatch: the combination is invalid at CloudFormation and
|
|
16903
|
+
* at the service API, so the only correct outcome is a template edit (see
|
|
16904
|
+
* the rule module's header).
|
|
16905
|
+
*
|
|
16906
|
+
* Unlike the provider-side refusal this fires even when the resource already
|
|
16907
|
+
* exists and the deploy diff classifies NO_CHANGE, which is the gap the
|
|
16908
|
+
* issue was filed for.
|
|
16909
|
+
*/
|
|
16910
|
+
validateMutuallyExclusiveProperties(resources) {
|
|
16911
|
+
const lines = [];
|
|
16912
|
+
for (const { logicalId, resourceType, properties } of resources) for (const violation of findMutuallyExclusiveViolations(resourceType, properties)) lines.push(buildMutuallyExclusiveMessage(logicalId, violation));
|
|
16913
|
+
if (lines.length === 0) return;
|
|
16914
|
+
throw new Error(`The following resources declare mutually exclusive properties:\n` + lines.join("\n") + "\n\nCloudFormation rejects these combinations too — edit the template to declare only one of each set.");
|
|
16746
16915
|
}
|
|
16747
16916
|
/**
|
|
16748
16917
|
* Info-log every silent-drop routing decision (auto-route via CC API) and
|
|
@@ -19352,7 +19521,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
19352
19521
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
19353
19522
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
19354
19523
|
function getCdkdVersion() {
|
|
19355
|
-
return "0.280.
|
|
19524
|
+
return "0.280.28";
|
|
19356
19525
|
}
|
|
19357
19526
|
/**
|
|
19358
19527
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -21485,4 +21654,4 @@ var DeployEngine = class {
|
|
|
21485
21654
|
|
|
21486
21655
|
//#endregion
|
|
21487
21656
|
export { requireConfigObject as $, AssemblyReader as $t, green as A, __exportAll as An, runDockerForeground as At, disableInstanceApiTermination as B, resolveCaptureObservedState as Bt, MULTI_REGION_RECREATE_BLOCKED_TYPES as C, StackTerminationProtectionError as Cn, getBootstrapMarkerKey as Ct, bold as D, isCdkdError as Dn, buildDockerImage as Dt, formatResourceLine as E, formatError as En, validateContainerRepoName as Et, clearOnUpdateRemoval as F, synthesisStatusMessage as Ft, WAFv2WebACLProvider as G, stateBucketExistenceConfirmed as Gt, IntrinsicFunctionResolver as H, resolveStateBucketWithDefault as Ht, ProviderRegistry as I, getDefaultStateBucketName as It, assertRegionMatch as J, CFN_TEMPLATE_URL_LIMIT as Jt, normalizeAwsTagsToCfn as K, warnDeprecatedNoPrefixCliFlag as Kt, findActionableSilentDrops as L, getLegacyStateBucketName as Lt, yellow as M, AssetManifestLoader as Mt, IAMRoleProvider as N, getDockerImageBySourceHash as Nt, cyan as O, normalizeAwsError as On, formatDockerLoginError as Ot, collectInlinePolicyNamesManagedBySiblings as P, Synthesizer as Pt, requireConfigArray as Q, expectedOwnerParam as Qt, CloudControlProvider as R, resolveApp as Rt, extractDeploymentEventError as S, StackHasActiveImportsError as Sn, ensureAssetStorage as St, renderStatefulReason as T, SynthesisError as Tn, validateAssetBucketName as Tt, cfnRefValueFromPhysicalId as U, resolveStateBucketWithDefaultAndSource as Ut, isTerminationProtectionPropagationError as V, resolveSkipPrefix as Vt, refStateLookupFromResource as W, resolveUseCdkBootstrapAssets as Wt, readConfigString as X, findLargeInlineResources as Xt, configStringRefusal as Y, MIGRATE_TMP_PREFIX as Yt, replayWarn as Z, uploadCfnTemplate as Zt, createPreDeleteFinalSnapshot as _, NestedStackChildDirectDestroyError as _n, createAssetRedirectResolver as _t, DeploymentEventsStore as a, resetAwsClients as an, isRetryableTransientError as at, unsupportedFinalSnapshotError as b, ResourceTimeoutError as bn, AssetModeResolver as bt, replayFailedOperations as c, CdkdError as cn, TemplateParser as ct, IMPLICIT_DELETE_DEPENDENCIES as d, DeployCancelledError as dn, rebuildClientForBucketRegion as dt, processStackMessages as en, requireConfigString as et, computeImplicitDeleteEdges as f, LocalInvokeBuildError as fn, shouldRetainResource as ft, ccRoutedFinalSnapshotError as g, MissingCdkCliError as gn, buildAssetRedirectMap as gt, buildFinalSnapshotIdentifier as h, LockError as hn, WorkGraph as ht, DeploymentEventsReader as i, getAwsClients as in, withRetry as it, red as j, runDockerStreaming as jt, gray as k, withErrorHandling as kn, getDockerCmd as kt, replayRollback as l, ConfigError as ln, LockManager as lt, PRE_DELETE_SNAPSHOT_TYPES as m, LocalStartServiceError as mn, stringifyValue as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, resolveBucketRegion as nn, DiffCalculator as nt, planFailedOps as o, setAwsClients as on, isThrottlingError as ot, ATOMIC_FINAL_SNAPSHOT_TYPES as p, LocalMigrateError as pn, AssetPublisher as pt, resolveExplicitPhysicalId as q, CFN_TEMPLATE_BODY_LIMIT as qt, DeployEngine as r, AwsClients as rn, describeTypeWithThrottleRetry as rt, planRollback as s, AssetError as sn, DagBuilder as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, clearBucketRegionCache as tn, applyRoleArnIfSet as tt, withResourceDeadline as u, DependencyError as un, S3StateBackend as ut, isFinalSnapshotError as v, PartialFailureError as vn, loadPublishableAssetManifest as vt, isStatefulRecreateTargetSync as w, StateError as wn, parseBootstrapMarker as wt, makeCanonicalizePropertiesFn as x, ResourceUpdateNotSupportedError as xn, BOOTSTRAP_MARKER_PREFIX as xt, refusesFinalSnapshot as y, ProvisioningError as yn, rewriteTemplateAssetReferences as yt, slowCcOperationTimeoutMs as z, resolveAutoAssetStorage as zt };
|
|
21488
|
-
//# sourceMappingURL=deploy-engine-
|
|
21657
|
+
//# sourceMappingURL=deploy-engine-Dkep5i7x.js.map
|