@go-to-k/cdkd 0.283.5 → 0.283.7

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/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as configStringRefusal, $t as resolveSkipPrefix, A as green, At as rewriteTemplateAssetReferences, B as slowCcOperationTimeoutMs, Bt as formatDockerLoginError, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as CdkdError, Ct as shouldRetainResource, D as bold, Dn as LocalInvokeBuildError$1, Dt as buildAssetRedirectMap, E as formatResourceLine, En as DeployCancelledError, Et as WorkGraph, F as clearOnUpdateRemoval, Fn as ResourceTimeoutError, Ft as parseBootstrapMarker, G as getAccountInfo, Gt as getDockerImageBySourceHash, H as isTerminationProtectionPropagationError, Ht as runDockerForeground, I as ProviderRegistry, In as ResourceUpdateNotSupportedError, It as validateAssetBucketName, J as normalizeAwsTagsToCfn, Jt as getDefaultStateBucketName, K as refStateLookupFromResource, Kt as Synthesizer, L as findActionableSilentDrops, Ln as StackHasActiveImportsError, Lt as validateContainerRepoName, M as yellow, Mn as NestedStackChildDirectDestroyError, Mt as BOOTSTRAP_MARKER_PREFIX, N as IAMRoleProvider, Nn as PartialFailureError, Nt as ensureAssetStorage, O as cyan, On as LocalMigrateError, Ot as createAssetRedirectResolver, P as collectInlinePolicyNamesManagedBySiblings, Pn as ProvisioningError, Pt as getBootstrapMarkerKey, Q as configBooleanRefusal, Qt as resolveCaptureObservedState, R as findSilentDropProperties, Rn as StackTerminationProtectionError, Rt as buildDenyExternalAccessPolicy, S as extractDeploymentEventError, St as rebuildClientForBucketRegion, T as renderStatefulReason, Tt as stringifyValue, U as IntrinsicFunctionResolver, Un as normalizeAwsError, Ut as runDockerStreaming, V as disableInstanceApiTermination, Vt as getDockerCmd, W as cfnRefValueFromPhysicalId, Wn as withErrorHandling, Wt as AssetManifestLoader, X as assertRegionMatch, Xt as resolveApp, Y as resolveExplicitPhysicalId, Yt as getLegacyStateBucketName, Z as coerceCfnBoolean, Zt as resolveAutoAssetStorage, _ as createPreDeleteFinalSnapshot, _n as resolveBucketRegion, _t as markNonRetryable, a as DeploymentEventsStore, an as CFN_TEMPLATE_BODY_LIMIT, at as s3BucketArn, b as unsupportedFinalSnapshotError, bn as resetAwsClients, bt as LockManager, c as replayFailedOperations, cn as findLargeInlineResources, ct as s3BucketRegionalDomainName, d as IMPLICIT_DELETE_DEPENDENCIES, dn as PARTITION_TABLE, dt as DiffCalculator, en as resolveStateBucketWithDefault, et as readConfigString, f as computeImplicitDeleteEdges, fn as canonicalizeRegion, ft as describeTypeWithThrottleRetry, g as ccRoutedFinalSnapshotError, gt as isThrottlingError, h as buildFinalSnapshotIdentifier, hn as processStackMessages, ht as isRetryableTransientError, i as DeploymentEventsReader, in as warnDeprecatedNoPrefixCliFlag, it as requireConfigString, j as red, jn as MissingCdkCliError, jt as AssetModeResolver, k as gray, kn as LocalStartServiceError, kt as loadPublishableAssetManifest, l as replayRollback, ln as uploadCfnTemplate, lt as s3BucketWebsiteUrl, m as PRE_DELETE_SNAPSHOT_TYPES, mn as AssemblyReader, mt as isMarkedNonRetryable, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resolveUseCdkBootstrapAssets, nt as requireConfigArray, o as planFailedOps, on as CFN_TEMPLATE_URL_LIMIT, ot as s3BucketDomainName, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as derivePartitionAndUrlSuffix, pt as withRetry, q as WAFv2WebACLProvider, qt as synthesisStatusMessage, r as DeployEngine, rn as stateBucketExistenceConfirmed, rt as requireConfigObject, s as planRollback, sn as MIGRATE_TMP_PREFIX, st as s3BucketDualStackDomainName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveStateBucketWithDefaultAndSource, tt as replayWarn, u as withResourceDeadline, un as expectedOwnerParam, ut as applyRoleArnIfSet, v as isFinalSnapshotError, vn as AwsClients, vt as DagBuilder, w as isStatefulRecreateTargetSync, wt as AssetPublisher, x as makeCanonicalizePropertiesFn, xn as setAwsClients, xt as S3StateBackend, y as refusesFinalSnapshot, yn as getAwsClients, yt as TemplateParser, z as CloudControlProvider, zt as buildDockerImage } from "./deploy-engine-BcwNPlSk.js";
2
+ import { $ as configStringRefusal, $t as resolveSkipPrefix, A as green, At as rewriteTemplateAssetReferences, B as slowCcOperationTimeoutMs, Bt as formatDockerLoginError, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as CdkdError, Ct as shouldRetainResource, D as bold, Dn as LocalInvokeBuildError$1, Dt as buildAssetRedirectMap, E as formatResourceLine, En as DeployCancelledError, Et as WorkGraph, F as clearOnUpdateRemoval, Fn as ResourceTimeoutError, Ft as parseBootstrapMarker, G as getAccountInfo, Gt as getDockerImageBySourceHash, H as isTerminationProtectionPropagationError, Ht as runDockerForeground, I as ProviderRegistry, In as ResourceUpdateNotSupportedError, It as validateAssetBucketName, J as normalizeAwsTagsToCfn, Jt as getDefaultStateBucketName, K as refStateLookupFromResource, Kt as Synthesizer, L as findActionableSilentDrops, Ln as StackHasActiveImportsError, Lt as validateContainerRepoName, M as yellow, Mn as NestedStackChildDirectDestroyError, Mt as BOOTSTRAP_MARKER_PREFIX, N as IAMRoleProvider, Nn as PartialFailureError, Nt as ensureAssetStorage, O as cyan, On as LocalMigrateError, Ot as createAssetRedirectResolver, P as collectInlinePolicyNamesManagedBySiblings, Pn as ProvisioningError, Pt as getBootstrapMarkerKey, Q as configBooleanRefusal, Qt as resolveCaptureObservedState, R as findSilentDropProperties, Rn as StackTerminationProtectionError, Rt as buildDenyExternalAccessPolicy, S as extractDeploymentEventError, St as rebuildClientForBucketRegion, T as renderStatefulReason, Tt as stringifyValue, U as IntrinsicFunctionResolver, Un as normalizeAwsError, Ut as runDockerStreaming, V as disableInstanceApiTermination, Vt as getDockerCmd, W as cfnRefValueFromPhysicalId, Wn as withErrorHandling, Wt as AssetManifestLoader, X as assertRegionMatch, Xt as resolveApp, Y as resolveExplicitPhysicalId, Yt as getLegacyStateBucketName, Z as coerceCfnBoolean, Zt as resolveAutoAssetStorage, _ as createPreDeleteFinalSnapshot, _n as resolveBucketRegion, _t as markNonRetryable, a as DeploymentEventsStore, an as CFN_TEMPLATE_BODY_LIMIT, at as s3BucketArn, b as unsupportedFinalSnapshotError, bn as resetAwsClients, bt as LockManager, c as replayFailedOperations, cn as findLargeInlineResources, ct as s3BucketRegionalDomainName, d as IMPLICIT_DELETE_DEPENDENCIES, dn as PARTITION_TABLE, dt as DiffCalculator, en as resolveStateBucketWithDefault, et as readConfigString, f as computeImplicitDeleteEdges, fn as canonicalizeRegion, ft as describeTypeWithThrottleRetry, g as ccRoutedFinalSnapshotError, gt as isThrottlingError, h as buildFinalSnapshotIdentifier, hn as processStackMessages, ht as isRetryableTransientError, i as DeploymentEventsReader, in as warnDeprecatedNoPrefixCliFlag, it as requireConfigString, j as red, jn as MissingCdkCliError, jt as AssetModeResolver, k as gray, kn as LocalStartServiceError, kt as loadPublishableAssetManifest, l as replayRollback, ln as uploadCfnTemplate, lt as s3BucketWebsiteUrl, m as PRE_DELETE_SNAPSHOT_TYPES, mn as AssemblyReader, mt as isMarkedNonRetryable, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resolveUseCdkBootstrapAssets, nt as requireConfigArray, o as planFailedOps, on as CFN_TEMPLATE_URL_LIMIT, ot as s3BucketDomainName, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as derivePartitionAndUrlSuffix, pt as withRetry, q as WAFv2WebACLProvider, qt as synthesisStatusMessage, r as DeployEngine, rn as stateBucketExistenceConfirmed, rt as requireConfigObject, s as planRollback, sn as MIGRATE_TMP_PREFIX, st as s3BucketDualStackDomainName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveStateBucketWithDefaultAndSource, tt as replayWarn, u as withResourceDeadline, un as expectedOwnerParam, ut as applyRoleArnIfSet, v as isFinalSnapshotError, vn as AwsClients, vt as DagBuilder, w as isStatefulRecreateTargetSync, wt as AssetPublisher, x as makeCanonicalizePropertiesFn, xn as setAwsClients, xt as S3StateBackend, y as refusesFinalSnapshot, yn as getAwsClients, yt as TemplateParser, z as CloudControlProvider, zt as buildDockerImage } from "./deploy-engine-CKpBSlde.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-B9eSuRPc.js";
4
+ import { t as ASGProvider } from "./asg-provider-o7QsopaI.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";
@@ -40935,7 +40935,7 @@ var GlueProvider = class {
40935
40935
  }
40936
40936
  async create(logicalId, resourceType, properties, context) {
40937
40937
  switch (resourceType) {
40938
- case "AWS::Glue::Database": return this.createDatabase(logicalId, resourceType, properties);
40938
+ case "AWS::Glue::Database": return this.createDatabase(logicalId, resourceType, properties, context);
40939
40939
  case "AWS::Glue::Table": return this.createTable(logicalId, resourceType, properties, context);
40940
40940
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
40941
40941
  }
@@ -40954,7 +40954,7 @@ var GlueProvider = class {
40954
40954
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
40955
40955
  }
40956
40956
  }
40957
- async createDatabase(logicalId, resourceType, properties) {
40957
+ async createDatabase(logicalId, resourceType, properties, context) {
40958
40958
  this.logger.debug(`Creating Glue Database ${logicalId}`);
40959
40959
  const databaseInput = properties["DatabaseInput"];
40960
40960
  if (!databaseInput) throw new ProvisioningError(`DatabaseInput is required for Glue Database ${logicalId}`, resourceType, logicalId);
@@ -40964,7 +40964,7 @@ var GlueProvider = class {
40964
40964
  try {
40965
40965
  await this.getClient().send(new CreateDatabaseCommand({
40966
40966
  CatalogId: catalogId,
40967
- DatabaseInput: this.buildDatabaseInput(databaseInput, databaseName)
40967
+ DatabaseInput: this.buildDatabaseInput(databaseInput, databaseName, { ...replayWarn(this.logger, context) })
40968
40968
  }));
40969
40969
  this.logger.debug(`Successfully created Glue Database ${logicalId}: ${databaseName}`);
40970
40970
  return {
@@ -40982,9 +40982,13 @@ var GlueProvider = class {
40982
40982
  if (!databaseInput) throw new ProvisioningError(`DatabaseInput is required for Glue Database update ${logicalId}`, resourceType, logicalId, physicalId);
40983
40983
  const catalogId = properties["CatalogId"];
40984
40984
  const liveParameters = await this.readLiveDatabaseParameters(logicalId, resourceType, physicalId, catalogId);
40985
+ const previousDatabaseInput = asRecord(previousProperties?.["DatabaseInput"]);
40985
40986
  try {
40986
- const builtDatabaseInput = this.buildDatabaseInput(databaseInput, physicalId);
40987
- this.preserveAwsManagedParameters(builtDatabaseInput, databaseInput["Parameters"], (previousProperties?.["DatabaseInput"])?.["Parameters"], liveParameters);
40987
+ const builtDatabaseInput = this.buildDatabaseInput(databaseInput, physicalId, {
40988
+ onUnusable: (message) => this.logger.warn(message),
40989
+ previousDatabaseInput
40990
+ });
40991
+ this.preserveAwsManagedParameters(builtDatabaseInput, databaseInput["Parameters"], previousDatabaseInput?.["Parameters"], liveParameters);
40988
40992
  await this.getClient().send(new UpdateDatabaseCommand({
40989
40993
  ...catalogId !== void 0 && { CatalogId: catalogId },
40990
40994
  Name: physicalId,
@@ -41358,15 +41362,137 @@ var GlueProvider = class {
41358
41362
  * so empty-string Description, empty Parameters map, etc. reach AWS
41359
41363
  * intact — `cdkd drift --revert` relies on this to clear console-side
41360
41364
  * additions.
41361
- */
41362
- buildDatabaseInput(databaseInput, fallbackName) {
41365
+ *
41366
+ * `options` carries the shape-guard policy for the three nested blocks added
41367
+ * by issue #1807, and the two call sites answer differently because their
41368
+ * desired bag has a different provenance (`.claude/rules/providers.md`):
41369
+ *
41370
+ * - CREATE passes NO `onUnusable`, so a malformed block is REFUSED before
41371
+ * the AWS call — the value is template-borne, and the alternative is
41372
+ * sending an empty `{}` block AWS would reject with a far less
41373
+ * actionable message. Under `CreateContext.replayingState` the refusal
41374
+ * downgrades through the shared `replayWarn`, since a reverse-replacement
41375
+ * re-create reads a STATE record the user cannot edit from the template.
41376
+ * - UPDATE passes a warn callback UNCONDITIONALLY (the `updateRoute`
41377
+ * precedent — `update()` has no context, so it cannot tell a template
41378
+ * push from the state-borne bag `drift --revert` and the rollback revert
41379
+ * arm hand it), and then RETAINS the PREVIOUS side's block, which is why
41380
+ * `previousDatabaseInput` is threaded in. Omitting instead would ERASE a
41381
+ * live resource link, because `UpdateDatabase` replaces `DatabaseInput`
41382
+ * wholesale — the #1612 "UPDATE retains the previous value" row, reached
41383
+ * here through a whole-blob API rather than a per-item one. When the
41384
+ * previous side is unusable too the key is DROPPED: there is no value
41385
+ * cdkd can vouch for either way (#1653 review).
41386
+ */
41387
+ buildDatabaseInput(databaseInput, fallbackName, options) {
41388
+ const guardOptions = options?.onUnusable ? { onUnusable: options.onUnusable } : void 0;
41389
+ const previous = options?.previousDatabaseInput;
41390
+ /**
41391
+ * The record a nested block is BUILT FROM: the desired side when it names
41392
+ * at least one sendable member, else the previous side when IT does, else
41393
+ * nothing.
41394
+ *
41395
+ * The emptiness test is what makes the ladder complete, and getting it
41396
+ * wrong is how the first cut of this guard still erased a live resource
41397
+ * link: a bare `{}` and an unresolved `{Ref: ...}` are plain OBJECTS, so
41398
+ * the shape guard accepts them and a `?? previous` fallback never fires —
41399
+ * the block then built empty and `UpdateDatabase`, which replaces
41400
+ * `DatabaseInput` wholesale, dropped a link the template still declared.
41401
+ *
41402
+ * A member counts as sendable only when it is a STRING or a NUMBER: the
41403
+ * leaf casts below are unchecked, so an intrinsic-valued `DatabaseName`
41404
+ * would otherwise put an OBJECT on the wire — the same defect one level in.
41405
+ */
41406
+ const pickBlockSource = (desiredRaw, previousRaw, path, block, members) => {
41407
+ const namesSendable = (bag) => {
41408
+ if (bag === void 0) return false;
41409
+ const present = members.filter((m) => bag[m] !== void 0);
41410
+ return present.length > 0 && present.every((m) => isSendableLeaf(bag[m]));
41411
+ };
41412
+ const desired = guardOptions ? requireConfigObject(desiredRaw, path, guardOptions) : requireConfigObject(desiredRaw, path);
41413
+ if (namesSendable(desired)) return desired;
41414
+ if (desired !== void 0) {
41415
+ const message = emptyBlockMessage(block);
41416
+ if (options?.onUnusable) options.onUnusable(message);
41417
+ else throw new Error(message);
41418
+ }
41419
+ const retained = asRecord(previousRaw);
41420
+ return namesSendable(retained) ? retained : void 0;
41421
+ };
41363
41422
  const result = { Name: databaseInput["Name"] ?? fallbackName };
41364
41423
  if (databaseInput["Description"] !== void 0) result.Description = databaseInput["Description"];
41365
41424
  if (databaseInput["LocationUri"] !== void 0) result.LocationUri = databaseInput["LocationUri"];
41366
41425
  if (databaseInput["Parameters"] !== void 0) result.Parameters = stringifyParameterValues(databaseInput["Parameters"]);
41426
+ if (databaseInput["TargetDatabase"] !== void 0) {
41427
+ const target = pickBlockSource(databaseInput["TargetDatabase"], previous?.["TargetDatabase"], "AWS::Glue::Database DatabaseInput.TargetDatabase", "TargetDatabase", TARGET_DATABASE_MEMBERS);
41428
+ if (target !== void 0) {
41429
+ const targetDatabase = {};
41430
+ if (isSendableLeaf(target["CatalogId"])) targetDatabase.CatalogId = String(target["CatalogId"]);
41431
+ if (isSendableLeaf(target["DatabaseName"])) targetDatabase.DatabaseName = String(target["DatabaseName"]);
41432
+ if (isSendableLeaf(target["Region"])) targetDatabase.Region = String(target["Region"]);
41433
+ result.TargetDatabase = targetDatabase;
41434
+ }
41435
+ }
41436
+ if (databaseInput["FederatedDatabase"] !== void 0) {
41437
+ const federated = pickBlockSource(databaseInput["FederatedDatabase"], previous?.["FederatedDatabase"], "AWS::Glue::Database DatabaseInput.FederatedDatabase", "FederatedDatabase", FEDERATED_DATABASE_MEMBERS);
41438
+ if (federated !== void 0) {
41439
+ const federatedDatabase = {};
41440
+ if (isSendableLeaf(federated["ConnectionName"])) federatedDatabase.ConnectionName = String(federated["ConnectionName"]);
41441
+ if (isSendableLeaf(federated["Identifier"])) federatedDatabase.Identifier = String(federated["Identifier"]);
41442
+ result.FederatedDatabase = federatedDatabase;
41443
+ }
41444
+ }
41445
+ if (databaseInput["CreateTableDefaultPermissions"] !== void 0) {
41446
+ const permissions = this.usableDefaultPermissions(databaseInput["CreateTableDefaultPermissions"], guardOptions) ?? this.usableDefaultPermissions(previous?.["CreateTableDefaultPermissions"], { onUnusable: () => void 0 });
41447
+ if (permissions !== void 0) result.CreateTableDefaultPermissions = permissions.map((entry) => {
41448
+ const permission = {};
41449
+ if (entry["Permissions"] !== void 0) permission.Permissions = entry["Permissions"];
41450
+ const declaredPrincipal = asRecord(entry["Principal"]);
41451
+ if (declaredPrincipal !== void 0) {
41452
+ const principal = {};
41453
+ if (isSendableLeaf(declaredPrincipal["DataLakePrincipalIdentifier"])) principal.DataLakePrincipalIdentifier = String(declaredPrincipal["DataLakePrincipalIdentifier"]);
41454
+ permission.Principal = principal;
41455
+ }
41456
+ return permission;
41457
+ });
41458
+ }
41367
41459
  return result;
41368
41460
  }
41369
41461
  /**
41462
+ * `DatabaseInput.CreateTableDefaultPermissions` as a USABLE list, or
41463
+ * `undefined` when the declared value is a shape cdkd refuses to narrow.
41464
+ *
41465
+ * The block is validated ALL-OR-NOTHING rather than per entry, which is the
41466
+ * opposite of the S3 per-`Id` appliers and deliberate: this is a Lake
41467
+ * Formation grant list delivered by ONE wholesale `CreateDatabase` /
41468
+ * `UpdateDatabase` call, so dropping the entries cdkd cannot read would
41469
+ * silently NARROW the permissions the template declared — the failure this
41470
+ * guard exists to prevent, one level in from the block itself. A malformed
41471
+ * ENTRY therefore invalidates the block, and the caller then refuses (create)
41472
+ * or retains the previous list (update).
41473
+ *
41474
+ * `Permissions` is checked for ARRAY-ness for the same reason: the SDK member
41475
+ * is `Permission[]`, so a bare string would be forwarded verbatim and the
41476
+ * serializer would drop it, granting nothing while the deploy reports success.
41477
+ */
41478
+ usableDefaultPermissions(value, guardOptions) {
41479
+ if (value === void 0) return void 0;
41480
+ const path = "AWS::Glue::Database DatabaseInput.CreateTableDefaultPermissions";
41481
+ const entries = guardOptions ? requireConfigArray(value, path, guardOptions) : requireConfigArray(value, path);
41482
+ if (entries === void 0) return void 0;
41483
+ for (const entry of entries) {
41484
+ const principal = asRecord(entry["Principal"]);
41485
+ if (asRecord(entry) !== void 0 && (entry["Permissions"] !== void 0 || entry["Principal"] !== void 0) && (entry["Permissions"] === void 0 || Array.isArray(entry["Permissions"])) && (entry["Principal"] === void 0 || principal !== void 0 && isSendableLeaf(principal["DataLakePrincipalIdentifier"]))) continue;
41486
+ const detail = `${path} carries an entry cdkd cannot read (each entry must be an object naming Permissions as a list and/or Principal as an object whose DataLakePrincipalIdentifier is a string or a number)`;
41487
+ if (guardOptions?.onUnusable) {
41488
+ guardOptions.onUnusable(`${detail}. Leaving the whole block unapplied rather than sending a NARROWED grant list. On an UPDATE that is a RESET: UpdateDatabase replaces DatabaseInput wholesale, so unless a usable previous list is retained the database falls back to the account default (IAM_ALLOWED_PRINCIPALS / ALL) — fix the template value to restore the declared grants`);
41489
+ return;
41490
+ }
41491
+ throw new Error(detail);
41492
+ }
41493
+ return entries;
41494
+ }
41495
+ /**
41370
41496
  * Build TableInput for Glue API from CFn template properties
41371
41497
  */
41372
41498
  buildTableInput(tableInput, fallbackName) {
@@ -41476,9 +41602,30 @@ var GlueProvider = class {
41476
41602
  * readback for it (and fabricating a placeholder would itself fire false
41477
41603
  * drift). Declaring it here keeps the drift comparator from false-positiving
41478
41604
  * on a state-recorded `OpenTableFormatInput` that the readback never surfaces.
41605
+ *
41606
+ * `AWS::Glue::Database` declares `DatabaseInput.CreateTableDefaultPermissions`
41607
+ * PER RESOURCE (the issue #1602 seam), and only for a database whose template
41608
+ * does NOT declare the block. AWS materializes a Lake Formation DEFAULT there
41609
+ * — `[{IAM_ALLOWED_PRINCIPALS, [ALL]}]` on a catalog that has not been
41610
+ * onboarded to Lake Formation (measured us-east-1, 2026-08-13) — so once
41611
+ * issue #1807 taught `readDatabase` to surface the block, comparing it would
41612
+ * report drift on EVERY existing database: the baseline is an
41613
+ * `observedProperties` capture written by a binary that did not read the key,
41614
+ * `drift.ts` walks the key UNION on that baseline (`unionWalkObjects`), and
41615
+ * `deploy-engine.ts` does not refresh a record that already has one — so the
41616
+ * phantom would persist until an unrelated UPDATE or a `drift --accept`.
41617
+ * This is the ELBv2 `Targets` shape one type over: a value a SIBLING owns is
41618
+ * ignored while the template declares none, and an explicit declaration —
41619
+ * including an empty list — IS a declaration and stays compared.
41620
+ *
41621
+ * `TargetDatabase` / `FederatedDatabase` need no such scoping: AWS reports
41622
+ * them only when they were actually set, so an appearance is real drift.
41479
41623
  */
41480
- getDriftUnknownPaths(resourceType) {
41624
+ getDriftUnknownPaths(resourceType, properties) {
41481
41625
  if (resourceType === "AWS::Glue::Table") return ["OpenTableFormatInput"];
41626
+ if (resourceType === "AWS::Glue::Database") {
41627
+ if (asRecord(properties?.["DatabaseInput"])?.["CreateTableDefaultPermissions"] === void 0) return ["DatabaseInput.CreateTableDefaultPermissions"];
41628
+ }
41482
41629
  return [];
41483
41630
  }
41484
41631
  async readDatabase(physicalId, catalogId) {
@@ -41498,6 +41645,9 @@ var GlueProvider = class {
41498
41645
  dbInput["Description"] = db.Description ?? "";
41499
41646
  if (db.LocationUri !== void 0) dbInput["LocationUri"] = db.LocationUri;
41500
41647
  dbInput["Parameters"] = db.Parameters ?? {};
41648
+ if (db.TargetDatabase !== void 0) dbInput["TargetDatabase"] = { ...db.TargetDatabase };
41649
+ if (db.FederatedDatabase !== void 0) dbInput["FederatedDatabase"] = { ...db.FederatedDatabase };
41650
+ if (db.CreateTableDefaultPermissions !== void 0) dbInput["CreateTableDefaultPermissions"] = db.CreateTableDefaultPermissions.map((entry) => ({ ...entry }));
41501
41651
  const result = { DatabaseInput: dbInput };
41502
41652
  if (db.Name !== void 0) result["DatabaseName"] = db.Name;
41503
41653
  return result;
@@ -42380,6 +42530,35 @@ function parameterKeySet(value) {
42380
42530
  * its `Fn::*`/`Ref` key — harmless here, since providers only ever see
42381
42531
  * resolved values.)
42382
42532
  */
42533
+ /**
42534
+ * The CFn members of the two `DatabaseInput` object blocks, as the SENDABLE-ness
42535
+ * test sees them. They must stay in step with the per-member writes in
42536
+ * `buildDatabaseInput` — a name here that the builder does not write would let
42537
+ * an EMPTY block onto the wire, and one the builder writes but this list omits
42538
+ * would refuse a valid template that names only it. The unit suite fences both
42539
+ * directions by asserting each member ALONE reaches AWS, which fails on a
42540
+ * dropped entry (the member stops being sendable) and on an added one (the
42541
+ * block builds empty).
42542
+ */
42543
+ const TARGET_DATABASE_MEMBERS = [
42544
+ "CatalogId",
42545
+ "DatabaseName",
42546
+ "Region"
42547
+ ];
42548
+ const FEDERATED_DATABASE_MEMBERS = ["ConnectionName", "Identifier"];
42549
+ /**
42550
+ * Is this leaf value one cdkd can put on the wire? A STRING or a NUMBER — CFn
42551
+ * is stringly typed and an unquoted YAML account id arrives as a number (the
42552
+ * reason `catalogIdForApi` exists) — and nothing else, so an unresolved
42553
+ * intrinsic cannot ride an unchecked cast into the request.
42554
+ */
42555
+ const isSendableLeaf = (value) => typeof value === "string" || typeof value === "number" && Number.isFinite(value);
42556
+ /**
42557
+ * The refusal sentence for a `DatabaseInput` block that IS a plain object and
42558
+ * still names nothing cdkd can send — an unresolved intrinsic or a bare `{}`.
42559
+ * Shared so the create refusal and the update warning cannot drift apart.
42560
+ */
42561
+ const emptyBlockMessage = (block) => `AWS::Glue::Database DatabaseInput.${block} declares no member cdkd can send (an unresolved intrinsic, an empty block), or mixes sendable members with unreadable ones; sending it would put an empty or NARROWED ${block} on the wire. Where this is only WARNED — an update, or a state replay — the previously applied block is retained if cdkd can still read one, and otherwise the key is omitted, which UpdateDatabase applies as a removal because it replaces DatabaseInput wholesale`;
42383
42562
  function asRecord(value) {
42384
42563
  if (typeof value !== "object" || value === null || Array.isArray(value)) return;
42385
42564
  return value;
@@ -77006,7 +77185,7 @@ function createMigrateCommand() {
77006
77185
  */
77007
77186
  function buildProgram() {
77008
77187
  const program = new Command();
77009
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.5");
77188
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.7");
77010
77189
  program.hook("preAction", (_thisCommand, actionCommand) => {
77011
77190
  const { profile } = actionCommand.optsWithGlobals();
77012
77191
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;