@go-to-k/cdkd 0.281.14 → 0.281.16
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-C1WbHit9.js → asg-provider-B1pHGT_X.js} +2 -2
- package/dist/{asg-provider-C1WbHit9.js.map → asg-provider-B1pHGT_X.js.map} +1 -1
- package/dist/cli.js +554 -134
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-3BZtmJqZ.js → deploy-engine-CfnMSwyx.js} +3 -3
- package/dist/{deploy-engine-3BZtmJqZ.js.map → deploy-engine-CfnMSwyx.js.map} +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as replayWarn, $t as findLargeInlineResources, A as green, At as buildDockerImage, B as slowCcOperationTimeoutMs, Bt as getLegacyStateBucketName, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as ResourceTimeoutError, Ct as AssetModeResolver, D as bold, Dt as parseBootstrapMarker, E as formatResourceLine, En as StackTerminationProtectionError, Et as getBootstrapMarkerKey, F as clearOnUpdateRemoval, Ft as AssetManifestLoader, G as getAccountInfo, Gt as resolveStateBucketWithDefault, H as isTerminationProtectionPropagationError, Ht as resolveAutoAssetStorage, I as ProviderRegistry, It as getDockerImageBySourceHash, J as normalizeAwsTagsToCfn, Jt as stateBucketExistenceConfirmed, K as refStateLookupFromResource, Kt as resolveStateBucketWithDefaultAndSource, L as findActionableSilentDrops, Lt as Synthesizer, M as yellow, Mn as withErrorHandling, Mt as getDockerCmd, N as IAMRoleProvider, Nt as runDockerForeground, O as cyan, Ot as validateAssetBucketName, P as collectInlinePolicyNamesManagedBySiblings, Pt as runDockerStreaming, Q as readConfigString, Qt as MIGRATE_TMP_PREFIX, R as findSilentDropProperties, Rt as synthesisStatusMessage, S as extractDeploymentEventError, Sn as ProvisioningError, St as rewriteTemplateAssetReferences, T as renderStatefulReason, Tn as StackHasActiveImportsError, Tt as ensureAssetStorage, U as IntrinsicFunctionResolver, Ut as resolveCaptureObservedState, V as disableInstanceApiTermination, Vt as resolveApp, W as cfnRefValueFromPhysicalId, Wt as resolveSkipPrefix, X as assertRegionMatch, Xt as CFN_TEMPLATE_BODY_LIMIT, Y as resolveExplicitPhysicalId, Yt as warnDeprecatedNoPrefixCliFlag, Z as configStringRefusal, Zt as CFN_TEMPLATE_URL_LIMIT, _ as createPreDeleteFinalSnapshot, _n as LocalStartServiceError, _t as stringifyValue, a as DeploymentEventsStore, an as resolveBucketRegion, at as DiffCalculator, b as unsupportedFinalSnapshotError, bn as NestedStackChildDirectDestroyError, bt as createAssetRedirectResolver, c as replayFailedOperations, cn as resetAwsClients, ct as isRetryableTransientError, d as IMPLICIT_DELETE_DEPENDENCIES, dn as CdkdError, dt as TemplateParser, en as uploadCfnTemplate, et as requireConfigArray, f as computeImplicitDeleteEdges, ft as LockManager, g as ccRoutedFinalSnapshotError, gn as LocalMigrateError, gt as AssetPublisher, h as buildFinalSnapshotIdentifier, hn as LocalInvokeBuildError$1, ht as shouldRetainResource, i as DeploymentEventsReader, it as applyRoleArnIfSet, j as red, jn as normalizeAwsError, jt as formatDockerLoginError, k as gray, kt as validateContainerRepoName, l as replayRollback, ln as setAwsClients, lt as isThrottlingError, m as PRE_DELETE_SNAPSHOT_TYPES, mn as DeployCancelledError, mt as rebuildClientForBucketRegion, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AssemblyReader, nt as requireConfigString, o as planFailedOps, on as AwsClients, ot as describeTypeWithThrottleRetry, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pt as S3StateBackend, q as WAFv2WebACLProvider, qt as resolveUseCdkBootstrapAssets, r as DeployEngine, rn as processStackMessages, rt as derivePartitionAndUrlSuffix, s as planRollback, sn as getAwsClients, st as withRetry, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as expectedOwnerParam, tt as requireConfigObject, u as withResourceDeadline, ut as DagBuilder, v as isFinalSnapshotError, vt as WorkGraph, w as isStatefulRecreateTargetSync, wn as ResourceUpdateNotSupportedError, wt as BOOTSTRAP_MARKER_PREFIX, x as makeCanonicalizePropertiesFn, xn as PartialFailureError, xt as loadPublishableAssetManifest, y as refusesFinalSnapshot, yn as MissingCdkCliError, yt as buildAssetRedirectMap, z as CloudControlProvider, zt as getDefaultStateBucketName } from "./deploy-engine-
|
|
2
|
+
import { $ as replayWarn, $t as findLargeInlineResources, A as green, At as buildDockerImage, B as slowCcOperationTimeoutMs, Bt as getLegacyStateBucketName, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as ResourceTimeoutError, Ct as AssetModeResolver, D as bold, Dt as parseBootstrapMarker, E as formatResourceLine, En as StackTerminationProtectionError, Et as getBootstrapMarkerKey, F as clearOnUpdateRemoval, Ft as AssetManifestLoader, G as getAccountInfo, Gt as resolveStateBucketWithDefault, H as isTerminationProtectionPropagationError, Ht as resolveAutoAssetStorage, I as ProviderRegistry, It as getDockerImageBySourceHash, J as normalizeAwsTagsToCfn, Jt as stateBucketExistenceConfirmed, K as refStateLookupFromResource, Kt as resolveStateBucketWithDefaultAndSource, L as findActionableSilentDrops, Lt as Synthesizer, M as yellow, Mn as withErrorHandling, Mt as getDockerCmd, N as IAMRoleProvider, Nt as runDockerForeground, O as cyan, Ot as validateAssetBucketName, P as collectInlinePolicyNamesManagedBySiblings, Pt as runDockerStreaming, Q as readConfigString, Qt as MIGRATE_TMP_PREFIX, R as findSilentDropProperties, Rt as synthesisStatusMessage, S as extractDeploymentEventError, Sn as ProvisioningError, St as rewriteTemplateAssetReferences, T as renderStatefulReason, Tn as StackHasActiveImportsError, Tt as ensureAssetStorage, U as IntrinsicFunctionResolver, Ut as resolveCaptureObservedState, V as disableInstanceApiTermination, Vt as resolveApp, W as cfnRefValueFromPhysicalId, Wt as resolveSkipPrefix, X as assertRegionMatch, Xt as CFN_TEMPLATE_BODY_LIMIT, Y as resolveExplicitPhysicalId, Yt as warnDeprecatedNoPrefixCliFlag, Z as configStringRefusal, Zt as CFN_TEMPLATE_URL_LIMIT, _ as createPreDeleteFinalSnapshot, _n as LocalStartServiceError, _t as stringifyValue, a as DeploymentEventsStore, an as resolveBucketRegion, at as DiffCalculator, b as unsupportedFinalSnapshotError, bn as NestedStackChildDirectDestroyError, bt as createAssetRedirectResolver, c as replayFailedOperations, cn as resetAwsClients, ct as isRetryableTransientError, d as IMPLICIT_DELETE_DEPENDENCIES, dn as CdkdError, dt as TemplateParser, en as uploadCfnTemplate, et as requireConfigArray, f as computeImplicitDeleteEdges, ft as LockManager, g as ccRoutedFinalSnapshotError, gn as LocalMigrateError, gt as AssetPublisher, h as buildFinalSnapshotIdentifier, hn as LocalInvokeBuildError$1, ht as shouldRetainResource, i as DeploymentEventsReader, it as applyRoleArnIfSet, j as red, jn as normalizeAwsError, jt as formatDockerLoginError, k as gray, kt as validateContainerRepoName, l as replayRollback, ln as setAwsClients, lt as isThrottlingError, m as PRE_DELETE_SNAPSHOT_TYPES, mn as DeployCancelledError, mt as rebuildClientForBucketRegion, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AssemblyReader, nt as requireConfigString, o as planFailedOps, on as AwsClients, ot as describeTypeWithThrottleRetry, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pt as S3StateBackend, q as WAFv2WebACLProvider, qt as resolveUseCdkBootstrapAssets, r as DeployEngine, rn as processStackMessages, rt as derivePartitionAndUrlSuffix, s as planRollback, sn as getAwsClients, st as withRetry, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as expectedOwnerParam, tt as requireConfigObject, u as withResourceDeadline, ut as DagBuilder, v as isFinalSnapshotError, vt as WorkGraph, w as isStatefulRecreateTargetSync, wn as ResourceUpdateNotSupportedError, wt as BOOTSTRAP_MARKER_PREFIX, x as makeCanonicalizePropertiesFn, xn as PartialFailureError, xt as loadPublishableAssetManifest, y as refusesFinalSnapshot, yn as MissingCdkCliError, yt as buildAssetRedirectMap, z as CloudControlProvider, zt as getDefaultStateBucketName } from "./deploy-engine-CfnMSwyx.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-B1pHGT_X.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
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, 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";
|
|
@@ -4892,18 +4892,270 @@ function s3BucketDestinationPath(dest, destinationPath) {
|
|
|
4892
4892
|
return isPlainObject$1(dest) && isFlattenedDestination(dest) ? destinationPath : `${destinationPath}.S3BucketDestination`;
|
|
4893
4893
|
}
|
|
4894
4894
|
/**
|
|
4895
|
-
* The
|
|
4896
|
-
*
|
|
4897
|
-
*
|
|
4895
|
+
* The FLATTENED CFn `Destination` block cdkd actually SENDS for `declared`
|
|
4896
|
+
* (issue #1707) — the shared fold behind BOTH halves of the #1633 pair: the
|
|
4897
|
+
* per-item `effectiveProperties` record and
|
|
4898
|
+
* {@link S3BucketProvider.canonicalizeDesiredProperties}.
|
|
4899
|
+
*
|
|
4900
|
+
* The desired side accepts TWO spellings (`resolveS3BucketDestination` picks
|
|
4901
|
+
* either the flattened CFn block or the nested SDK `S3BucketDestination` one)
|
|
4902
|
+
* plus a `Bucket` / `BucketArn` alias inside either, while the readback
|
|
4903
|
+
* (`inventorySdkToCfn` / `analyticsSdkToCfn`) emits ONLY the flattened form with
|
|
4904
|
+
* `BucketArn`. So a record written in any other spelling can never match what
|
|
4905
|
+
* `readCurrentState` returns: `cdkd drift` re-reports it forever and `--revert`
|
|
4906
|
+
* re-issues the same Put — the never-emitted-KEY class of issue #1686, reached
|
|
4907
|
+
* through the SHAPE rather than through a value. The live CFn registry schema
|
|
4908
|
+
* agrees the flattened form is the only CFn spelling (`Destination` is
|
|
4909
|
+
* `{BucketArn, BucketAccountId, Format, Prefix}` with `additionalProperties:
|
|
4910
|
+
* false`), so `S3BucketDestination` and `Bucket` are cdkd-only tolerances.
|
|
4911
|
+
*
|
|
4912
|
+
* This NORMALIZES rather than retracting the tolerance, per the #1686 rule:
|
|
4913
|
+
* refusing the SDK spelling would break templates that deploy today. It also
|
|
4914
|
+
* supersedes #1670's write-back-at-the-DECLARED-branch decision, whose whole
|
|
4915
|
+
* reason was that writing at a hardcoded branch would leave the malformed value
|
|
4916
|
+
* alive at the other key and add a stray one — there is no other key left once
|
|
4917
|
+
* the block is normalized wholesale.
|
|
4918
|
+
*
|
|
4919
|
+
* `Format` is the one member a caller can override: the applier passes the
|
|
4920
|
+
* value it SENT, which differs from the declared one when the warn-and-
|
|
4921
|
+
* SUBSTITUTE read replaced it (#1670). Absent on BOTH sides it defaults to the
|
|
4922
|
+
* `'CSV'` the appliers send, so the record carries what AWS will report back
|
|
4923
|
+
* rather than one key fewer.
|
|
4924
|
+
*
|
|
4925
|
+
* @returns the flattened block, or `declared` BY IDENTITY when it already is
|
|
4926
|
+
* one (so an ordinary template records byte-for-byte what it declared) or is
|
|
4927
|
+
* not a shape this can fold at all (absent / malformed — left for the
|
|
4928
|
+
* resolver's own refusal to report).
|
|
4929
|
+
*/
|
|
4930
|
+
function effectiveS3BucketDestination(declared, sentFormat) {
|
|
4931
|
+
if (!isPlainObject$1(declared)) return declared;
|
|
4932
|
+
const bag = isFlattenedDestination(declared) ? declared : declared["S3BucketDestination"];
|
|
4933
|
+
if (!isPlainObject$1(bag)) return declared;
|
|
4934
|
+
if (!bag["BucketArn"] && !bag["Bucket"]) return declared;
|
|
4935
|
+
const out = {};
|
|
4936
|
+
const bucketArn = bag["BucketArn"] ?? bag["Bucket"];
|
|
4937
|
+
if (bucketArn !== void 0) out["BucketArn"] = bucketArn;
|
|
4938
|
+
if (bag["BucketAccountId"] !== void 0) out["BucketAccountId"] = bag["BucketAccountId"];
|
|
4939
|
+
out["Format"] = sentFormat ?? declaredOrDefault(bag, "Format", INVENTORY_DESTINATION_DEFAULT_FORMAT);
|
|
4940
|
+
if (bag["Prefix"] !== void 0) out["Prefix"] = bag["Prefix"];
|
|
4941
|
+
return sameScalarRecord(out, declared) ? declared : out;
|
|
4942
|
+
}
|
|
4943
|
+
/**
|
|
4944
|
+
* `container[key]` when the key is DECLARED, else `fallback` — presence-based,
|
|
4945
|
+
* NEVER `??`, and that distinction is the whole guard against issue #1670's
|
|
4946
|
+
* finding 3.
|
|
4947
|
+
*
|
|
4948
|
+
* The ONE licensed `??` in these folds is the ALIAS read
|
|
4949
|
+
* `bag['BucketArn'] ?? bag['Bucket']`, and it is not an exception to this rule —
|
|
4950
|
+
* it supplies no DEFAULT. It picks between two spellings of the same declared
|
|
4951
|
+
* value, exactly as the applier's own read does, so a nullish `BucketArn`
|
|
4952
|
+
* correctly falls through to the alias the wire would have used. When neither is
|
|
4953
|
+
* declared the key is simply absent from the fold's output, which is what makes
|
|
4954
|
+
* a bucket-less block identity-return rather than gain a phantom key.
|
|
4955
|
+
*
|
|
4956
|
+
* The default belongs to a key the template OMITTED: there the provider sends
|
|
4957
|
+
* it, the readback reports it, and folding both sides is what makes them agree.
|
|
4958
|
+
* A key DECLARED with a malformed value is a different case entirely — the
|
|
4959
|
+
* provider warns and substitutes, records what it SENT, and `cdkd diff` must go
|
|
4960
|
+
* on reporting the difference until the template is corrected, because that
|
|
4961
|
+
* report plus the warning are the user's only signal. A `??` chain cannot tell
|
|
4962
|
+
* the two apart: it treats a declared `null` as absent and quietly folds the
|
|
4963
|
+
* template side onto the substituted value, so the comparison comes out equal,
|
|
4964
|
+
* the provider is never called, and the warning STOPS — the exact concealment
|
|
4965
|
+
* #1670 refused a twin over. Measured on `Format: null` while reviewing this
|
|
4966
|
+
* change; a malformed value that is not nullish (a blank string, an array, an
|
|
4967
|
+
* unresolved intrinsic) never hit it, so the unit rows written first all passed.
|
|
4968
|
+
*/
|
|
4969
|
+
function declaredOrDefault(container, key, fallback) {
|
|
4970
|
+
return key in container ? container[key] : fallback;
|
|
4971
|
+
}
|
|
4972
|
+
/**
|
|
4973
|
+
* The default `Destination.Format` both per-item appliers send when the block
|
|
4974
|
+
* declares none. Named rather than repeated so the wire value and the fold above
|
|
4975
|
+
* cannot drift apart.
|
|
4976
|
+
*/
|
|
4977
|
+
const INVENTORY_DESTINATION_DEFAULT_FORMAT = "CSV";
|
|
4978
|
+
/**
|
|
4979
|
+
* The default `Enabled` / `IncludedObjectVersions` an inventory item is SENT
|
|
4980
|
+
* with when it declares none — the same drift-apart concern as
|
|
4981
|
+
* {@link INVENTORY_DESTINATION_DEFAULT_FORMAT}.
|
|
4982
|
+
*/
|
|
4983
|
+
const INVENTORY_DEFAULT_ENABLED = true;
|
|
4984
|
+
const INVENTORY_DEFAULT_INCLUDED_OBJECT_VERSIONS = "All";
|
|
4985
|
+
/** The `OutputSchemaVersion` an analytics data export is SENT with by default. */
|
|
4986
|
+
const ANALYTICS_DEFAULT_OUTPUT_SCHEMA_VERSION = "V_1";
|
|
4987
|
+
/**
|
|
4988
|
+
* Are two records equal as SCALAR-valued bags? Used only to identity-return an
|
|
4989
|
+
* already-canonical block, so a fold that changes nothing cannot manufacture a
|
|
4990
|
+
* fresh object (and, through it, a fresh diff line on a template nobody edited).
|
|
4991
|
+
*
|
|
4992
|
+
* Scalar-only is sufficient here: every member of the CFn `Destination`
|
|
4993
|
+
* definition is a scalar (`{BucketArn, BucketAccountId, Format, Prefix}`).
|
|
4994
|
+
*
|
|
4995
|
+
* Precisely (review of #1707 corrected an over-claim): a nested value is
|
|
4996
|
+
* compared by REFERENCE like any other. On the flattened branch the fold copies
|
|
4997
|
+
* the caller's own references, so a nested value is `===` itself and identity IS
|
|
4998
|
+
* returned — which is correct, since nothing changed. It is only a
|
|
4999
|
+
* REBUILT nested value that compares different, and that falls back to recording
|
|
5000
|
+
* the folded block — the safe direction.
|
|
5001
|
+
*/
|
|
5002
|
+
function sameScalarRecord(a, b) {
|
|
5003
|
+
const aKeys = Object.keys(a);
|
|
5004
|
+
if (aKeys.length !== Object.keys(b).length) return false;
|
|
5005
|
+
return aKeys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && Object.is(a[k], b[k]));
|
|
5006
|
+
}
|
|
5007
|
+
/** The `ScheduleFrequency` an inventory item is SENT with when it declares none. */
|
|
5008
|
+
const INVENTORY_DEFAULT_SCHEDULE_FREQUENCY = "Weekly";
|
|
5009
|
+
/**
|
|
5010
|
+
* The `InventoryConfigurations[]` ITEM cdkd actually SENDS for `item` — the ONE
|
|
5011
|
+
* helper behind both halves of the #1633 pair, per
|
|
5012
|
+
* `.claude/rules/providers.md`'s "share ONE helper with the provisioning path
|
|
5013
|
+
* rather than re-deriving the rule".
|
|
5014
|
+
*
|
|
5015
|
+
* Called from `applyInventoryConfigurations` with the values that went on the
|
|
5016
|
+
* wire (`sent`), and from
|
|
5017
|
+
* {@link S3BucketProvider.canonicalizeDesiredProperties} with none — where the
|
|
5018
|
+
* declared value IS what would be sent for every item the appliers do not warn
|
|
5019
|
+
* about, which is the whole population the twin exists for. On a MALFORMED value
|
|
5020
|
+
* the two deliberately disagree (the applier records the substituted value, the
|
|
5021
|
+
* fold keeps the declared one), so `cdkd diff` keeps reporting the field until
|
|
5022
|
+
* the template is corrected — the #1670 finding-3 outcome, preserved rather than
|
|
5023
|
+
* concealed.
|
|
5024
|
+
*
|
|
5025
|
+
* Three folds, all of them the never-emitted-KEY class (issue #1686) rather than
|
|
5026
|
+
* a narrowing:
|
|
5027
|
+
*
|
|
5028
|
+
* - `Schedule: { Frequency }` -> `ScheduleFrequency`. cdkd accepts the SDK
|
|
5029
|
+
* spelling (the #1605 fall-through) while `inventorySdkToCfn` emits only the
|
|
5030
|
+
* CFn one, which the registry schema declares REQUIRED and whose `Schedule`
|
|
5031
|
+
* sibling it does not declare at all.
|
|
5032
|
+
* - `Destination` -> the flattened CFn block (see
|
|
5033
|
+
* {@link effectiveS3BucketDestination}).
|
|
5034
|
+
* - `Enabled` / `IncludedObjectVersions` DEFAULTS. Both are always sent
|
|
5035
|
+
* (`IsEnabled: … ?? true`, `readConfigString(…, 'All')`) and always read back,
|
|
5036
|
+
* so an item omitting them recorded one key FEWER than the readback produces
|
|
5037
|
+
* and the comparator saw a key-count mismatch on the whole array — the
|
|
5038
|
+
* defaulted-but-SENT class of issue #1670, one property over (issue #1718).
|
|
5039
|
+
*
|
|
5040
|
+
* COPYING rather than mutating is load-bearing, not hygiene: `item` is an
|
|
5041
|
+
* element of the caller's DESIRED property bag, which `DiffCalculator` and the
|
|
5042
|
+
* engine still read afterwards — and the diff is now one of the two callers, so
|
|
5043
|
+
* mutating in place would rewrite the template side of a comparison the other
|
|
5044
|
+
* caller is in the middle of.
|
|
5045
|
+
*
|
|
5046
|
+
* @returns `item` BY IDENTITY when every fold is a no-op, so an ordinary
|
|
5047
|
+
* fully-declared CFn template records and diffs byte-for-byte as before.
|
|
5048
|
+
*/
|
|
5049
|
+
function effectiveInventoryItem(item, sent) {
|
|
5050
|
+
const patch = {};
|
|
5051
|
+
if (item["Enabled"] === void 0) patch["Enabled"] = INVENTORY_DEFAULT_ENABLED;
|
|
5052
|
+
if (item["IncludedObjectVersions"] === void 0) patch["IncludedObjectVersions"] = INVENTORY_DEFAULT_INCLUDED_OBJECT_VERSIONS;
|
|
5053
|
+
const declaredSchedule = item["Schedule"];
|
|
5054
|
+
const declaresScheduleFrequency = item["ScheduleFrequency"] !== void 0;
|
|
5055
|
+
const scheduleFrequencyUsable = declaresScheduleFrequency && configStringRefusal(item, "ScheduleFrequency", INVENTORY_DEFAULT_SCHEDULE_FREQUENCY, "AWS::S3::Bucket InventoryConfigurations[]") === void 0;
|
|
5056
|
+
const scheduleContainerUsable = configStringRefusal(declaredSchedule, "Frequency", INVENTORY_DEFAULT_SCHEDULE_FREQUENCY, "AWS::S3::Bucket InventoryConfigurations[].Schedule") === void 0;
|
|
5057
|
+
const canFoldSchedule = sent?.frequency !== void 0 || scheduleFrequencyUsable || !declaresScheduleFrequency && scheduleContainerUsable;
|
|
5058
|
+
if (canFoldSchedule) {
|
|
5059
|
+
const declaredFrequency = declaresScheduleFrequency ? item["ScheduleFrequency"] : isPlainObject$1(declaredSchedule) && declaredSchedule["Frequency"] !== void 0 ? declaredSchedule["Frequency"] : INVENTORY_DEFAULT_SCHEDULE_FREQUENCY;
|
|
5060
|
+
const frequency = sent?.frequency ?? declaredFrequency;
|
|
5061
|
+
if (!Object.is(item["ScheduleFrequency"], frequency)) patch["ScheduleFrequency"] = frequency;
|
|
5062
|
+
}
|
|
5063
|
+
const destination = effectiveS3BucketDestination(item["Destination"], sent?.format);
|
|
5064
|
+
if (destination !== item["Destination"]) patch["Destination"] = destination;
|
|
5065
|
+
const dropsSchedule = declaredSchedule !== void 0 && canFoldSchedule;
|
|
5066
|
+
if (!dropsSchedule && Object.keys(patch).length === 0) return item;
|
|
5067
|
+
const out = {
|
|
5068
|
+
...item,
|
|
5069
|
+
...patch
|
|
5070
|
+
};
|
|
5071
|
+
if (dropsSchedule) delete out["Schedule"];
|
|
5072
|
+
return out;
|
|
5073
|
+
}
|
|
5074
|
+
/**
|
|
5075
|
+
* The `AnalyticsConfigurations[]` ITEM twin of {@link effectiveInventoryItem}.
|
|
5076
|
+
*
|
|
5077
|
+
* Only the data-export block diverges: the destination takes the same flattening
|
|
5078
|
+
* (`analyticsSdkToCfn` emits the flattened CFn spelling too — note it spells the
|
|
5079
|
+
* account `BucketAccountId` on BOTH sides here, unlike the inventory reverse
|
|
5080
|
+
* mapper), and `OutputSchemaVersion` is a defaulted-but-SENT member of the same
|
|
5081
|
+
* class as inventory's `Enabled`.
|
|
5082
|
+
*
|
|
5083
|
+
* A non-object `StorageClassAnalysis` / `DataExport` is left ALONE rather than
|
|
5084
|
+
* rebuilt: those are exactly the shapes `requireConfigObject` refuses, so the
|
|
5085
|
+
* item is SKIPPED on the wire and folding it here would describe a call that
|
|
5086
|
+
* never went out.
|
|
5087
|
+
*/
|
|
5088
|
+
function effectiveAnalyticsItem(item, sent) {
|
|
5089
|
+
const storageClassAnalysis = item["StorageClassAnalysis"];
|
|
5090
|
+
if (!isPlainObject$1(storageClassAnalysis)) return item;
|
|
5091
|
+
const dataExport = storageClassAnalysis["DataExport"];
|
|
5092
|
+
if (!isPlainObject$1(dataExport)) return item;
|
|
5093
|
+
const patch = {};
|
|
5094
|
+
const outputSchemaVersion = sent?.outputSchemaVersion ?? declaredOrDefault(dataExport, "OutputSchemaVersion", ANALYTICS_DEFAULT_OUTPUT_SCHEMA_VERSION);
|
|
5095
|
+
if (!Object.is(dataExport["OutputSchemaVersion"], outputSchemaVersion)) patch["OutputSchemaVersion"] = outputSchemaVersion;
|
|
5096
|
+
const destination = effectiveS3BucketDestination(dataExport["Destination"], sent?.format);
|
|
5097
|
+
if (destination !== dataExport["Destination"]) patch["Destination"] = destination;
|
|
5098
|
+
if (Object.keys(patch).length === 0) return item;
|
|
5099
|
+
return {
|
|
5100
|
+
...item,
|
|
5101
|
+
StorageClassAnalysis: {
|
|
5102
|
+
...storageClassAnalysis,
|
|
5103
|
+
DataExport: {
|
|
5104
|
+
...dataExport,
|
|
5105
|
+
...patch
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
};
|
|
5109
|
+
}
|
|
5110
|
+
/** The `Status` an intelligent-tiering item is SENT with when it declares none. */
|
|
5111
|
+
const INTELLIGENT_TIERING_DEFAULT_STATUS = "Enabled";
|
|
5112
|
+
/**
|
|
5113
|
+
* The `IntelligentTieringConfigurations[]` ITEM twin of
|
|
5114
|
+
* {@link effectiveInventoryItem} — the third applier of the #1718 audit.
|
|
5115
|
+
*
|
|
5116
|
+
* It has no never-emitted KEY or SHAPE (its destination-less block is spelled
|
|
5117
|
+
* identically on both sides), so the ONLY fold is the defaulted-but-SENT
|
|
5118
|
+
* `Status`: the Put always carries one and `intelligentTieringSdkToCfn` always
|
|
5119
|
+
* reads it back, so an item omitting it recorded one key fewer than the readback
|
|
5120
|
+
* produces. There is no `sent` parameter because this applier has no
|
|
5121
|
+
* warn-and-SUBSTITUTE arm at all — a malformed `Status` SKIPS the item (issue
|
|
5122
|
+
* #1595: defaulting to `Enabled` would start moving objects into archive
|
|
5123
|
+
* tiers), so the only value the fold can ever supply is the create-path default.
|
|
5124
|
+
*
|
|
5125
|
+
* The fourth applier, metrics, needs no fold: it sends `Id` plus a `Filter`
|
|
5126
|
+
* derived from the declared predicates and defaults no scalar member.
|
|
5127
|
+
*/
|
|
5128
|
+
function effectiveIntelligentTieringItem(item) {
|
|
5129
|
+
if (item["Status"] !== void 0) return item;
|
|
5130
|
+
return {
|
|
5131
|
+
...item,
|
|
5132
|
+
Status: INTELLIGENT_TIERING_DEFAULT_STATUS
|
|
5133
|
+
};
|
|
5134
|
+
}
|
|
5135
|
+
/**
|
|
5136
|
+
* Apply a per-ITEM fold to one of the `Id`-keyed array properties on BOTH sides
|
|
5137
|
+
* of a diff — the `canonicalizeDesiredProperties` plumbing shared by the
|
|
5138
|
+
* inventory and analytics folds.
|
|
4898
5139
|
*
|
|
4899
|
-
*
|
|
4900
|
-
*
|
|
4901
|
-
*
|
|
4902
|
-
*
|
|
4903
|
-
* cannot drift from the resolver at all.
|
|
5140
|
+
* Identity-returns at every level (untouched item -> untouched array ->
|
|
5141
|
+
* untouched bag), and leaves a non-array property or a non-object element ALONE
|
|
5142
|
+
* so a malformed template still reaches the provider's own refusal rather than
|
|
5143
|
+
* being reshaped into something that looks valid.
|
|
4904
5144
|
*/
|
|
4905
|
-
function
|
|
4906
|
-
|
|
5145
|
+
function canonicalizeItemList(properties, key, fold) {
|
|
5146
|
+
const list = properties[key];
|
|
5147
|
+
if (!Array.isArray(list)) return properties;
|
|
5148
|
+
let changed = false;
|
|
5149
|
+
const mapped = list.map((element) => {
|
|
5150
|
+
if (!isPlainObject$1(element)) return element;
|
|
5151
|
+
const next = fold(element);
|
|
5152
|
+
if (next !== element) changed = true;
|
|
5153
|
+
return next;
|
|
5154
|
+
});
|
|
5155
|
+
return changed ? {
|
|
5156
|
+
...properties,
|
|
5157
|
+
[key]: mapped
|
|
5158
|
+
} : properties;
|
|
4907
5159
|
}
|
|
4908
5160
|
/**
|
|
4909
5161
|
* Name a malformed value in a refusal message, the way `config-shape.ts`'s
|
|
@@ -5169,24 +5421,37 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5169
5421
|
* the declared value describes a configuration AWS does not hold, and
|
|
5170
5422
|
* `readCurrentState` reads all of these back — the permanent phantom drift
|
|
5171
5423
|
* `.claude/rules/providers.md` records for the #1633 warn-and-default arm.
|
|
5172
|
-
*
|
|
5173
|
-
*
|
|
5174
|
-
*
|
|
5175
|
-
*
|
|
5176
|
-
*
|
|
5177
|
-
*
|
|
5178
|
-
*
|
|
5179
|
-
* -
|
|
5180
|
-
*
|
|
5181
|
-
*
|
|
5182
|
-
*
|
|
5183
|
-
*
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5424
|
+
*
|
|
5425
|
+
* These sites DO take a `canonicalizeDesiredProperties` twin as of issues
|
|
5426
|
+
* #1707 / #1717, and this paragraph said the opposite until then — it is
|
|
5427
|
+
* restated rather than deleted because the earlier answer was not wrong, it
|
|
5428
|
+
* was answering a different question. #1670 asked it about the SUBSTITUTED
|
|
5429
|
+
* VALUE and said no, because canonicalizing there would conceal a malformed
|
|
5430
|
+
* value whose warning is the user's only signal. The twin that shipped folds
|
|
5431
|
+
* the never-emitted KEY and SHAPE at the same sites, which have no fault to
|
|
5432
|
+
* conceal and emit no warning at all, and it is keyed off the DECLARED shape
|
|
5433
|
+
* so the substituted value stays visible exactly as before. See
|
|
5434
|
+
* {@link S3BucketProvider.canonicalizeDesiredProperties} and
|
|
5435
|
+
* {@link declaredOrDefault}.
|
|
5436
|
+
*
|
|
5437
|
+
* **It is now a verbatim alias of `readConfigString`**, and the bullets that
|
|
5438
|
+
* used to sit here described a probe that no longer exists — the same stale
|
|
5439
|
+
* -rationale class this round fixed one paragraph up, so it is named rather
|
|
5440
|
+
* than quietly rewritten. The `onSubstituted` callback it used to take is GONE
|
|
5441
|
+
* (review of #1707): recording is the per-item fold's job now, and all three
|
|
5442
|
+
* call sites had been left passing a comment-only no-op, so the probe ran a
|
|
5443
|
+
* second `configStringRefusal` per read purely to invoke nothing.
|
|
5444
|
+
*
|
|
5445
|
+
* It is kept rather than inlined for ONE reason, which is worth stating so the
|
|
5446
|
+
* next reader can weigh it: the name marks these three reads as the
|
|
5447
|
+
* warn-and-SUBSTITUTE sites, distinguishing them at a glance from the
|
|
5448
|
+
* SKIP-guarded ones (`skipOnUnusableConfigString`) and the plain strict reads.
|
|
5449
|
+
* The behavior is `readConfigString`'s, unchanged — `options.onUnusable`
|
|
5450
|
+
* carries the WARNING the "already ANNOUNCED" condition on
|
|
5451
|
+
* `effectiveProperties` needs, and without it the read still THROWS.
|
|
5452
|
+
*/
|
|
5453
|
+
readSubstitutedConfigString(container, key, fallback, containerPath, options) {
|
|
5454
|
+
return readConfigString(container, key, fallback, containerPath, options);
|
|
5190
5455
|
}
|
|
5191
5456
|
/**
|
|
5192
5457
|
* Apply lifecycle configuration
|
|
@@ -5627,10 +5892,8 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5627
5892
|
for (const config of configs) {
|
|
5628
5893
|
index += 1;
|
|
5629
5894
|
const id = config["Id"];
|
|
5630
|
-
let
|
|
5631
|
-
|
|
5632
|
-
sentItem = S3BucketProvider.withDeepValue(sentItem ?? config, path, sent);
|
|
5633
|
-
};
|
|
5895
|
+
let sentOutputSchemaVersion;
|
|
5896
|
+
let sentFormat;
|
|
5634
5897
|
const rawStorageClassAnalysis = config["StorageClassAnalysis"];
|
|
5635
5898
|
if (rawStorageClassAnalysis != null && requireConfigObject(rawStorageClassAnalysis, "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis", { ...onUnusable ? { onUnusable } : {} }) === void 0) {
|
|
5636
5899
|
skipped.push(index);
|
|
@@ -5669,23 +5932,12 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5669
5932
|
continue;
|
|
5670
5933
|
}
|
|
5671
5934
|
const destPath = s3BucketDestinationPath(rawDest, analyticsDestPath);
|
|
5672
|
-
const destSegments = s3Dest ? s3BucketDestinationSegments(rawDest, s3Dest) : [];
|
|
5673
5935
|
analyticsConfig.StorageClassAnalysis = { DataExport: {
|
|
5674
|
-
OutputSchemaVersion: this.readSubstitutedConfigString(dataExport, "OutputSchemaVersion",
|
|
5675
|
-
"StorageClassAnalysis",
|
|
5676
|
-
"DataExport",
|
|
5677
|
-
"OutputSchemaVersion"
|
|
5678
|
-
], sent)),
|
|
5936
|
+
OutputSchemaVersion: sentOutputSchemaVersion = this.readSubstitutedConfigString(dataExport, "OutputSchemaVersion", ANALYTICS_DEFAULT_OUTPUT_SCHEMA_VERSION, "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis.DataExport", { ...onUnusable ? { onUnusable } : {} }),
|
|
5679
5937
|
Destination: s3Dest ? { S3BucketDestination: {
|
|
5680
5938
|
Bucket: s3Dest["BucketArn"] ?? s3Dest["Bucket"],
|
|
5681
5939
|
BucketAccountId: s3Dest["BucketAccountId"],
|
|
5682
|
-
Format: this.readSubstitutedConfigString(s3Dest, "Format",
|
|
5683
|
-
"StorageClassAnalysis",
|
|
5684
|
-
"DataExport",
|
|
5685
|
-
"Destination",
|
|
5686
|
-
...destSegments,
|
|
5687
|
-
"Format"
|
|
5688
|
-
], sent)),
|
|
5940
|
+
Format: sentFormat = this.readSubstitutedConfigString(s3Dest, "Format", INVENTORY_DESTINATION_DEFAULT_FORMAT, destPath, { ...onUnusable ? { onUnusable } : {} }),
|
|
5689
5941
|
Prefix: s3Dest["Prefix"]
|
|
5690
5942
|
} } : void 0
|
|
5691
5943
|
} };
|
|
@@ -5695,7 +5947,11 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5695
5947
|
Id: id,
|
|
5696
5948
|
AnalyticsConfiguration: analyticsConfig
|
|
5697
5949
|
}));
|
|
5698
|
-
|
|
5950
|
+
const sentItem = effectiveAnalyticsItem(config, {
|
|
5951
|
+
...sentOutputSchemaVersion !== void 0 ? { outputSchemaVersion: sentOutputSchemaVersion } : {},
|
|
5952
|
+
...sentFormat !== void 0 ? { format: sentFormat } : {}
|
|
5953
|
+
});
|
|
5954
|
+
if (sentItem !== config) substituted.set(index, sentItem);
|
|
5699
5955
|
}
|
|
5700
5956
|
this.logger.debug(`Applied ${configs.length - skipped.length} analytics configuration(s) to bucket ${bucketName}`);
|
|
5701
5957
|
return {
|
|
@@ -5712,11 +5968,18 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5712
5968
|
* @returns what happened to each item, as {@link PerItemApplyOutcome} — the
|
|
5713
5969
|
* per-item unit of `effectiveProperties` (issue #1612). Its per-item
|
|
5714
5970
|
* `Status` read takes the SKIP downgrade rather than a substitution (issue
|
|
5715
|
-
* #1595 — defaulting to `Enabled` would start archiving objects), so
|
|
5716
|
-
* `substituted` map is
|
|
5971
|
+
* #1595 — defaulting to `Enabled` would start archiving objects), so no
|
|
5972
|
+
* MALFORMED value is ever recorded here. The `substituted` map is still
|
|
5973
|
+
* populated for the defaulted-but-SENT arm (issue #1718): an item that
|
|
5974
|
+
* simply OMITS `Status` is sent `Enabled` and read back with it, so a record
|
|
5975
|
+
* without the key compares unequal to the readback for the whole array.
|
|
5976
|
+
* That is the audit #1718 asks for on the sibling appliers — `metrics` has
|
|
5977
|
+
* no defaulted scalar member at all (it sends only `Id` plus a `Filter`
|
|
5978
|
+
* derived from the declared predicates), so it needs no fold.
|
|
5717
5979
|
*/
|
|
5718
5980
|
async applyIntelligentTieringConfigurations(bucketName, configs, onUnusable) {
|
|
5719
5981
|
const skipped = [];
|
|
5982
|
+
const substituted = /* @__PURE__ */ new Map();
|
|
5720
5983
|
let index = -1;
|
|
5721
5984
|
for (const config of configs) {
|
|
5722
5985
|
index += 1;
|
|
@@ -5752,11 +6015,13 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5752
6015
|
Id: id,
|
|
5753
6016
|
IntelligentTieringConfiguration: itConfig
|
|
5754
6017
|
}));
|
|
6018
|
+
const sentItem = effectiveIntelligentTieringItem(config);
|
|
6019
|
+
if (sentItem !== config) substituted.set(index, sentItem);
|
|
5755
6020
|
}
|
|
5756
6021
|
this.logger.debug(`Applied ${configs.length - skipped.length} intelligent tiering configuration(s) to bucket ${bucketName}`);
|
|
5757
6022
|
return {
|
|
5758
6023
|
skipped,
|
|
5759
|
-
substituted
|
|
6024
|
+
substituted
|
|
5760
6025
|
};
|
|
5761
6026
|
}
|
|
5762
6027
|
/**
|
|
@@ -5790,11 +6055,7 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5790
6055
|
continue;
|
|
5791
6056
|
}
|
|
5792
6057
|
const destPath = s3BucketDestinationPath(rawDest, inventoryDestPath);
|
|
5793
|
-
|
|
5794
|
-
let sentItem;
|
|
5795
|
-
const recordSubstitution = (path, sent) => {
|
|
5796
|
-
sentItem = S3BucketProvider.withDeepValue(sentItem ?? config, path, sent);
|
|
5797
|
-
};
|
|
6058
|
+
let sentFormat;
|
|
5798
6059
|
const scheduleFrequencyRefusal = onUnusable ? configStringRefusal(config, "ScheduleFrequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[]") : void 0;
|
|
5799
6060
|
if (scheduleFrequencyRefusal !== void 0 && config["Schedule"] == null) {
|
|
5800
6061
|
onUnusable?.(`${scheduleFrequencyRefusal}. Skipping this inventory configuration: there is no Schedule.Frequency to fall back to, and defaulting to Weekly would silently re-cadence a live inventory report; the same value is REFUSED on a template-path create`);
|
|
@@ -5808,22 +6069,15 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5808
6069
|
continue;
|
|
5809
6070
|
}
|
|
5810
6071
|
const frequency = useScheduleFrequency ? requireConfigString(config["ScheduleFrequency"], "Weekly", "AWS::S3::Bucket InventoryConfigurations[].ScheduleFrequency") : readConfigString(config["Schedule"], "Frequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[].Schedule");
|
|
5811
|
-
const declaresSdkSchedule = config["Schedule"] !== void 0;
|
|
5812
|
-
if (scheduleFrequencyRefusal !== void 0 || declaresSdkSchedule) recordSubstitution(["ScheduleFrequency"], frequency);
|
|
5813
|
-
if (declaresSdkSchedule) sentItem = S3BucketProvider.withoutKey(sentItem ?? config, "Schedule");
|
|
5814
6072
|
const inventoryConfig = {
|
|
5815
6073
|
Id: id,
|
|
5816
|
-
IsEnabled: config["Enabled"] ??
|
|
5817
|
-
IncludedObjectVersions: readConfigString(config, "IncludedObjectVersions",
|
|
6074
|
+
IsEnabled: config["Enabled"] ?? INVENTORY_DEFAULT_ENABLED,
|
|
6075
|
+
IncludedObjectVersions: readConfigString(config, "IncludedObjectVersions", INVENTORY_DEFAULT_INCLUDED_OBJECT_VERSIONS, "AWS::S3::Bucket InventoryConfigurations[]"),
|
|
5818
6076
|
Schedule: { Frequency: frequency },
|
|
5819
6077
|
Destination: { S3BucketDestination: s3Dest ? {
|
|
5820
6078
|
Bucket: s3Dest["BucketArn"] ?? s3Dest["Bucket"],
|
|
5821
6079
|
AccountId: s3Dest["BucketAccountId"],
|
|
5822
|
-
Format: this.readSubstitutedConfigString(s3Dest, "Format",
|
|
5823
|
-
"Destination",
|
|
5824
|
-
...destSegments,
|
|
5825
|
-
"Format"
|
|
5826
|
-
], sent)),
|
|
6080
|
+
Format: sentFormat = this.readSubstitutedConfigString(s3Dest, "Format", INVENTORY_DESTINATION_DEFAULT_FORMAT, destPath, { ...onUnusable ? { onUnusable } : {} }),
|
|
5827
6081
|
Prefix: s3Dest["Prefix"]
|
|
5828
6082
|
} : void 0 },
|
|
5829
6083
|
OptionalFields: config["OptionalFields"],
|
|
@@ -5834,7 +6088,11 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
5834
6088
|
Id: id,
|
|
5835
6089
|
InventoryConfiguration: inventoryConfig
|
|
5836
6090
|
}));
|
|
5837
|
-
|
|
6091
|
+
const sentItem = effectiveInventoryItem(config, {
|
|
6092
|
+
frequency,
|
|
6093
|
+
...sentFormat !== void 0 ? { format: sentFormat } : {}
|
|
6094
|
+
});
|
|
6095
|
+
if (sentItem !== config) substituted.set(index, sentItem);
|
|
5838
6096
|
}
|
|
5839
6097
|
this.logger.debug(`Applied ${configs.length - skipped.length} inventory configuration(s) to bucket ${bucketName}`);
|
|
5840
6098
|
return {
|
|
@@ -6209,53 +6467,6 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
6209
6467
|
return effective;
|
|
6210
6468
|
}
|
|
6211
6469
|
/**
|
|
6212
|
-
* A COPY of `container` with `path` set to `value` (issue #1670).
|
|
6213
|
-
*
|
|
6214
|
-
* Copying rather than mutating is load-bearing, not hygiene: the container is
|
|
6215
|
-
* an item of the caller's DESIRED property bag, which `DiffCalculator` and the
|
|
6216
|
-
* engine still read afterwards — writing the substituted value into it would
|
|
6217
|
-
* silently rewrite the template side of the next comparison, which is exactly
|
|
6218
|
-
* the `canonicalizeDesiredProperties` behavior this fix must NOT have. Only
|
|
6219
|
-
* the objects ALONG the path are cloned; every sibling subtree is shared,
|
|
6220
|
-
* which is safe because nothing here mutates them.
|
|
6221
|
-
*
|
|
6222
|
-
* A non-object intermediate is rebuilt as an EMPTY block, which is not a
|
|
6223
|
-
* defensive shrug but the accurate record: it is what `readConfigString`
|
|
6224
|
-
* itself did on the way in — its container arm says "Treating the block as
|
|
6225
|
-
* empty, so <path>.<key> takes its default" — so `{}` plus the substituted
|
|
6226
|
-
* leaf IS what was sent. No call site can reach it today (every intermediate
|
|
6227
|
-
* has already been validated by `requireConfigObject` /
|
|
6228
|
-
* `resolveS3BucketDestination`, which SKIP the item on a malformed one).
|
|
6229
|
-
*/
|
|
6230
|
-
static withDeepValue(container, path, value) {
|
|
6231
|
-
const [head, ...rest] = path;
|
|
6232
|
-
if (head === void 0) return container;
|
|
6233
|
-
const child = container[head];
|
|
6234
|
-
return {
|
|
6235
|
-
...container,
|
|
6236
|
-
[head]: rest.length === 0 ? value : S3BucketProvider.withDeepValue(isPlainObject$1(child) ? child : {}, rest, value)
|
|
6237
|
-
};
|
|
6238
|
-
}
|
|
6239
|
-
/**
|
|
6240
|
-
* The REMOVAL twin of {@link withDeepValue}: a copy of `container` without
|
|
6241
|
-
* `key`, sharing every surviving subtree and never mutating the input (same
|
|
6242
|
-
* reason — the input is an item of the caller's DESIRED bag).
|
|
6243
|
-
*
|
|
6244
|
-
* A substitution answers "the value we sent differs from the declared one";
|
|
6245
|
-
* this answers "the KEY we sent does not exist on the readback at all", which
|
|
6246
|
-
* is the shape-mismatch half of the same phantom-drift class (issue #1686).
|
|
6247
|
-
* Setting the key to `undefined` instead would NOT do: `deepEqual` is
|
|
6248
|
-
* `JSON.stringify`, which drops an `undefined` member — but the state record
|
|
6249
|
-
* is written from this object and a present-but-`undefined` key survives a
|
|
6250
|
-
* `structuredClone`, so the two sides would disagree on key COUNT for any
|
|
6251
|
-
* consumer that walks `Object.keys` (the `unionWalkObjects` drift path does).
|
|
6252
|
-
*/
|
|
6253
|
-
static withoutKey(container, key) {
|
|
6254
|
-
if (!(key in container)) return container;
|
|
6255
|
-
const { [key]: _dropped, ...rest } = container;
|
|
6256
|
-
return rest;
|
|
6257
|
-
}
|
|
6258
|
-
/**
|
|
6259
6470
|
* The empty-collection placeholder arm of {@link applySubConfigDiffs}: the
|
|
6260
6471
|
* Put is SKIPPED, so AWS still holds the previously-applied configuration and
|
|
6261
6472
|
* the record must say so (issue #1671).
|
|
@@ -6316,6 +6527,51 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
6316
6527
|
retainPrevious(key);
|
|
6317
6528
|
}
|
|
6318
6529
|
/**
|
|
6530
|
+
* The CREATE-path sibling of {@link emptyCollectionSkip} (issue #1718).
|
|
6531
|
+
*
|
|
6532
|
+
* `applyAllSubConfigsForCreate` carries the same empty-collection guard the
|
|
6533
|
+
* update path does and skipped in SILENCE, so a fresh bucket came up with a
|
|
6534
|
+
* declared lifecycle / CORS configuration that was never applied and nothing
|
|
6535
|
+
* said so. The template shape that motivated #1671 — a condition-pruned or
|
|
6536
|
+
* intrinsic-collapsed empty array — is equally reachable on a create, and the
|
|
6537
|
+
* live A/B recorded on {@link emptyCollectionSkip} measured CloudFormation
|
|
6538
|
+
* REFUSING it there too, so the skip stands and only the silence was the
|
|
6539
|
+
* defect.
|
|
6540
|
+
*
|
|
6541
|
+
* WHAT IT RECORDS is where the two paths differ, and the answer here is
|
|
6542
|
+
* NOTHING — deliberately, not by omission. The update path retains the
|
|
6543
|
+
* PREVIOUS value because AWS still holds it; on a create there is no previous
|
|
6544
|
+
* value, and the rule's replay-CREATE line ("DROP the key") would be the
|
|
6545
|
+
* obvious import. It is the wrong one at this site: `readLifecycle` /
|
|
6546
|
+
* `readCors` ALWAYS emit the empty placeholder for an unconfigured bucket
|
|
6547
|
+
* (`{Rules: []}` on `NoSuchLifecycleConfiguration`, `{CorsRules: []}` on an
|
|
6548
|
+
* absent CORS config), so the declared empty collection is ALREADY exactly
|
|
6549
|
+
* what `readCurrentState` returns — the two agree with no override at all,
|
|
6550
|
+
* while dropping the key would leave the template declaring one the record
|
|
6551
|
+
* does not and churn a no-op UPDATE on every deploy. The rule's drop answer is
|
|
6552
|
+
* scoped to a skip whose declared value AWS cannot report; this one it can.
|
|
6553
|
+
*
|
|
6554
|
+
* Known bound, stated because the guard is wider than the shape that was
|
|
6555
|
+
* measured. It fires for every DECLARED collection the apply gate rejects, not
|
|
6556
|
+
* only the `{Rules: []}` the live A/B covered:
|
|
6557
|
+
*
|
|
6558
|
+
* - a MALFORMED non-array collection (an unresolved intrinsic), where the
|
|
6559
|
+
* record keeps a value the readback can never match — narrowing the record
|
|
6560
|
+
* there is the #1612 malformed-value class rather than this one;
|
|
6561
|
+
* - an EMPTY block (`{}`), where the message is literally true (it declares no
|
|
6562
|
+
* rules) and nothing is lost.
|
|
6563
|
+
*
|
|
6564
|
+
* The wording says "declares no rules" rather than asserting an empty ARRAY
|
|
6565
|
+
* for exactly that reason — the same hedge {@link emptyCollectionSkip} carries
|
|
6566
|
+
* on the update path, whose guard is equally wide. The gate is `!= null` and
|
|
6567
|
+
* not `!== undefined` (review of #1718): a property explicitly set to `null`
|
|
6568
|
+
* declares no configuration to lose, so warning about it would be noise, and
|
|
6569
|
+
* `!= null` is the convention the rest of this file's container gates use.
|
|
6570
|
+
*/
|
|
6571
|
+
announceCreateEmptyCollectionSkip(key, collectionPath) {
|
|
6572
|
+
this.logger.warn(`AWS::S3::Bucket ${collectionPath} declares no rules, which CloudFormation rejects outright (the collection is a required property, and an empty one is not a removal intent). Creating the bucket WITHOUT the declared ${key}; remove the whole ${key} property if that is what you meant, or check for a condition-pruned or intrinsic-collapsed rules array.`);
|
|
6573
|
+
}
|
|
6574
|
+
/**
|
|
6319
6575
|
* Diff CFn-shape sub-config values between previous and new state.
|
|
6320
6576
|
*
|
|
6321
6577
|
* Four transitions:
|
|
@@ -6605,10 +6861,11 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
6605
6861
|
if (notifConfig) await this.applyNotificationConfiguration(bucketName, notifConfig);
|
|
6606
6862
|
const corsConfig = properties["CorsConfiguration"];
|
|
6607
6863
|
if (corsConfig?.CorsRules && Array.isArray(corsConfig.CorsRules) && corsConfig.CorsRules.length > 0) await this.applyCorsConfiguration(bucketName, corsConfig);
|
|
6864
|
+
else if (corsConfig != null) this.announceCreateEmptyCollectionSkip("CorsConfiguration", "CorsConfiguration.CorsRules");
|
|
6608
6865
|
const lifecycleConfig = properties["LifecycleConfiguration"];
|
|
6609
6866
|
if (lifecycleConfig?.Rules && Array.isArray(lifecycleConfig.Rules) && lifecycleConfig.Rules.length > 0) {
|
|
6610
6867
|
if (!await this.applyLifecycleConfiguration(bucketName, lifecycleConfig, replayOnUnusable)) overrides.set("LifecycleConfiguration", void 0);
|
|
6611
|
-
}
|
|
6868
|
+
} else if (lifecycleConfig != null) this.announceCreateEmptyCollectionSkip("LifecycleConfiguration", "LifecycleConfiguration.Rules");
|
|
6612
6869
|
const loggingConfig = properties["LoggingConfiguration"];
|
|
6613
6870
|
const loggingRefusal = replayOnUnusable ? configStringRefusal(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") : void 0;
|
|
6614
6871
|
if (loggingRefusal !== void 0) {
|
|
@@ -6742,6 +6999,61 @@ var S3BucketProvider = class S3BucketProvider {
|
|
|
6742
6999
|
}
|
|
6743
7000
|
}
|
|
6744
7001
|
/**
|
|
7002
|
+
* The `effectiveProperties` TWIN required by `.claude/rules/providers.md`
|
|
7003
|
+
* ("`effectiveProperties` alone BREAKS the next deploy") for the per-item
|
|
7004
|
+
* folds the inventory and analytics appliers record — issue #1717 for the
|
|
7005
|
+
* inventory `Schedule` -> `ScheduleFrequency` half (#1686), issue #1707 for
|
|
7006
|
+
* the destination-shape half, issue #1718 for the defaulted-but-SENT members.
|
|
7007
|
+
*
|
|
7008
|
+
* Applied by `DiffCalculator` to BOTH comparison sides through
|
|
7009
|
+
* `makeCanonicalizePropertiesFn`, so the record's folded spelling and the
|
|
7010
|
+
* template's declared one compare EQUAL and an unchanged template stops
|
|
7011
|
+
* redeploying as `1 to update` forever. Folding both sides is also what heals
|
|
7012
|
+
* the already-deployed population: an item whose value never changes is never
|
|
7013
|
+
* re-Put, so the recording side alone can never reach a record written before
|
|
7014
|
+
* the fold.
|
|
7015
|
+
*
|
|
7016
|
+
* The rule's OTHER requirement — share ONE helper with the provisioning path
|
|
7017
|
+
* rather than re-deriving the rule — is met by
|
|
7018
|
+
* {@link effectiveInventoryItem} / {@link effectiveAnalyticsItem}, which the
|
|
7019
|
+
* appliers call with the values they actually sent and this calls with none.
|
|
7020
|
+
*
|
|
7021
|
+
* Note the twin question is re-asked per site rather than inherited: the #1670
|
|
7022
|
+
* sites next door answered NO because canonicalizing there would CONCEAL a
|
|
7023
|
+
* malformed value whose warning tells the user what to fix. These folds have
|
|
7024
|
+
* no fault to conceal — an item declaring only the SDK spelling is perfectly
|
|
7025
|
+
* valid input that emits no warning at all — so the twin's absence bought
|
|
7026
|
+
* nothing and cost a permanent `cdkd diff` line plus a redundant per-`Id` Put
|
|
7027
|
+
* on every deploy.
|
|
7028
|
+
*
|
|
7029
|
+
* Identity-returns an untouched bag, so every other `AWS::S3::Bucket` template
|
|
7030
|
+
* and every other resource type is byte-for-byte unaffected.
|
|
7031
|
+
*
|
|
7032
|
+
* THE #1717 OBSTACLE DID NOT REPRODUCE, and that is worth stating rather than
|
|
7033
|
+
* quietly omitting: the issue measured a `canonicalizeDesiredProperties`
|
|
7034
|
+
* folding this key making `gen-nested-key-coverage` report the still-correct
|
|
7035
|
+
* plural->singular `segmentRenames` entry for `InventoryConfigurations` STALE,
|
|
7036
|
+
* and warned that each piece is inert alone so only the combination trips it.
|
|
7037
|
+
* With the fold implemented as a MODULE-level pure function over an item
|
|
7038
|
+
* parameter, the shipped critic is green end to end — `vp run
|
|
7039
|
+
* audit:nested-key-coverage:check` reports 0 divergences in all three passes,
|
|
7040
|
+
* the committed matrix does not drift by a byte, and
|
|
7041
|
+
* `InventoryConfigurations` is still listed in the target's
|
|
7042
|
+
* `usedSegmentRenames`, i.e. the entry is doing work and the fence is not
|
|
7043
|
+
* masking anything. So no `segmentRenames` entry was removed and no staleness
|
|
7044
|
+
* check was widened; the issue's "resolve the interaction WITHOUT deleting the
|
|
7045
|
+
* entry" requirement is met by there being no interaction to resolve. What is
|
|
7046
|
+
* NOT claimed is a mechanism for why the issue's attempt tripped it — that
|
|
7047
|
+
* attempt is not in the tree to bisect against.
|
|
7048
|
+
*/
|
|
7049
|
+
canonicalizeDesiredProperties(resourceType, properties) {
|
|
7050
|
+
if (resourceType !== "AWS::S3::Bucket") return properties;
|
|
7051
|
+
let out = canonicalizeItemList(properties, "InventoryConfigurations", (item) => effectiveInventoryItem(item));
|
|
7052
|
+
out = canonicalizeItemList(out, "AnalyticsConfigurations", (item) => effectiveAnalyticsItem(item));
|
|
7053
|
+
out = canonicalizeItemList(out, "IntelligentTieringConfigurations", effectiveIntelligentTieringItem);
|
|
7054
|
+
return out;
|
|
7055
|
+
}
|
|
7056
|
+
/**
|
|
6745
7057
|
* Read the AWS-current S3 bucket configuration in CFn-property shape.
|
|
6746
7058
|
*
|
|
6747
7059
|
* Issues a small handful of independent S3 GET calls and stitches them
|
|
@@ -11882,6 +12194,43 @@ function packCompositeId(resourceType, logicalId, segments, options) {
|
|
|
11882
12194
|
else throw new ProvisioningError(refusal, resourceType, logicalId);
|
|
11883
12195
|
return segments.map((segment) => String(segment.value)).join("|");
|
|
11884
12196
|
}
|
|
12197
|
+
/** Render the expected id shape, e.g. `<databaseName>|<tableName>`. */
|
|
12198
|
+
function formatShape(segments) {
|
|
12199
|
+
return segments.map((segment) => `<${segment}>`).join("|");
|
|
12200
|
+
}
|
|
12201
|
+
/**
|
|
12202
|
+
* The message EVERY malformed-composite-id decode site emits (issue
|
|
12203
|
+
* [#1657](https://github.com/go-to-k/cdkd/issues/1657)).
|
|
12204
|
+
*
|
|
12205
|
+
* ## Why one builder
|
|
12206
|
+
*
|
|
12207
|
+
* Roughly half the decode sites stated the expected shape and half only echoed
|
|
12208
|
+
* the value back, and the format is documented nowhere else — so on the silent
|
|
12209
|
+
* half the message was the user's only possible route to the answer and it did
|
|
12210
|
+
* not carry it. Routing the sites that already got it right through the same
|
|
12211
|
+
* builder is what stops them drifting back.
|
|
12212
|
+
*
|
|
12213
|
+
* ## This is a WORDING fix, not an acceptance change
|
|
12214
|
+
*
|
|
12215
|
+
* Each caller keeps its own split-and-guard exactly as it was. The arities
|
|
12216
|
+
* genuinely differ (some sites accept AT LEAST N segments, some destructure the
|
|
12217
|
+
* first N and tolerate extras — the #1672 ambiguity, tracked separately), and
|
|
12218
|
+
* folding them into one predicate here would silently change which ids deploy.
|
|
12219
|
+
*
|
|
12220
|
+
* ## The `skipping` variant
|
|
12221
|
+
*
|
|
12222
|
+
* On the DELETE path the established policy is warn-and-continue, so a
|
|
12223
|
+
* malformed record leaves the AWS resource ALIVE while `cdkd destroy` reports
|
|
12224
|
+
* success. That policy is not changed here — but the warning now says what the
|
|
12225
|
+
* skip costs, because an orphan the user is never told about is one they cannot
|
|
12226
|
+
* go and delete.
|
|
12227
|
+
*/
|
|
12228
|
+
function compositeIdFormatMessage(format, logicalId, physicalId, options) {
|
|
12229
|
+
const accepted = format.alsoAccepts ? `"${formatShape(format.segments)}" or ${format.alsoAccepts}` : `"${formatShape(format.segments)}"`;
|
|
12230
|
+
const head = `Invalid physicalId format for ${format.label} ${logicalId}: expected ${accepted}, got "${physicalId}"`;
|
|
12231
|
+
if (!options?.skipping) return head;
|
|
12232
|
+
return `${head}. Skipping the delete — the AWS resource is LEFT IN PLACE and cdkd will report this delete as successful, so delete it by hand (or repair the id in state.json and re-run) if it still exists. NOTE this arm is reached from cdkd destroy AND from the deploy engine's replacement / rollback deletes.`;
|
|
12233
|
+
}
|
|
11885
12234
|
|
|
11886
12235
|
//#endregion
|
|
11887
12236
|
//#region src/provisioning/providers/lambda-event-invoke-config-provider.ts
|
|
@@ -18888,6 +19237,32 @@ function canonicalizeIpProtocolValue(value) {
|
|
|
18888
19237
|
|
|
18889
19238
|
//#endregion
|
|
18890
19239
|
//#region src/provisioning/providers/ec2-provider.ts
|
|
19240
|
+
/** Shapes of the four `AWS::EC2::*` composite physicalIds (issue #1657). */
|
|
19241
|
+
const EC2_VPC_GATEWAY_ATTACHMENT_ID_FORMAT = {
|
|
19242
|
+
label: "VPCGatewayAttachment",
|
|
19243
|
+
segments: ["internetGatewayId", "vpcId"]
|
|
19244
|
+
};
|
|
19245
|
+
const EC2_ROUTE_ID_FORMAT = {
|
|
19246
|
+
label: "Route",
|
|
19247
|
+
segments: ["routeTableId", "destination"]
|
|
19248
|
+
};
|
|
19249
|
+
const EC2_SG_INGRESS_ID_FORMAT = {
|
|
19250
|
+
label: "SecurityGroupIngress",
|
|
19251
|
+
segments: [
|
|
19252
|
+
"groupId",
|
|
19253
|
+
"ipProtocol",
|
|
19254
|
+
"fromPort",
|
|
19255
|
+
"toPort"
|
|
19256
|
+
]
|
|
19257
|
+
};
|
|
19258
|
+
const EC2_NETWORK_ACL_ENTRY_ID_FORMAT = {
|
|
19259
|
+
label: "NetworkAclEntry",
|
|
19260
|
+
segments: [
|
|
19261
|
+
"networkAclId",
|
|
19262
|
+
"ruleNumber",
|
|
19263
|
+
"egress"
|
|
19264
|
+
]
|
|
19265
|
+
};
|
|
18891
19266
|
/**
|
|
18892
19267
|
* The `AWS::EC2::Route` destination keys, in CloudFormation's own precedence
|
|
18893
19268
|
* order — which is also the order `createRoute`'s
|
|
@@ -19780,7 +20155,7 @@ var EC2Provider = class {
|
|
|
19780
20155
|
async deleteVpcGatewayAttachment(logicalId, physicalId, resourceType, context) {
|
|
19781
20156
|
this.logger.debug(`Deleting VPCGatewayAttachment ${logicalId}: ${physicalId}`);
|
|
19782
20157
|
const parts = physicalId.split("|");
|
|
19783
|
-
if (parts.length !== 2) throw new ProvisioningError(
|
|
20158
|
+
if (parts.length !== 2) throw new ProvisioningError(compositeIdFormatMessage(EC2_VPC_GATEWAY_ATTACHMENT_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
19784
20159
|
const [internetGatewayId, vpcId] = parts;
|
|
19785
20160
|
try {
|
|
19786
20161
|
await this.withDependencyViolationRetry(() => this.ec2Client.send(new DetachInternetGatewayCommand({
|
|
@@ -19973,7 +20348,7 @@ var EC2Provider = class {
|
|
|
19973
20348
|
async deleteRoute(logicalId, physicalId, resourceType, context) {
|
|
19974
20349
|
this.logger.debug(`Deleting Route ${logicalId}: ${physicalId}`);
|
|
19975
20350
|
const parts = physicalId.split("|");
|
|
19976
|
-
if (parts.length !== 2) throw new ProvisioningError(
|
|
20351
|
+
if (parts.length !== 2) throw new ProvisioningError(compositeIdFormatMessage(EC2_ROUTE_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
19977
20352
|
const [routeTableId, destination] = parts;
|
|
19978
20353
|
try {
|
|
19979
20354
|
const destinationParam = destination?.includes(":") ? { DestinationIpv6CidrBlock: destination } : destination?.startsWith("pl-") ? { DestinationPrefixListId: destination } : { DestinationCidrBlock: destination };
|
|
@@ -20317,7 +20692,7 @@ var EC2Provider = class {
|
|
|
20317
20692
|
async deleteSecurityGroupIngress(logicalId, physicalId, resourceType, properties, context) {
|
|
20318
20693
|
this.logger.debug(`Deleting SecurityGroupIngress ${logicalId}: ${physicalId}`);
|
|
20319
20694
|
const parts = physicalId.split("|");
|
|
20320
|
-
if (parts.length !== 4) throw new ProvisioningError(
|
|
20695
|
+
if (parts.length !== 4) throw new ProvisioningError(compositeIdFormatMessage(EC2_SG_INGRESS_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
20321
20696
|
const [groupId, ipProtocol, fromPortStr, toPortStr] = parts;
|
|
20322
20697
|
const ipPermission = properties ? this.buildIpPermission(properties) : {
|
|
20323
20698
|
IpProtocol: ipProtocol,
|
|
@@ -21007,7 +21382,7 @@ var EC2Provider = class {
|
|
|
21007
21382
|
this.logger.debug(`Deleting NetworkAclEntry ${logicalId}: ${physicalId}`);
|
|
21008
21383
|
const parts = physicalId.split("|");
|
|
21009
21384
|
if (parts.length < 3) {
|
|
21010
|
-
this.logger.warn(
|
|
21385
|
+
this.logger.warn(compositeIdFormatMessage(EC2_NETWORK_ACL_ENTRY_ID_FORMAT, logicalId, physicalId, { skipping: true }));
|
|
21011
21386
|
return;
|
|
21012
21387
|
}
|
|
21013
21388
|
const networkAclId = parts[0];
|
|
@@ -21317,7 +21692,7 @@ var EC2Provider = class {
|
|
|
21317
21692
|
* Skipped (return `undefined`, falls through to the comparator's
|
|
21318
21693
|
* "unsupported" outcome):
|
|
21319
21694
|
* - **AWS::EC2::VPCGatewayAttachment**: physical id is
|
|
21320
|
-
*
|
|
21695
|
+
* `<internetGatewayId>|<vpcId>`. The two ids are immutable inputs to the SDK call;
|
|
21321
21696
|
* drift detection on this resource has no useful signal beyond
|
|
21322
21697
|
* existence verification (which the user can do via the parent IGW
|
|
21323
21698
|
* / VPC drift report).
|
|
@@ -22009,6 +22384,15 @@ function isDefaultEgressRule(rule) {
|
|
|
22009
22384
|
|
|
22010
22385
|
//#endregion
|
|
22011
22386
|
//#region src/provisioning/providers/apigateway-provider.ts
|
|
22387
|
+
/** Shape of an `AWS::ApiGateway::Method` physicalId (issue #1657). */
|
|
22388
|
+
const APIGW_METHOD_ID_FORMAT = {
|
|
22389
|
+
label: "API Gateway Method",
|
|
22390
|
+
segments: [
|
|
22391
|
+
"restApiId",
|
|
22392
|
+
"resourceId",
|
|
22393
|
+
"httpMethod"
|
|
22394
|
+
]
|
|
22395
|
+
};
|
|
22012
22396
|
/**
|
|
22013
22397
|
* AWS API Gateway Provider
|
|
22014
22398
|
*
|
|
@@ -22994,7 +23378,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
|
|
|
22994
23378
|
async updateMethod(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
22995
23379
|
this.logger.debug(`Updating API Gateway Method ${logicalId}: ${physicalId}`);
|
|
22996
23380
|
const parts = physicalId.split("|");
|
|
22997
|
-
if (parts.length !== 3) throw new ProvisioningError(
|
|
23381
|
+
if (parts.length !== 3) throw new ProvisioningError(compositeIdFormatMessage(APIGW_METHOD_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
22998
23382
|
const [restApiId, resourceId, httpMethod] = parts;
|
|
22999
23383
|
const patchOperations = [];
|
|
23000
23384
|
for (const { key, path } of [
|
|
@@ -23068,7 +23452,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
|
|
|
23068
23452
|
async deleteMethod(logicalId, physicalId, resourceType, context) {
|
|
23069
23453
|
this.logger.debug(`Deleting API Gateway Method ${logicalId}: ${physicalId}`);
|
|
23070
23454
|
const parts = physicalId.split("|");
|
|
23071
|
-
if (parts.length !== 3) throw new ProvisioningError(
|
|
23455
|
+
if (parts.length !== 3) throw new ProvisioningError(compositeIdFormatMessage(APIGW_METHOD_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
23072
23456
|
const [restApiId, resourceId, httpMethod] = parts;
|
|
23073
23457
|
try {
|
|
23074
23458
|
await this.apiGatewayClient.send(new DeleteMethodCommand({
|
|
@@ -36869,6 +37253,23 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
|
|
|
36869
37253
|
|
|
36870
37254
|
//#endregion
|
|
36871
37255
|
//#region src/provisioning/providers/appsync-provider.ts
|
|
37256
|
+
/** Shapes of the three `AWS::AppSync::*` child composite physicalIds (issue #1657). */
|
|
37257
|
+
const APPSYNC_DATASOURCE_ID_FORMAT = {
|
|
37258
|
+
label: "AppSync DataSource",
|
|
37259
|
+
segments: ["apiId", "name"]
|
|
37260
|
+
};
|
|
37261
|
+
const APPSYNC_RESOLVER_ID_FORMAT = {
|
|
37262
|
+
label: "AppSync Resolver",
|
|
37263
|
+
segments: [
|
|
37264
|
+
"apiId",
|
|
37265
|
+
"typeName",
|
|
37266
|
+
"fieldName"
|
|
37267
|
+
]
|
|
37268
|
+
};
|
|
37269
|
+
const APPSYNC_APIKEY_ID_FORMAT = {
|
|
37270
|
+
label: "AppSync ApiKey",
|
|
37271
|
+
segments: ["apiId", "apiKeyId"]
|
|
37272
|
+
};
|
|
36872
37273
|
const MUTABLE_GRAPHQL_API_CONFIG_PROPERTIES = [
|
|
36873
37274
|
"UserPoolConfig",
|
|
36874
37275
|
"OpenIDConnectConfig",
|
|
@@ -37339,7 +37740,7 @@ var AppSyncProvider = class {
|
|
|
37339
37740
|
if (next !== void 0 && prev !== void 0 && next !== prev) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS AppSync DataSource.${field} is immutable — destroy + redeploy to change`);
|
|
37340
37741
|
}
|
|
37341
37742
|
const [apiId, name] = physicalId.split("|");
|
|
37342
|
-
if (!apiId || !name) throw new ProvisioningError(
|
|
37743
|
+
if (!apiId || !name) throw new ProvisioningError(compositeIdFormatMessage(APPSYNC_DATASOURCE_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
37343
37744
|
const type = properties["Type"] ?? previousProperties["Type"];
|
|
37344
37745
|
if (![
|
|
37345
37746
|
"Description",
|
|
@@ -37377,7 +37778,7 @@ var AppSyncProvider = class {
|
|
|
37377
37778
|
if (next !== void 0 && prev !== void 0 && next !== prev) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS AppSync Resolver.${field} is immutable — destroy + redeploy to change`);
|
|
37378
37779
|
}
|
|
37379
37780
|
const parts = physicalId.split("|");
|
|
37380
|
-
if (parts.length < 3) throw new ProvisioningError(
|
|
37781
|
+
if (parts.length < 3) throw new ProvisioningError(compositeIdFormatMessage(APPSYNC_RESOLVER_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
37381
37782
|
const [apiId, typeName, fieldName] = parts;
|
|
37382
37783
|
if (![
|
|
37383
37784
|
"DataSourceName",
|
|
@@ -37411,7 +37812,7 @@ var AppSyncProvider = class {
|
|
|
37411
37812
|
async updateApiKey(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
37412
37813
|
if (properties["ApiId"] !== void 0 && previousProperties["ApiId"] !== void 0 && properties["ApiId"] !== previousProperties["ApiId"]) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS AppSync ApiKey.ApiId is immutable — destroy + redeploy to change");
|
|
37413
37814
|
const [apiId, apiKeyId] = physicalId.split("|");
|
|
37414
|
-
if (!apiId || !apiKeyId) throw new ProvisioningError(
|
|
37815
|
+
if (!apiId || !apiKeyId) throw new ProvisioningError(compositeIdFormatMessage(APPSYNC_APIKEY_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
37415
37816
|
const newDesc = properties["Description"];
|
|
37416
37817
|
const oldDesc = previousProperties["Description"];
|
|
37417
37818
|
const newExp = properties["Expires"];
|
|
@@ -38117,7 +38518,7 @@ var AppSyncProvider = class {
|
|
|
38117
38518
|
this.logger.debug(`Deleting DataSource ${logicalId}: ${physicalId}`);
|
|
38118
38519
|
const [apiId, name] = physicalId.split("|");
|
|
38119
38520
|
if (!apiId || !name) {
|
|
38120
|
-
this.logger.warn(
|
|
38521
|
+
this.logger.warn(compositeIdFormatMessage(APPSYNC_DATASOURCE_ID_FORMAT, logicalId, physicalId, { skipping: true }));
|
|
38121
38522
|
return;
|
|
38122
38523
|
}
|
|
38123
38524
|
try {
|
|
@@ -38180,7 +38581,7 @@ var AppSyncProvider = class {
|
|
|
38180
38581
|
this.logger.debug(`Deleting Resolver ${logicalId}: ${physicalId}`);
|
|
38181
38582
|
const parts = physicalId.split("|");
|
|
38182
38583
|
if (parts.length < 3) {
|
|
38183
|
-
this.logger.warn(
|
|
38584
|
+
this.logger.warn(compositeIdFormatMessage(APPSYNC_RESOLVER_ID_FORMAT, logicalId, physicalId, { skipping: true }));
|
|
38184
38585
|
return;
|
|
38185
38586
|
}
|
|
38186
38587
|
const [apiId, typeName, fieldName] = parts;
|
|
@@ -38236,7 +38637,7 @@ var AppSyncProvider = class {
|
|
|
38236
38637
|
this.logger.debug(`Deleting ApiKey ${logicalId}: ${physicalId}`);
|
|
38237
38638
|
const [apiId, apiKeyId] = physicalId.split("|");
|
|
38238
38639
|
if (!apiId || !apiKeyId) {
|
|
38239
|
-
this.logger.warn(
|
|
38640
|
+
this.logger.warn(compositeIdFormatMessage(APPSYNC_APIKEY_ID_FORMAT, logicalId, physicalId, { skipping: true }));
|
|
38240
38641
|
return;
|
|
38241
38642
|
}
|
|
38242
38643
|
try {
|
|
@@ -38623,6 +39024,11 @@ var AppSyncProvider = class {
|
|
|
38623
39024
|
|
|
38624
39025
|
//#endregion
|
|
38625
39026
|
//#region src/provisioning/providers/glue-provider.ts
|
|
39027
|
+
/** Shape of an `AWS::Glue::Table` physicalId, for every decode site (issue #1657). */
|
|
39028
|
+
const GLUE_TABLE_ID_FORMAT = {
|
|
39029
|
+
label: "Glue Table",
|
|
39030
|
+
segments: ["databaseName", "tableName"]
|
|
39031
|
+
};
|
|
38626
39032
|
/**
|
|
38627
39033
|
* Read a template-borne value that is about to be forwarded to a Glue read
|
|
38628
39034
|
* API as a string.
|
|
@@ -38920,7 +39326,7 @@ var GlueProvider = class {
|
|
|
38920
39326
|
async updateTable(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
38921
39327
|
this.logger.debug(`Updating Glue Table ${logicalId}: ${physicalId}`);
|
|
38922
39328
|
const [databaseName, tableName] = physicalId.split("|");
|
|
38923
|
-
if (!databaseName || !tableName) throw new ProvisioningError(
|
|
39329
|
+
if (!databaseName || !tableName) throw new ProvisioningError(compositeIdFormatMessage(GLUE_TABLE_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
38924
39330
|
const tableInput = properties["TableInput"];
|
|
38925
39331
|
if (!tableInput) throw new ProvisioningError(`TableInput is required for Glue Table update ${logicalId}`, resourceType, logicalId, physicalId);
|
|
38926
39332
|
const catalogId = properties["CatalogId"];
|
|
@@ -38957,7 +39363,7 @@ var GlueProvider = class {
|
|
|
38957
39363
|
this.logger.debug(`Deleting Glue Table ${logicalId}: ${physicalId}`);
|
|
38958
39364
|
const [databaseName, tableName] = physicalId.split("|");
|
|
38959
39365
|
if (!databaseName || !tableName) {
|
|
38960
|
-
this.logger.warn(
|
|
39366
|
+
this.logger.warn(compositeIdFormatMessage(GLUE_TABLE_ID_FORMAT, logicalId, physicalId, { skipping: true }));
|
|
38961
39367
|
return;
|
|
38962
39368
|
}
|
|
38963
39369
|
const target = deleteCatalogId(properties);
|
|
@@ -49920,6 +50326,20 @@ var S3DirectoryBucketProvider = class {
|
|
|
49920
50326
|
|
|
49921
50327
|
//#endregion
|
|
49922
50328
|
//#region src/provisioning/providers/s3-tables-provider.ts
|
|
50329
|
+
/** Shapes of the two `AWS::S3Tables::*` composite physicalIds (issue #1657). */
|
|
50330
|
+
const S3_TABLES_NAMESPACE_ID_FORMAT = {
|
|
50331
|
+
label: "S3 Tables Namespace",
|
|
50332
|
+
segments: ["tableBucketARN", "namespace"]
|
|
50333
|
+
};
|
|
50334
|
+
const S3_TABLES_TABLE_ID_FORMAT = {
|
|
50335
|
+
label: "S3 Tables Table",
|
|
50336
|
+
segments: [
|
|
50337
|
+
"tableBucketARN",
|
|
50338
|
+
"namespace",
|
|
50339
|
+
"name"
|
|
50340
|
+
],
|
|
50341
|
+
alsoAccepts: "a table ARN"
|
|
50342
|
+
};
|
|
49923
50343
|
/**
|
|
49924
50344
|
* Parse cdkd's composite namespace physicalId `<tableBucketARN>|<namespace>`.
|
|
49925
50345
|
*
|
|
@@ -50166,7 +50586,7 @@ var S3TablesProvider = class {
|
|
|
50166
50586
|
async deleteNamespace(logicalId, physicalId, resourceType, context) {
|
|
50167
50587
|
this.logger.debug(`Deleting S3 Tables Namespace ${logicalId}: ${physicalId}`);
|
|
50168
50588
|
const [tableBucketARN, namespaceName] = physicalId.split("|");
|
|
50169
|
-
if (!tableBucketARN || !namespaceName) throw new ProvisioningError(
|
|
50589
|
+
if (!tableBucketARN || !namespaceName) throw new ProvisioningError(compositeIdFormatMessage(S3_TABLES_NAMESPACE_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
50170
50590
|
try {
|
|
50171
50591
|
await this.getClient().send(new DeleteNamespaceCommand$1({
|
|
50172
50592
|
tableBucketARN,
|
|
@@ -50580,7 +51000,7 @@ var S3TablesProvider = class {
|
|
|
50580
51000
|
this.logger.debug(`S3 Tables Table ${physicalId} does not exist, skipping deletion`);
|
|
50581
51001
|
return;
|
|
50582
51002
|
}
|
|
50583
|
-
if (resolved === "unparseable") throw new ProvisioningError(
|
|
51003
|
+
if (resolved === "unparseable") throw new ProvisioningError(compositeIdFormatMessage(S3_TABLES_TABLE_ID_FORMAT, logicalId, physicalId), resourceType, logicalId, physicalId);
|
|
50584
51004
|
const { tableBucketARN, namespace, name } = resolved;
|
|
50585
51005
|
try {
|
|
50586
51006
|
await this.getClient().send(new DeleteTableCommand$2({
|
|
@@ -50701,7 +51121,7 @@ var S3TablesProvider = class {
|
|
|
50701
51121
|
if (removedKeys.length === 0 && Object.keys(upserts).length === 0) return;
|
|
50702
51122
|
const resolvedParts = await this.resolveTableParts(physicalId);
|
|
50703
51123
|
if (resolvedParts === "not-found") throw new ProvisioningError(`applyTableTagsDiff: table '${physicalId}' no longer exists (state is out of sync) — refusing to silently drop the tag update`, resourceType, logicalId, physicalId);
|
|
50704
|
-
if (resolvedParts === "unparseable") throw new ProvisioningError(`applyTableTagsDiff:
|
|
51124
|
+
if (resolvedParts === "unparseable") throw new ProvisioningError(`applyTableTagsDiff: ${compositeIdFormatMessage(S3_TABLES_TABLE_ID_FORMAT, logicalId, physicalId)}. Cannot derive the table ARN for the tag update, and refusing to silently drop it.`, resourceType, logicalId, physicalId);
|
|
50705
51125
|
const { tableBucketARN, namespace, name } = resolvedParts;
|
|
50706
51126
|
const resourceArn = await this.lookupTableArn(tableBucketARN, namespace, name);
|
|
50707
51127
|
if (!resourceArn) throw new ProvisioningError(`applyTableTagsDiff: GetTable returned no tableARN for ${physicalId} — table is gone or state is out-of-sync. Refusing to silently drop the tag update (run 'cdkd state orphan ${logicalId}' to clean up if the table was deleted out-of-band).`, resourceType, logicalId, physicalId);
|
|
@@ -73545,7 +73965,7 @@ function createMigrateCommand() {
|
|
|
73545
73965
|
*/
|
|
73546
73966
|
function buildProgram() {
|
|
73547
73967
|
const program = new Command();
|
|
73548
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.
|
|
73968
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.281.16");
|
|
73549
73969
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
73550
73970
|
const { profile } = actionCommand.optsWithGlobals();
|
|
73551
73971
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|