@go-to-k/cdkd 0.117.0 → 0.117.2

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
@@ -44,10 +44,10 @@ import { CreateWebACLCommand, DeleteWebACLCommand, GetWebACLCommand, ListTagsFor
44
44
  import { CognitoIdentityProviderClient, CreateUserPoolCommand, DeleteUserPoolCommand, DescribeUserPoolCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$14, ListUserPoolsCommand, ResourceNotFoundException as ResourceNotFoundException$6, UpdateUserPoolCommand } from "@aws-sdk/client-cognito-identity-provider";
45
45
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$4, CreateCacheClusterCommand, CreateCacheSubnetGroupCommand, DeleteCacheClusterCommand, DeleteCacheSubnetGroupCommand, DescribeCacheClustersCommand, DescribeCacheSubnetGroupsCommand, ElastiCacheClient, ListTagsForResourceCommand as ListTagsForResourceCommand$15, ModifyCacheClusterCommand, ModifyCacheSubnetGroupCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$4 } from "@aws-sdk/client-elasticache";
46
46
  import { CreatePrivateDnsNamespaceCommand, CreateServiceCommand as CreateServiceCommand$1, DeleteNamespaceCommand, DeleteServiceCommand as DeleteServiceCommand$1, GetNamespaceCommand, GetOperationCommand, GetServiceCommand, ListNamespacesCommand, ListServicesCommand as ListServicesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$16, NamespaceNotFound, ServiceDiscoveryClient, ServiceNotFound, UpdatePrivateDnsNamespaceCommand, UpdateServiceCommand as UpdateServiceCommand$1 } from "@aws-sdk/client-servicediscovery";
47
- import { AppSyncClient, CreateApiKeyCommand, CreateDataSourceCommand, CreateGraphqlApiCommand, CreateResolverCommand, DeleteApiKeyCommand, DeleteDataSourceCommand, DeleteGraphqlApiCommand, DeleteResolverCommand, GetDataSourceCommand, GetGraphqlApiCommand, GetIntrospectionSchemaCommand, GetResolverCommand, ListApiKeysCommand, ListGraphqlApisCommand, NotFoundException as NotFoundException$4, StartSchemaCreationCommand } from "@aws-sdk/client-appsync";
47
+ import { AppSyncClient, CreateApiKeyCommand, CreateDataSourceCommand, CreateGraphqlApiCommand, CreateResolverCommand, DeleteApiKeyCommand, DeleteDataSourceCommand, DeleteGraphqlApiCommand, DeleteResolverCommand, GetDataSourceCommand, GetGraphqlApiCommand, GetIntrospectionSchemaCommand, GetResolverCommand, ListApiKeysCommand, ListGraphqlApisCommand, NotFoundException as NotFoundException$4, StartSchemaCreationCommand, TagResourceCommand as TagResourceCommand$13, UntagResourceCommand as UntagResourceCommand$12, UpdateApiKeyCommand, UpdateDataSourceCommand, UpdateGraphqlApiCommand, UpdateResolverCommand } from "@aws-sdk/client-appsync";
48
48
  import { parse, print } from "graphql";
49
49
  import { CreateConnectionCommand, CreateCrawlerCommand, CreateDatabaseCommand, CreateJobCommand, CreateSecurityConfigurationCommand, CreateTableCommand as CreateTableCommand$1, CreateTriggerCommand, CreateWorkflowCommand, DeleteConnectionCommand, DeleteCrawlerCommand, DeleteDatabaseCommand, DeleteJobCommand, DeleteSecurityConfigurationCommand, DeleteTableCommand as DeleteTableCommand$1, DeleteTriggerCommand, DeleteWorkflowCommand, EntityNotFoundException, GetConnectionCommand, GetCrawlerCommand, GetDatabaseCommand, GetDatabasesCommand, GetJobCommand, GetSecurityConfigurationCommand, GetSecurityConfigurationsCommand, GetTableCommand, GetTablesCommand, GetTagsCommand, GetTriggerCommand, GetWorkflowCommand, GlueClient, ListWorkflowsCommand, StartCrawlerScheduleCommand, StartTriggerCommand, StopCrawlerScheduleCommand, StopTriggerCommand, UpdateConnectionCommand, UpdateCrawlerCommand, UpdateDatabaseCommand, UpdateJobCommand, UpdateTableCommand as UpdateTableCommand$1, UpdateTriggerCommand, UpdateWorkflowCommand } from "@aws-sdk/client-glue";
50
- import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListStreamsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$7, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$13, UntagResourceCommand as UntagResourceCommand$12, UpdateShardCountCommand } from "@aws-sdk/client-kinesis";
50
+ import { AddTagsToStreamCommand, CreateStreamCommand, DecreaseStreamRetentionPeriodCommand, DeleteStreamCommand, DeregisterStreamConsumerCommand, DescribeStreamCommand, DescribeStreamConsumerCommand, IncreaseStreamRetentionPeriodCommand, KinesisClient, ListStreamsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, ListTagsForStreamCommand, RegisterStreamConsumerCommand, RemoveTagsFromStreamCommand, ResourceNotFoundException as ResourceNotFoundException$7, StartStreamEncryptionCommand, StopStreamEncryptionCommand, TagResourceCommand as TagResourceCommand$14, UntagResourceCommand as UntagResourceCommand$13, UpdateShardCountCommand } from "@aws-sdk/client-kinesis";
51
51
  import { AccessPointNotFound, CreateAccessPointCommand, CreateFileSystemCommand, CreateMountTargetCommand, DeleteAccessPointCommand, DeleteFileSystemCommand, DeleteMountTargetCommand, DescribeAccessPointsCommand, DescribeBackupPolicyCommand, DescribeFileSystemsCommand, DescribeLifecycleConfigurationCommand, DescribeMountTargetSecurityGroupsCommand, DescribeMountTargetsCommand, EFSClient, FileSystemNotFound, ModifyMountTargetSecurityGroupsCommand, MountTargetNotFound, UpdateFileSystemCommand } from "@aws-sdk/client-efs";
52
52
  import { CreateDeliveryStreamCommand, DeleteDeliveryStreamCommand, DescribeDeliveryStreamCommand, FirehoseClient, ListDeliveryStreamsCommand, ListTagsForDeliveryStreamCommand, ResourceNotFoundException as ResourceNotFoundException$8 } from "@aws-sdk/client-firehose";
53
53
  import { AddTagsCommand as AddTagsCommand$1, CloudTrailClient, CreateTrailCommand, DeleteTrailCommand, GetEventSelectorsCommand, GetInsightSelectorsCommand, GetTrailCommand, GetTrailStatusCommand, ListTagsCommand as ListTagsCommand$1, ListTrailsCommand, PutEventSelectorsCommand, PutInsightSelectorsCommand, RemoveTagsCommand as RemoveTagsCommand$1, StartLoggingCommand, StopLoggingCommand, TrailNotFoundException, UpdateTrailCommand } from "@aws-sdk/client-cloudtrail";
@@ -7375,7 +7375,7 @@ var LambdaLayerVersionProvider = class {
7375
7375
  * template should re-deploy with `--replace`.
7376
7376
  */
7377
7377
  async update(logicalId, _physicalId, resourceType, _properties, _previousProperties) {
7378
- return Promise.reject(new ResourceUpdateNotSupportedError(resourceType, logicalId, "Lambda layer versions are immutable on AWS; re-deploy with cdkd deploy --replace, or change the resource definition to publish a new version"));
7378
+ return Promise.reject(new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS Lambda LayerVersion is immutable on AWS — there is no UpdateLayerVersion API; every change requires PublishLayerVersion (a new version with a new LayerVersionArn). Re-deploy with cdkd deploy --replace, or change the resource definition to publish a new version."));
7379
7379
  }
7380
7380
  /**
7381
7381
  * Delete a Lambda layer version
@@ -7901,11 +7901,12 @@ var DynamoDBTableProvider = class {
7901
7901
  * is surfaced for BOTH the LOCAL replica AND cross-region replicas
7902
7902
  * via per-region SDK clients (cached in `regionalClientCache` for
7903
7903
  * the deploy run). Issue #389 lifted the v1 LOCAL-only limitation.
7904
- * - Cross-region replica Tags propagation (Issue #389): when the
7905
- * update path detects a Tags-only diff on a non-local replica,
7906
- * cdkd resolves the replica's table ARN by swapping the region
7907
- * segment of the local ARN and issues `TagResource` /
7908
- * `UntagResource` against a per-region client.
7904
+ * - Cross-region replica Tags propagation (Issue #389 / #441):
7905
+ * BOTH `create()` and `update()` resolve each non-local replica's
7906
+ * table ARN by swapping the region segment of the local ARN and
7907
+ * issue `TagResource` / `UntagResource` against a per-region
7908
+ * client. The shared helper `applyCrossRegionReplicaTagsDiff`
7909
+ * centralizes the diff + best-effort WARN-on-failure contract.
7909
7910
  */
7910
7911
  var DynamoDBGlobalTableProvider = class {
7911
7912
  dynamoDBClient;
@@ -8114,6 +8115,13 @@ var DynamoDBGlobalTableProvider = class {
8114
8115
  if (!region || region === currentRegion) continue;
8115
8116
  await this.addReplica(tableName, replica, region, logicalId);
8116
8117
  }
8118
+ for (const replica of replicas) {
8119
+ const region = replica["Region"];
8120
+ if (!region || region === currentRegion) continue;
8121
+ const replicaTags = replica["Tags"];
8122
+ if (!replicaTags || replicaTags.length === 0) continue;
8123
+ await this.applyCrossRegionReplicaTagsDiff(tableInfo.tableArn, region, void 0, replicaTags, tableName);
8124
+ }
8117
8125
  if (properties["TimeToLiveSpecification"]) {
8118
8126
  const ttl = properties["TimeToLiveSpecification"];
8119
8127
  const attributeName = ttl["AttributeName"];
@@ -8299,6 +8307,8 @@ var DynamoDBGlobalTableProvider = class {
8299
8307
  const region = replica["Region"];
8300
8308
  if (!region || region === currentRegion) continue;
8301
8309
  await this.addReplica(physicalId, replica, region, logicalId);
8310
+ const newReplicaTags = replica["Tags"];
8311
+ await this.applyCrossRegionReplicaTagsDiff(tableArn, region, void 0, newReplicaTags, physicalId);
8302
8312
  const newReadAutoScaling = (replica["ReadProvisionedThroughputSettings"] ?? {})["ReadCapacityAutoScalingSettings"];
8303
8313
  if (newBilling === "PROVISIONED" && newReadAutoScaling) {
8304
8314
  const regionalAutoScalingClient = this.getRegionalAutoScalingClient(region);
@@ -8311,16 +8321,7 @@ var DynamoDBGlobalTableProvider = class {
8311
8321
  const oldReplica = (previousProperties["Replicas"] ?? []).find((r) => r["Region"] === region);
8312
8322
  const oldReplicaTags = oldReplica?.["Tags"];
8313
8323
  const newReplicaTags = replica["Tags"];
8314
- if (!deepEqual$1(oldReplicaTags, newReplicaTags)) if (tableArn) {
8315
- const replicaArn = this.replicaArnForRegion(tableArn, region);
8316
- if (replicaArn) try {
8317
- const regionalClient = this.getRegionalClient(region);
8318
- await this.applyTagDiffOnClient(regionalClient, replicaArn, oldReplicaTags, newReplicaTags);
8319
- } catch (tagErr) {
8320
- this.logger.warn(`Could not apply Tags diff to cross-region replica ${region} of ${physicalId}: ${tagErr instanceof Error ? tagErr.message : String(tagErr)}. The replica's Tags state will surface as drift until the next successful deploy.`);
8321
- }
8322
- else this.logger.warn(`Could not derive replica ARN for region ${region} from ${tableArn} — skipping Tags propagation for ${physicalId}`);
8323
- } else this.logger.warn(`Local DescribeTable returned no TableArn — cannot propagate Tags to cross-region replica ${region} of ${physicalId}`);
8324
+ await this.applyCrossRegionReplicaTagsDiff(tableArn, region, oldReplicaTags, newReplicaTags, physicalId);
8324
8325
  const oldReadAutoScaling = (oldReplica?.["ReadProvisionedThroughputSettings"] ?? {})["ReadCapacityAutoScalingSettings"];
8325
8326
  const newReadAutoScaling = (replica["ReadProvisionedThroughputSettings"] ?? {})["ReadCapacityAutoScalingSettings"];
8326
8327
  const effectiveNewReadAutoScaling = newBilling === "PAY_PER_REQUEST" ? void 0 : newReadAutoScaling;
@@ -8439,10 +8440,49 @@ var DynamoDBGlobalTableProvider = class {
8439
8440
  await this.applyTagDiffOnClient(this.dynamoDBClient, tableArn, oldTagsRaw, newTagsRaw);
8440
8441
  }
8441
8442
  /**
8443
+ * Propagate a per-replica Tags diff to ONE cross-region replica via a
8444
+ * per-region client (Issue #389 / #441 — closes the create-side gap).
8445
+ * Centralizes the common shape used by BOTH `create()` (`oldTags`
8446
+ * undefined → every new tag is an add) and `update()` (per-replica
8447
+ * modify path's old-vs-new diff). Best-effort: a failure here logs at
8448
+ * WARN naming the offending region + ARN + reason and the deploy
8449
+ * continues — the cross-region Tags state will surface as drift on
8450
+ * the next run (or `cdkd drift --revert`) rather than aborting the
8451
+ * deploy mid-flight. Mirrors the autoscaling diff's failure contract
8452
+ * (PR #393).
8453
+ *
8454
+ * `tableArn` is the LOCAL replica's table ARN (returned by the
8455
+ * post-create `waitForTableActive` or the inline `DescribeTable` in
8456
+ * `update()`); the helper swaps the region segment via
8457
+ * `replicaArnForRegion` before issuing `TagResource` / `UntagResource`
8458
+ * against the per-region client.
8459
+ *
8460
+ * No-op when `oldTags` deep-equals `newTags` — the caller is allowed
8461
+ * to invoke unconditionally without first diffing.
8462
+ */
8463
+ async applyCrossRegionReplicaTagsDiff(tableArn, region, oldTags, newTags, physicalIdForLogs) {
8464
+ if (deepEqual$1(oldTags, newTags)) return;
8465
+ if (!tableArn) {
8466
+ this.logger.warn(`Local DescribeTable returned no TableArn — cannot propagate Tags to cross-region replica ${region} of ${physicalIdForLogs}`);
8467
+ return;
8468
+ }
8469
+ const replicaArn = this.replicaArnForRegion(tableArn, region);
8470
+ if (!replicaArn) {
8471
+ this.logger.warn(`Could not derive replica ARN for region ${region} from ${tableArn} — skipping Tags propagation for ${physicalIdForLogs}`);
8472
+ return;
8473
+ }
8474
+ try {
8475
+ const regionalClient = this.getRegionalClient(region);
8476
+ await this.applyTagDiffOnClient(regionalClient, replicaArn, oldTags, newTags);
8477
+ } catch (tagErr) {
8478
+ this.logger.warn(`Could not apply Tags diff to cross-region replica ${region} of ${physicalIdForLogs}: ${tagErr instanceof Error ? tagErr.message : String(tagErr)}. The replica's Tags state will surface as drift until the next successful deploy.`);
8479
+ }
8480
+ }
8481
+ /**
8442
8482
  * Apply a Tags diff against the given `DynamoDBClient` (which may be
8443
8483
  * the local client or a per-region client returned by
8444
8484
  * `getRegionalClient`). Used by the local-replica path AND the
8445
- * cross-region replica Tags propagation path (Issue #389).
8485
+ * cross-region replica Tags propagation path (Issue #389 / #441).
8446
8486
  */
8447
8487
  async applyTagDiffOnClient(client, tableArn, oldTagsRaw, newTagsRaw) {
8448
8488
  const toMap = (tags) => {
@@ -8732,9 +8772,12 @@ var DynamoDBGlobalTableProvider = class {
8732
8772
  *
8733
8773
  * Per-replica sub-specifications (`ContributorInsightsSpecification` /
8734
8774
  * `PointInTimeRecoverySpecification` / `KinesisStreamSpecification`)
8735
- * are surfaced only for the LOCAL replica. Cross-region replicas
8736
- * require per-region SDK clients (`new DynamoDBClient({region})`),
8737
- * deferred to a follow-up PR.
8775
+ * are surfaced for BOTH the LOCAL replica AND cross-region replicas
8776
+ * via per-region SDK clients cached in `regionalClientCache` (Issue
8777
+ * #389 lifted the v1 LOCAL-only limitation; the per-replica reads
8778
+ * happen in `readReplicaSubSpecs` below). Each cross-region call is
8779
+ * best-effort — a permissions gap in one region omits the offending
8780
+ * key rather than aborting the whole drift read.
8738
8781
  */
8739
8782
  async readCurrentState(physicalId, _logicalId, _resourceType) {
8740
8783
  try {
@@ -11212,7 +11255,7 @@ var EC2Provider = class {
11212
11255
  case "AWS::EC2::Instance": return this.updateInstance(logicalId, physicalId, resourceType, properties, previousProperties);
11213
11256
  case "AWS::EC2::NetworkAcl":
11214
11257
  case "AWS::EC2::NetworkAclEntry":
11215
- case "AWS::EC2::SubnetNetworkAclAssociation": throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "destroy + redeploy. The property surface for this resource type is effectively immutable in cdkd today.");
11258
+ case "AWS::EC2::SubnetNetworkAclAssociation": throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS provides no in-place Update API for this EC2 sub-resource type; every property change requires Delete + Create. Re-deploy with cdkd deploy --replace, or destroy + redeploy.");
11216
11259
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
11217
11260
  }
11218
11261
  }
@@ -13727,7 +13770,7 @@ var ApiGatewayProvider = class ApiGatewayProvider {
13727
13770
  * `ResourceUpdateNotSupportedError` instead of silently no-op'ing.
13728
13771
  */
13729
13772
  updateDeployment(logicalId, _physicalId, _resourceType) {
13730
- return Promise.reject(new ResourceUpdateNotSupportedError("AWS::ApiGateway::Deployment", logicalId, "API Gateway Deployment is immutable; re-deploy with cdkd deploy --replace, or change the resource definition to create a new Deployment"));
13773
+ return Promise.reject(new ResourceUpdateNotSupportedError("AWS::ApiGateway::Deployment", logicalId, "API Gateway Deployment is immutable on AWS — there is no UpdateDeployment API for the deployment itself (UpdateStage is for the stage that points at the deployment); every change requires CreateDeployment to produce a new immutable deployment. Re-deploy with cdkd deploy --replace, or change the resource definition to create a new Deployment."));
13731
13774
  }
13732
13775
  /**
13733
13776
  * Delete an API Gateway Deployment
@@ -14520,7 +14563,7 @@ var ApiGatewayV2Provider = class {
14520
14563
  case "AWS::ApiGatewayV2::Integration": return this.updateIntegration(logicalId, physicalId, resourceType, properties, previousProperties);
14521
14564
  case "AWS::ApiGatewayV2::Route": return this.updateRoute(logicalId, physicalId, resourceType, properties, previousProperties);
14522
14565
  case "AWS::ApiGatewayV2::Authorizer": return this.updateAuthorizer(logicalId, physicalId, resourceType, properties, previousProperties);
14523
- default: throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "unsupported API Gateway V2 resource type for in-place update; re-deploy with cdkd deploy --replace, or destroy + redeploy the stack");
14566
+ default: throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "Unsupported API Gateway V2 resource type for in-place update in cdkd; re-deploy with cdkd deploy --replace, or destroy + redeploy the stack.");
14524
14567
  }
14525
14568
  }
14526
14569
  async delete(logicalId, physicalId, resourceType, properties, context) {
@@ -16617,7 +16660,7 @@ var ECSProvider = class {
16617
16660
  }
16618
16661
  }
16619
16662
  async updateTaskDefinition(logicalId, _physicalId, _resourceType, _properties) {
16620
- return Promise.reject(new ResourceUpdateNotSupportedError("AWS::ECS::TaskDefinition", logicalId, "TaskDefinition revisions are immutable; re-deploy with cdkd deploy --replace, or destroy + redeploy the stack"));
16663
+ return Promise.reject(new ResourceUpdateNotSupportedError("AWS::ECS::TaskDefinition", logicalId, "ECS TaskDefinition revisions are immutable on AWS — there is no UpdateTaskDefinition API; every change registers a new revision via RegisterTaskDefinition. Re-deploy with cdkd deploy --replace, or destroy + redeploy the stack."));
16621
16664
  }
16622
16665
  async deleteTaskDefinition(logicalId, physicalId, resourceType, context) {
16623
16666
  this.logger.debug(`Deleting ECS task definition ${logicalId}: ${physicalId}`);
@@ -17398,7 +17441,7 @@ var ELBv2Provider = class {
17398
17441
  for (const [k, v] of Object.entries(p)) if (!handledKeys.has(k)) out[k] = v;
17399
17442
  return out;
17400
17443
  };
17401
- if (JSON.stringify(stripHandled(properties)) !== JSON.stringify(stripHandled(previousProperties))) throw new ResourceUpdateNotSupportedError("AWS::ElasticLoadBalancingV2::LoadBalancer", logicalId, "ELBv2 LoadBalancer in-place updates are supported for LoadBalancerAttributes / Subnets / SubnetMappings / SecurityGroups / IpAddressType / Tags only; for Name / Type / Scheme, re-deploy with cdkd deploy --replace, or destroy + redeploy the stack");
17444
+ if (JSON.stringify(stripHandled(properties)) !== JSON.stringify(stripHandled(previousProperties))) throw new ResourceUpdateNotSupportedError("AWS::ElasticLoadBalancingV2::LoadBalancer", logicalId, "ELBv2 LoadBalancer Name / Type / Scheme are immutable on AWS — none of the ELBv2 Modify* / Set* APIs accept these fields; they are fixed at creation. cdkd handles LoadBalancerAttributes / Subnets / SubnetMappings / SecurityGroups / IpAddressType / Tags in-place; for Name / Type / Scheme re-deploy with cdkd deploy --replace, or destroy + redeploy the stack.");
17402
17445
  const newAttrs = properties["LoadBalancerAttributes"] ?? [];
17403
17446
  const oldAttrs = previousProperties["LoadBalancerAttributes"] ?? [];
17404
17447
  const newAttrMap = new Map(newAttrs.map((a) => [a.Key, a.Value]));
@@ -23242,6 +23285,20 @@ var AppSyncProvider = class {
23242
23285
  client;
23243
23286
  providerRegion = process.env["AWS_REGION"];
23244
23287
  logger = getLogger().child("AppSyncProvider");
23288
+ /**
23289
+ * Cache of `apiId -> GraphqlApi ARN` for the lifetime of this provider
23290
+ * instance. Populated lazily by `applyTagDiff` and reused on subsequent
23291
+ * tag-diff updates against the same API so we don't pay an extra
23292
+ * `GetGraphqlApi` round-trip per call. Mirrors the existing
23293
+ * `attributeCache` pattern used elsewhere in this provider family.
23294
+ *
23295
+ * Invalidation: the ARN of a GraphqlApi is stable for the life of the
23296
+ * API (it embeds the apiId), so the cache never needs to be invalidated
23297
+ * within a process — the only way the ARN changes is if the API itself
23298
+ * is replaced, in which case a new `physicalId` flows through `update()`
23299
+ * and the old entry simply becomes unreachable.
23300
+ */
23301
+ arnCache = /* @__PURE__ */ new Map();
23245
23302
  handledProperties = new Map([
23246
23303
  ["AWS::AppSync::GraphQLApi", new Set([
23247
23304
  "Name",
@@ -23298,15 +23355,307 @@ var AppSyncProvider = class {
23298
23355
  }
23299
23356
  }
23300
23357
  /**
23301
- * AppSync resources are treated as immutable by cdkd: every supported
23302
- * type (`GraphQLApi`, `GraphQLSchema`, `DataSource`, `Resolver`,
23303
- * `ApiKey`) is recreated on property changes via the deploy engine's
23304
- * immutable-property replacement path. There is no in-place update,
23305
- * so `cdkd drift --revert` surfaces a clear "use --replace or
23306
- * re-deploy" message instead of silently no-op'ing the revert.
23358
+ * Update an AppSync resource in-place via the SDK's `Update*` calls.
23359
+ *
23360
+ * Per-type API path:
23361
+ * - `GraphQLApi` → `UpdateGraphqlApiCommand` (`AuthenticationType` /
23362
+ * `XrayEnabled` / `LogConfig`) + `TagResource` / `UntagResource`
23363
+ * for `Tags` diff. `Name` is immutable on AWS.
23364
+ * - `DataSource` → `UpdateDataSourceCommand` (`Description` /
23365
+ * `ServiceRoleArn` / `DynamoDBConfig` / `LambdaConfig` / `HttpConfig`).
23366
+ * `ApiId` / `Name` / `Type` are immutable identity fields.
23367
+ * - `Resolver` → `UpdateResolverCommand` (`DataSourceName` /
23368
+ * `RequestMappingTemplate` / `ResponseMappingTemplate` / `Kind` /
23369
+ * `PipelineConfig` / `Runtime` / `Code`). `ApiId` / `TypeName` /
23370
+ * `FieldName` are immutable identity fields.
23371
+ * - `ApiKey` → `UpdateApiKeyCommand` (`Description` / `Expires`).
23372
+ * `ApiId` is immutable; the AWS-generated key id is immutable.
23373
+ * - `GraphQLSchema` → `StartSchemaCreationCommand` (re-upload the
23374
+ * SDL; this is the canonical AppSync schema-update path).
23375
+ *
23376
+ * Every Update* call uses `!== undefined` field gates per
23377
+ * memory rule `feedback_update_optional_field_undefined_check.md` so
23378
+ * `cdkd drift --revert` can clear a console-side ADD via an empty
23379
+ * string / 0 / false. Identity / immutable field changes throw
23380
+ * `ResourceUpdateNotSupportedError` as defense-in-depth — the deploy
23381
+ * engine's replacement-detection layer should normally route those
23382
+ * through CREATE+DELETE.
23307
23383
  */
23308
- update(logicalId, _physicalId, resourceType, _properties, _previousProperties) {
23309
- return Promise.reject(new ResourceUpdateNotSupportedError(resourceType, logicalId, "AppSync resources are recreated on property changes; re-deploy with cdkd deploy --replace, or destroy + redeploy the stack"));
23384
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
23385
+ switch (resourceType) {
23386
+ case "AWS::AppSync::GraphQLApi": return this.updateGraphQLApi(logicalId, physicalId, resourceType, properties, previousProperties);
23387
+ case "AWS::AppSync::GraphQLSchema": return this.updateGraphQLSchema(logicalId, physicalId, resourceType, properties, previousProperties);
23388
+ case "AWS::AppSync::DataSource": return this.updateDataSource(logicalId, physicalId, resourceType, properties, previousProperties);
23389
+ case "AWS::AppSync::Resolver": return this.updateResolver(logicalId, physicalId, resourceType, properties, previousProperties);
23390
+ case "AWS::AppSync::ApiKey": return this.updateApiKey(logicalId, physicalId, resourceType, properties, previousProperties);
23391
+ default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
23392
+ }
23393
+ }
23394
+ /**
23395
+ * Structural equality for the small object / array shapes that ride on
23396
+ * AppSync update inputs. `JSON.stringify` is sufficient because none of
23397
+ * these shapes contain `undefined` keys at this layer (the create /
23398
+ * readCurrentState paths filter them out).
23399
+ */
23400
+ deepEqual(a, b) {
23401
+ return JSON.stringify(a) === JSON.stringify(b);
23402
+ }
23403
+ async updateGraphQLApi(logicalId, physicalId, resourceType, properties, previousProperties) {
23404
+ if (properties["Name"] !== void 0 && previousProperties["Name"] !== void 0 && properties["Name"] !== previousProperties["Name"]) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS AppSync GraphqlApi.Name is immutable — destroy + redeploy to rename");
23405
+ const newAuthType = properties["AuthenticationType"];
23406
+ const oldAuthType = previousProperties["AuthenticationType"];
23407
+ const newXray = properties["XrayEnabled"];
23408
+ const oldXray = previousProperties["XrayEnabled"];
23409
+ const newLog = properties["LogConfig"];
23410
+ const oldLog = previousProperties["LogConfig"];
23411
+ const hasXrayDiff = ("XrayEnabled" in properties || "XrayEnabled" in previousProperties) && newXray !== oldXray;
23412
+ const hasAuthDiff = ("AuthenticationType" in properties || "AuthenticationType" in previousProperties) && newAuthType !== oldAuthType;
23413
+ const hasLogDiff = ("LogConfig" in properties || "LogConfig" in previousProperties) && !this.deepEqual(newLog, oldLog);
23414
+ if (hasAuthDiff || hasXrayDiff || hasLogDiff) {
23415
+ const input = {
23416
+ apiId: physicalId,
23417
+ name: properties["Name"] ?? previousProperties["Name"],
23418
+ authenticationType: newAuthType ?? oldAuthType ?? "API_KEY"
23419
+ };
23420
+ if (newXray !== void 0) input.xrayEnabled = newXray;
23421
+ if (newLog !== void 0) input.logConfig = {
23422
+ cloudWatchLogsRoleArn: newLog["CloudWatchLogsRoleArn"],
23423
+ fieldLogLevel: newLog["FieldLogLevel"],
23424
+ excludeVerboseContent: newLog["ExcludeVerboseContent"]
23425
+ };
23426
+ else if (oldLog !== void 0) {
23427
+ const existingRoleArn = oldLog["CloudWatchLogsRoleArn"];
23428
+ if (existingRoleArn) input.logConfig = {
23429
+ cloudWatchLogsRoleArn: existingRoleArn,
23430
+ fieldLogLevel: "NONE"
23431
+ };
23432
+ else this.logger.warn(`AppSync GraphqlApi ${logicalId}: cannot clear LogConfig — previous state has no CloudWatchLogsRoleArn to reuse for the disable call`);
23433
+ }
23434
+ try {
23435
+ await this.getClient().send(new UpdateGraphqlApiCommand(input));
23436
+ } catch (error) {
23437
+ throw this.wrapUpdateError(error, resourceType, logicalId, physicalId, "GraphqlApi");
23438
+ }
23439
+ }
23440
+ await this.applyTagDiff(physicalId, resourceType, logicalId, previousProperties["Tags"], properties["Tags"]);
23441
+ return {
23442
+ physicalId,
23443
+ wasReplaced: false,
23444
+ attributes: {}
23445
+ };
23446
+ }
23447
+ async updateGraphQLSchema(logicalId, physicalId, resourceType, properties, previousProperties) {
23448
+ const newDef = properties["Definition"];
23449
+ const oldDef = previousProperties["Definition"];
23450
+ if (newDef === void 0 || newDef === oldDef) return {
23451
+ physicalId,
23452
+ wasReplaced: false,
23453
+ attributes: {}
23454
+ };
23455
+ const apiId = properties["ApiId"] ?? physicalId;
23456
+ try {
23457
+ await this.getClient().send(new StartSchemaCreationCommand({
23458
+ apiId,
23459
+ definition: Buffer.from(newDef, "utf-8")
23460
+ }));
23461
+ } catch (error) {
23462
+ throw this.wrapUpdateError(error, resourceType, logicalId, physicalId, "GraphqlSchema");
23463
+ }
23464
+ return {
23465
+ physicalId,
23466
+ wasReplaced: false,
23467
+ attributes: {}
23468
+ };
23469
+ }
23470
+ async updateDataSource(logicalId, physicalId, resourceType, properties, previousProperties) {
23471
+ for (const field of [
23472
+ "ApiId",
23473
+ "Name",
23474
+ "Type"
23475
+ ]) {
23476
+ const next = properties[field];
23477
+ const prev = previousProperties[field];
23478
+ if (next !== void 0 && prev !== void 0 && next !== prev) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS AppSync DataSource.${field} is immutable — destroy + redeploy to change`);
23479
+ }
23480
+ const [apiId, name] = physicalId.split("|");
23481
+ if (!apiId || !name) throw new ProvisioningError(`Invalid DataSource physical ID format: ${physicalId}`, resourceType, logicalId, physicalId);
23482
+ const type = properties["Type"] ?? previousProperties["Type"];
23483
+ const newDesc = properties["Description"];
23484
+ const oldDesc = previousProperties["Description"];
23485
+ const newRole = properties["ServiceRoleArn"];
23486
+ const oldRole = previousProperties["ServiceRoleArn"];
23487
+ const newDDB = properties["DynamoDBConfig"];
23488
+ const oldDDB = previousProperties["DynamoDBConfig"];
23489
+ const newLambda = properties["LambdaConfig"];
23490
+ const oldLambda = previousProperties["LambdaConfig"];
23491
+ const newHttp = properties["HttpConfig"];
23492
+ const oldHttp = previousProperties["HttpConfig"];
23493
+ if (!(newDesc !== oldDesc || newRole !== oldRole || !this.deepEqual(newDDB, oldDDB) || !this.deepEqual(newLambda, oldLambda) || !this.deepEqual(newHttp, oldHttp))) return {
23494
+ physicalId,
23495
+ wasReplaced: false,
23496
+ attributes: {}
23497
+ };
23498
+ const input = {
23499
+ apiId,
23500
+ name,
23501
+ type
23502
+ };
23503
+ if (newDesc !== void 0) input.description = newDesc;
23504
+ if (newRole !== void 0) input.serviceRoleArn = newRole;
23505
+ if (newDDB !== void 0) input.dynamodbConfig = {
23506
+ tableName: newDDB["TableName"],
23507
+ awsRegion: newDDB["AwsRegion"],
23508
+ useCallerCredentials: newDDB["UseCallerCredentials"]
23509
+ };
23510
+ if (newLambda !== void 0) input.lambdaConfig = { lambdaFunctionArn: newLambda["LambdaFunctionArn"] };
23511
+ if (newHttp !== void 0) input.httpConfig = { endpoint: newHttp["Endpoint"] };
23512
+ try {
23513
+ await this.getClient().send(new UpdateDataSourceCommand(input));
23514
+ } catch (error) {
23515
+ throw this.wrapUpdateError(error, resourceType, logicalId, physicalId, "DataSource");
23516
+ }
23517
+ return {
23518
+ physicalId,
23519
+ wasReplaced: false,
23520
+ attributes: {}
23521
+ };
23522
+ }
23523
+ async updateResolver(logicalId, physicalId, resourceType, properties, previousProperties) {
23524
+ for (const field of [
23525
+ "ApiId",
23526
+ "TypeName",
23527
+ "FieldName"
23528
+ ]) {
23529
+ const next = properties[field];
23530
+ const prev = previousProperties[field];
23531
+ if (next !== void 0 && prev !== void 0 && next !== prev) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS AppSync Resolver.${field} is immutable — destroy + redeploy to change`);
23532
+ }
23533
+ const parts = physicalId.split("|");
23534
+ if (parts.length < 3) throw new ProvisioningError(`Invalid Resolver physical ID format: ${physicalId}`, resourceType, logicalId, physicalId);
23535
+ const [apiId, typeName, fieldName] = parts;
23536
+ if (![
23537
+ "DataSourceName",
23538
+ "RequestMappingTemplate",
23539
+ "ResponseMappingTemplate",
23540
+ "Kind",
23541
+ "PipelineConfig",
23542
+ "Runtime",
23543
+ "Code"
23544
+ ].some((key) => !this.deepEqual(properties[key], previousProperties[key]))) return {
23545
+ physicalId,
23546
+ wasReplaced: false,
23547
+ attributes: {}
23548
+ };
23549
+ const input = {
23550
+ apiId,
23551
+ typeName,
23552
+ fieldName
23553
+ };
23554
+ const effectiveKind = properties["Kind"] ?? previousProperties["Kind"] ?? "UNIT";
23555
+ if (effectiveKind === "UNIT" && properties["DataSourceName"] !== void 0) input.dataSourceName = properties["DataSourceName"];
23556
+ if (properties["RequestMappingTemplate"] !== void 0) input.requestMappingTemplate = properties["RequestMappingTemplate"];
23557
+ if (properties["ResponseMappingTemplate"] !== void 0) input.responseMappingTemplate = properties["ResponseMappingTemplate"];
23558
+ if (properties["Kind"] !== void 0) input.kind = properties["Kind"];
23559
+ if (effectiveKind === "PIPELINE" && properties["PipelineConfig"] !== void 0) input.pipelineConfig = { functions: properties["PipelineConfig"]["Functions"] };
23560
+ if (properties["Runtime"] !== void 0) {
23561
+ const runtime = properties["Runtime"];
23562
+ input.runtime = {
23563
+ name: runtime["Name"],
23564
+ runtimeVersion: runtime["RuntimeVersion"]
23565
+ };
23566
+ }
23567
+ if (properties["Code"] !== void 0) input.code = properties["Code"];
23568
+ try {
23569
+ await this.getClient().send(new UpdateResolverCommand(input));
23570
+ } catch (error) {
23571
+ throw this.wrapUpdateError(error, resourceType, logicalId, physicalId, "Resolver");
23572
+ }
23573
+ return {
23574
+ physicalId,
23575
+ wasReplaced: false,
23576
+ attributes: {}
23577
+ };
23578
+ }
23579
+ async updateApiKey(logicalId, physicalId, resourceType, properties, previousProperties) {
23580
+ 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");
23581
+ const [apiId, apiKeyId] = physicalId.split("|");
23582
+ if (!apiId || !apiKeyId) throw new ProvisioningError(`Invalid ApiKey physical ID format: ${physicalId}`, resourceType, logicalId, physicalId);
23583
+ const newDesc = properties["Description"];
23584
+ const oldDesc = previousProperties["Description"];
23585
+ const newExp = properties["Expires"];
23586
+ const oldExp = previousProperties["Expires"];
23587
+ if (newDesc === oldDesc && newExp === oldExp) return {
23588
+ physicalId,
23589
+ wasReplaced: false,
23590
+ attributes: {}
23591
+ };
23592
+ const input = {
23593
+ apiId,
23594
+ id: apiKeyId
23595
+ };
23596
+ if (newDesc !== void 0) input.description = newDesc;
23597
+ if (newExp !== void 0) input.expires = newExp;
23598
+ try {
23599
+ await this.getClient().send(new UpdateApiKeyCommand(input));
23600
+ } catch (error) {
23601
+ throw this.wrapUpdateError(error, resourceType, logicalId, physicalId, "ApiKey");
23602
+ }
23603
+ return {
23604
+ physicalId,
23605
+ wasReplaced: false,
23606
+ attributes: {}
23607
+ };
23608
+ }
23609
+ /**
23610
+ * Apply a Tags diff to a GraphqlApi via TagResource / UntagResource.
23611
+ *
23612
+ * Tags are keyed by the GraphqlApi ARN — recover it from
23613
+ * `GetGraphqlApi`. Failure to recover the ARN is a hard error (the API
23614
+ * itself just changed) rather than a silent drop, so the user knows
23615
+ * the tag diff was not applied.
23616
+ */
23617
+ async applyTagDiff(apiId, resourceType, logicalId, oldTags, newTags) {
23618
+ const oldMap = this.tagsToMap(oldTags ?? []);
23619
+ const newMap = this.tagsToMap(newTags ?? []);
23620
+ if (this.deepEqual(oldMap, newMap)) return;
23621
+ let arn = this.arnCache.get(apiId);
23622
+ if (!arn) {
23623
+ try {
23624
+ arn = (await this.getClient().send(new GetGraphqlApiCommand({ apiId }))).graphqlApi?.arn;
23625
+ } catch (error) {
23626
+ throw this.wrapUpdateError(error, resourceType, logicalId, apiId, "GraphqlApi");
23627
+ }
23628
+ if (!arn) throw new ProvisioningError(`Could not resolve ARN for AppSync GraphqlApi ${apiId} to apply tags diff`, resourceType, logicalId, apiId);
23629
+ this.arnCache.set(apiId, arn);
23630
+ }
23631
+ const tagKeysToRemove = Object.keys(oldMap).filter((k) => !(k in newMap));
23632
+ const tagsToAdd = {};
23633
+ for (const [k, v] of Object.entries(newMap)) if (oldMap[k] !== v) tagsToAdd[k] = v;
23634
+ if (tagKeysToRemove.length > 0) try {
23635
+ await this.getClient().send(new UntagResourceCommand$12({
23636
+ resourceArn: arn,
23637
+ tagKeys: tagKeysToRemove
23638
+ }));
23639
+ } catch (error) {
23640
+ throw this.wrapUpdateError(error, resourceType, logicalId, apiId, "GraphqlApi (untag)");
23641
+ }
23642
+ if (Object.keys(tagsToAdd).length > 0) try {
23643
+ await this.getClient().send(new TagResourceCommand$13({
23644
+ resourceArn: arn,
23645
+ tags: tagsToAdd
23646
+ }));
23647
+ } catch (error) {
23648
+ throw this.wrapUpdateError(error, resourceType, logicalId, apiId, "GraphqlApi (tag)");
23649
+ }
23650
+ }
23651
+ tagsToMap(tags) {
23652
+ const out = {};
23653
+ for (const t of tags) if (t.Key !== void 0 && t.Value !== void 0) out[t.Key] = t.Value;
23654
+ return out;
23655
+ }
23656
+ wrapUpdateError(error, resourceType, logicalId, physicalId, subType) {
23657
+ const cause = error instanceof Error ? error : void 0;
23658
+ return new ProvisioningError(`Failed to update AppSync ${subType} ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
23310
23659
  }
23311
23660
  async delete(logicalId, physicalId, resourceType, _properties, context) {
23312
23661
  switch (resourceType) {
@@ -24528,7 +24877,7 @@ var GlueSecurityConfigurationProvider = class {
24528
24877
  }
24529
24878
  }
24530
24879
  async update(logicalId, _physicalId, resourceType, _properties, _previousProperties) {
24531
- throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS::Glue::SecurityConfiguration is immutable; AWS provides no Update API. Use cdkd deploy --replace, or destroy + redeploy with the new EncryptionConfiguration.");
24880
+ throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS Glue SecurityConfiguration is immutable on AWS there is no UpdateSecurityConfiguration API; every change requires DeleteSecurityConfiguration + CreateSecurityConfiguration. Use cdkd deploy --replace, or destroy + redeploy with the new EncryptionConfiguration.");
24532
24881
  }
24533
24882
  async delete(logicalId, physicalId, resourceType, _properties, context) {
24534
24883
  this.logger.debug(`Deleting Glue SecurityConfiguration ${logicalId}: ${physicalId}`);
@@ -26393,7 +26742,7 @@ var KinesisStreamConsumerProvider = class {
26393
26742
  const oldConsumerName = previousProperties["ConsumerName"];
26394
26743
  const newStreamArn = properties["StreamARN"];
26395
26744
  const oldStreamArn = previousProperties["StreamARN"];
26396
- if (newConsumerName !== oldConsumerName || newStreamArn !== oldStreamArn) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS::Kinesis::StreamConsumer ConsumerName / StreamARN are immutable; re-deploy with cdkd deploy --replace, or destroy + redeploy");
26745
+ if (newConsumerName !== oldConsumerName || newStreamArn !== oldStreamArn) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS::Kinesis::StreamConsumer ConsumerName / StreamARN are immutable on AWS — there is no UpdateStreamConsumer API; every change registers a new consumer (RegisterStreamConsumer). Re-deploy with cdkd deploy --replace, or destroy + redeploy.");
26397
26746
  await this.applyTagDiff(physicalId, previousProperties["Tags"], properties["Tags"]);
26398
26747
  let attrs = {};
26399
26748
  try {
@@ -26508,14 +26857,14 @@ var KinesisStreamConsumerProvider = class {
26508
26857
  const tagsToRemove = [];
26509
26858
  for (const k of Object.keys(oldMap)) if (!(k in newMap)) tagsToRemove.push(k);
26510
26859
  if (tagsToRemove.length > 0) {
26511
- await this.getClient().send(new UntagResourceCommand$12({
26860
+ await this.getClient().send(new UntagResourceCommand$13({
26512
26861
  ResourceARN: consumerArn,
26513
26862
  TagKeys: tagsToRemove
26514
26863
  }));
26515
26864
  this.logger.debug(`Removed ${tagsToRemove.length} tag(s) from Kinesis stream consumer ${consumerArn}`);
26516
26865
  }
26517
26866
  if (Object.keys(tagsToAdd).length > 0) {
26518
- await this.getClient().send(new TagResourceCommand$13({
26867
+ await this.getClient().send(new TagResourceCommand$14({
26519
26868
  ResourceARN: consumerArn,
26520
26869
  Tags: tagsToAdd
26521
26870
  }));
@@ -26623,7 +26972,7 @@ var EFSProvider = class {
26623
26972
  switch (resourceType) {
26624
26973
  case "AWS::EFS::FileSystem": return this.updateFileSystem(logicalId, physicalId, resourceType, properties, previousProperties);
26625
26974
  case "AWS::EFS::MountTarget": return this.updateMountTarget(logicalId, physicalId, resourceType, properties);
26626
- case "AWS::EFS::AccessPoint": return Promise.reject(new ResourceUpdateNotSupportedError(resourceType, logicalId, "EFS AccessPoint is recreated on property changes; re-deploy with cdkd deploy --replace, or destroy + redeploy the stack"));
26975
+ case "AWS::EFS::AccessPoint": return Promise.reject(new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS EFS AccessPoint has no in-place update API — there is no UpdateAccessPoint command; every property change requires DeleteAccessPoint + CreateAccessPoint. Re-deploy with cdkd deploy --replace, or destroy + redeploy the stack."));
26627
26976
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
26628
26977
  }
26629
26978
  }
@@ -26635,7 +26984,7 @@ var EFSProvider = class {
26635
26984
  ]) {
26636
26985
  const next = properties[key];
26637
26986
  const prev = previousProperties[key];
26638
- if (next !== void 0 && prev !== void 0 && JSON.stringify(next) !== JSON.stringify(prev)) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `EFS FileSystem ${key} is immutable; re-deploy with cdkd deploy --replace, or destroy + redeploy the stack`);
26987
+ if (next !== void 0 && prev !== void 0 && JSON.stringify(next) !== JSON.stringify(prev)) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS EFS FileSystem ${key} is immutable on AWS — UpdateFileSystem does not accept ${key}; the property is fixed at creation. Re-deploy with cdkd deploy --replace, or destroy + redeploy the stack.`);
26639
26988
  }
26640
26989
  const newThroughputMode = properties["ThroughputMode"];
26641
26990
  const newProvisioned = properties["ProvisionedThroughputInMibps"];
@@ -27276,15 +27625,19 @@ var FirehoseProvider = class {
27276
27625
  }
27277
27626
  }
27278
27627
  /**
27279
- * Firehose delivery streams are treated as immutable by cdkd. Most
27280
- * destination-config changes require replacement, and AWS's
27281
- * `UpdateDestination` API surface is deep enough that the deploy engine's
27282
- * immutable-property replacement path covers the common cases more
27283
- * reliably. `cdkd drift --revert` therefore surfaces a clear "use
27284
- * --replace or re-deploy" message instead of silently no-op'ing.
27628
+ * Firehose delivery streams are treated as immutable by cdkd. AWS DOES
27629
+ * provide an `UpdateDestination` API, but the per-destination shape
27630
+ * matrix (Extended S3 / Redshift / OpenSearch / Splunk / HttpEndpoint /
27631
+ * Iceberg / etc.) is deep enough that the deploy engine's immutable-
27632
+ * property replacement path covers the common cases more reliably.
27633
+ * Treating the type as fully immutable for `cdkd drift --revert` is
27634
+ * the conservative choice; users who want in-place destination updates
27635
+ * should re-deploy with `cdkd deploy --replace` so the new shape is
27636
+ * applied via a fresh `CreateDeliveryStream`. Tracked as a follow-up
27637
+ * to issue (#443).
27285
27638
  */
27286
27639
  update(logicalId, _physicalId, resourceType, _properties, _previousProperties) {
27287
- return Promise.reject(new ResourceUpdateNotSupportedError(resourceType, logicalId, "Firehose delivery streams are recreated on property changes; re-deploy with cdkd deploy --replace, or destroy + redeploy the stack"));
27640
+ return Promise.reject(new ResourceUpdateNotSupportedError(resourceType, logicalId, "AWS::KinesisFirehose::DeliveryStream in-place update is not implemented in cdkd; AWS provides UpdateDestination but the per-destination shape matrix is large. Re-deploy with cdkd deploy --replace, or destroy + redeploy the stack."));
27288
27641
  }
27289
27642
  /**
27290
27643
  * Delete a Firehose delivery stream
@@ -29787,10 +30140,10 @@ var ASGProvider = class {
29787
30140
  if (resourceType !== "AWS::AutoScaling::AutoScalingGroup") throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
29788
30141
  this.logger.debug(`Updating AutoScalingGroup ${logicalId}: ${physicalId}`);
29789
30142
  const stringEq = (a, b) => JSON.stringify(a) === JSON.stringify(b);
29790
- if (!stringEq(properties["AutoScalingGroupName"], previousProperties["AutoScalingGroupName"])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AutoScalingGroupName is immutable; use cdkd deploy --replace to replace the group");
29791
- if (!stringEq(properties["Tags"] ?? [], previousProperties["Tags"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "Tags updates on AWS::AutoScaling::AutoScalingGroup are not yet supported by cdkd; use cdkd deploy --replace, or update the tags via AWS console / CLI");
29792
- if (!stringEq(properties["LoadBalancerNames"] ?? [], previousProperties["LoadBalancerNames"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "LoadBalancerNames diffs require Attach/Detach calls; use cdkd deploy --replace");
29793
- if (!stringEq(properties["TargetGroupARNs"] ?? [], previousProperties["TargetGroupARNs"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "TargetGroupARNs diffs require Attach/Detach calls; use cdkd deploy --replace");
30143
+ if (!stringEq(properties["AutoScalingGroupName"], previousProperties["AutoScalingGroupName"])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AutoScalingGroupName is immutable on AWS — UpdateAutoScalingGroup does not accept a new name; the name is fixed at creation. Use cdkd deploy --replace to replace the group.");
30144
+ if (!stringEq(properties["Tags"] ?? [], previousProperties["Tags"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "Tags updates on AWS::AutoScaling::AutoScalingGroup are not yet implemented in cdkd (AWS exposes CreateOrUpdateTags / DeleteTags); use cdkd deploy --replace, or update the tags via AWS console / CLI.");
30145
+ if (!stringEq(properties["LoadBalancerNames"] ?? [], previousProperties["LoadBalancerNames"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "LoadBalancerNames diffs on AWS::AutoScaling::AutoScalingGroup are not yet implemented in cdkd (AWS exposes AttachLoadBalancers / DetachLoadBalancers); use cdkd deploy --replace.");
30146
+ if (!stringEq(properties["TargetGroupARNs"] ?? [], previousProperties["TargetGroupARNs"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "TargetGroupARNs diffs on AWS::AutoScaling::AutoScalingGroup are not yet implemented in cdkd (AWS exposes AttachLoadBalancerTargetGroups / DetachLoadBalancerTargetGroups); use cdkd deploy --replace.");
29794
30147
  try {
29795
30148
  await this.applyMetricsCollectionDiff(physicalId, properties["MetricsCollection"], previousProperties["MetricsCollection"]);
29796
30149
  await this.applyLifecycleHooksDiff(physicalId, properties["LifecycleHookSpecificationList"], previousProperties["LifecycleHookSpecificationList"]);
@@ -45721,7 +46074,7 @@ function reorderArgs(argv) {
45721
46074
  */
45722
46075
  async function main() {
45723
46076
  const program = new Command();
45724
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.117.0");
46077
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.117.2");
45725
46078
  program.addCommand(createBootstrapCommand());
45726
46079
  program.addCommand(createSynthCommand());
45727
46080
  program.addCommand(createListCommand());