@go-to-k/cdkd 0.59.0 → 0.60.0

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
@@ -653,7 +653,7 @@ var destroyOptions = [
653
653
  ),
654
654
  new Option(
655
655
  "--remove-protection",
656
- "Bypass deletion protection on protected resources by flipping the per-resource protection flag off in-place before delete. Covers stack-level terminationProtection (CDK property) and resource-level protection on AWS::Logs::LogGroup, AWS::RDS::DBInstance, AWS::RDS::DBCluster, AWS::DynamoDB::Table, AWS::EC2::Instance, and AWS::ElasticLoadBalancingV2::LoadBalancer."
656
+ "Bypass deletion protection on protected resources by flipping the per-resource protection flag off in-place before delete. Covers stack-level terminationProtection (CDK property) and resource-level protection on AWS::Logs::LogGroup, AWS::RDS::DBInstance, AWS::RDS::DBCluster, AWS::DocDB::DBCluster, AWS::Neptune::DBCluster, AWS::Neptune::DBInstance, AWS::DynamoDB::Table, AWS::EC2::Instance, AWS::Cognito::UserPool, AWS::AutoScaling::AutoScalingGroup, and AWS::ElasticLoadBalancingV2::LoadBalancer."
657
657
  ).default(false)
658
658
  ];
659
659
 
@@ -738,6 +738,32 @@ var FALLBACK_NAME_RULES = {
738
738
  nameProperty: "DBInstanceIdentifier",
739
739
  options: { maxLength: 63, lowercase: true }
740
740
  },
741
+ // DocumentDB — RDS-shaped API; same name constraints.
742
+ "AWS::DocDB::DBSubnetGroup": {
743
+ nameProperty: "DBSubnetGroupName",
744
+ options: { maxLength: 255, lowercase: true }
745
+ },
746
+ "AWS::DocDB::DBCluster": {
747
+ nameProperty: "DBClusterIdentifier",
748
+ options: { maxLength: 63, lowercase: true }
749
+ },
750
+ "AWS::DocDB::DBInstance": {
751
+ nameProperty: "DBInstanceIdentifier",
752
+ options: { maxLength: 63, lowercase: true }
753
+ },
754
+ // Neptune — RDS-shaped API; same name constraints.
755
+ "AWS::Neptune::DBSubnetGroup": {
756
+ nameProperty: "DBSubnetGroupName",
757
+ options: { maxLength: 255, lowercase: true }
758
+ },
759
+ "AWS::Neptune::DBCluster": {
760
+ nameProperty: "DBClusterIdentifier",
761
+ options: { maxLength: 63, lowercase: true }
762
+ },
763
+ "AWS::Neptune::DBInstance": {
764
+ nameProperty: "DBInstanceIdentifier",
765
+ options: { maxLength: 63, lowercase: true }
766
+ },
741
767
  "AWS::ElasticLoadBalancingV2::LoadBalancer": {
742
768
  nameProperty: "Name",
743
769
  options: { maxLength: 32 }
@@ -5713,7 +5739,10 @@ import { GetFunctionUrlConfigCommand } from "@aws-sdk/client-lambda";
5713
5739
 
5714
5740
  // src/deployment/intrinsic-function-resolver.ts
5715
5741
  import { GetCallerIdentityCommand as GetCallerIdentityCommand3 } from "@aws-sdk/client-sts";
5716
- import { DescribeAvailabilityZonesCommand as DescribeAvailabilityZonesCommand2 } from "@aws-sdk/client-ec2";
5742
+ import {
5743
+ DescribeAvailabilityZonesCommand as DescribeAvailabilityZonesCommand2,
5744
+ DescribeLaunchTemplatesCommand
5745
+ } from "@aws-sdk/client-ec2";
5717
5746
  import { GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
5718
5747
  import { GetParameterCommand as GetParameterCommand2 } from "@aws-sdk/client-ssm";
5719
5748
  init_aws_clients();
@@ -6189,6 +6218,27 @@ var IntrinsicFunctionResolver = class {
6189
6218
  return physicalId;
6190
6219
  }
6191
6220
  }
6221
+ if (resourceType === "AWS::EC2::LaunchTemplate") {
6222
+ if (attributeName === "LatestVersionNumber" || attributeName === "DefaultVersionNumber") {
6223
+ try {
6224
+ const clients = getAwsClients();
6225
+ const response = await clients.ec2.send(
6226
+ new DescribeLaunchTemplatesCommand({ LaunchTemplateIds: [physicalId] })
6227
+ );
6228
+ const lt = response.LaunchTemplates?.[0];
6229
+ const value = attributeName === "LatestVersionNumber" ? lt?.LatestVersionNumber : lt?.DefaultVersionNumber;
6230
+ if (value !== void 0 && value !== null) {
6231
+ return String(value);
6232
+ }
6233
+ } catch (err) {
6234
+ this.logger.warn(
6235
+ `DescribeLaunchTemplates(${physicalId}) failed for ${attributeName}: ${err instanceof Error ? err.message : String(err)}`
6236
+ );
6237
+ }
6238
+ return attributeName === "LatestVersionNumber" ? "$Latest" : "$Default";
6239
+ }
6240
+ return physicalId;
6241
+ }
6192
6242
  this.logger.warn(
6193
6243
  `Unknown attribute ${attributeName} for resource type ${resourceType}, returning physical ID`
6194
6244
  );
@@ -29995,70 +30045,89 @@ var RDSProvider = class {
29995
30045
  }
29996
30046
  };
29997
30047
 
29998
- // src/provisioning/providers/route53-provider.ts
30048
+ // src/provisioning/providers/docdb-provider.ts
29999
30049
  import {
30000
- Route53Client as Route53Client2,
30001
- CreateHostedZoneCommand,
30002
- DeleteHostedZoneCommand,
30003
- GetHostedZoneCommand as GetHostedZoneCommand2,
30004
- ChangeResourceRecordSetsCommand,
30005
- UpdateHostedZoneCommentCommand,
30006
- ChangeTagsForResourceCommand,
30007
- AssociateVPCWithHostedZoneCommand,
30008
- DisassociateVPCFromHostedZoneCommand,
30009
- CreateQueryLoggingConfigCommand,
30010
- DeleteQueryLoggingConfigCommand,
30011
- ListQueryLoggingConfigsCommand,
30012
- ListHostedZonesByNameCommand as ListHostedZonesByNameCommand2,
30013
- ListHostedZonesCommand,
30014
- ListResourceRecordSetsCommand,
30015
- ListTagsForResourceCommand as ListTagsForResourceCommand11
30016
- } from "@aws-sdk/client-route-53";
30017
- var Route53Provider = class {
30018
- route53Client;
30050
+ DocDBClient,
30051
+ CreateDBClusterCommand as CreateDBClusterCommand2,
30052
+ DeleteDBClusterCommand as DeleteDBClusterCommand2,
30053
+ ModifyDBClusterCommand as ModifyDBClusterCommand2,
30054
+ DescribeDBClustersCommand as DescribeDBClustersCommand2,
30055
+ CreateDBInstanceCommand as CreateDBInstanceCommand2,
30056
+ DeleteDBInstanceCommand as DeleteDBInstanceCommand2,
30057
+ ModifyDBInstanceCommand as ModifyDBInstanceCommand2,
30058
+ DescribeDBInstancesCommand as DescribeDBInstancesCommand2,
30059
+ CreateDBSubnetGroupCommand as CreateDBSubnetGroupCommand2,
30060
+ DeleteDBSubnetGroupCommand as DeleteDBSubnetGroupCommand2,
30061
+ DescribeDBSubnetGroupsCommand as DescribeDBSubnetGroupsCommand2,
30062
+ ModifyDBSubnetGroupCommand as ModifyDBSubnetGroupCommand2,
30063
+ ListTagsForResourceCommand as ListTagsForResourceCommand11,
30064
+ AddTagsToResourceCommand as AddTagsToResourceCommand3,
30065
+ RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand3
30066
+ } from "@aws-sdk/client-docdb";
30067
+ var DocDBProvider = class {
30068
+ docdbClient;
30019
30069
  providerRegion = process.env["AWS_REGION"];
30020
- logger = getLogger().child("Route53Provider");
30070
+ logger = getLogger().child("DocDBProvider");
30021
30071
  handledProperties = /* @__PURE__ */ new Map([
30022
30072
  [
30023
- "AWS::Route53::HostedZone",
30024
- /* @__PURE__ */ new Set(["Name", "HostedZoneConfig", "HostedZoneTags", "VPCs", "QueryLoggingConfig"])
30073
+ "AWS::DocDB::DBSubnetGroup",
30074
+ /* @__PURE__ */ new Set(["DBSubnetGroupName", "DBSubnetGroupDescription", "SubnetIds", "Tags"])
30025
30075
  ],
30026
30076
  [
30027
- "AWS::Route53::RecordSet",
30077
+ "AWS::DocDB::DBCluster",
30028
30078
  /* @__PURE__ */ new Set([
30029
- "HostedZoneId",
30030
- "HostedZoneName",
30031
- "Name",
30032
- "Type",
30033
- "TTL",
30034
- "ResourceRecords",
30035
- "AliasTarget",
30036
- "SetIdentifier",
30037
- "Weight",
30038
- "Region",
30039
- "Failover",
30040
- "MultiValueAnswer",
30041
- "HealthCheckId",
30042
- "Comment",
30043
- "GeoLocation"
30079
+ "DBClusterIdentifier",
30080
+ "EngineVersion",
30081
+ "MasterUsername",
30082
+ "MasterUserPassword",
30083
+ "Port",
30084
+ "VpcSecurityGroupIds",
30085
+ "DBSubnetGroupName",
30086
+ "StorageEncrypted",
30087
+ "KmsKeyId",
30088
+ "BackupRetentionPeriod",
30089
+ "PreferredBackupWindow",
30090
+ "PreferredMaintenanceWindow",
30091
+ "DBClusterParameterGroupName",
30092
+ "DeletionProtection",
30093
+ "Tags"
30094
+ ])
30095
+ ],
30096
+ [
30097
+ "AWS::DocDB::DBInstance",
30098
+ // DocDB DBInstance does NOT support DeletionProtection (verified
30099
+ // against the @aws-sdk/client-docdb CreateDBInstanceMessage type —
30100
+ // the field is absent). Cluster-level DeletionProtection covers the
30101
+ // common case anyway; instance deletes are gated by the cluster's
30102
+ // protection flag in normal use.
30103
+ /* @__PURE__ */ new Set([
30104
+ "DBInstanceIdentifier",
30105
+ "DBInstanceClass",
30106
+ "DBClusterIdentifier",
30107
+ "AvailabilityZone",
30108
+ "PreferredMaintenanceWindow",
30109
+ "AutoMinorVersionUpgrade",
30110
+ "Tags"
30044
30111
  ])
30045
30112
  ]
30046
30113
  ]);
30047
30114
  getClient() {
30048
- if (!this.route53Client) {
30049
- this.route53Client = new Route53Client2(
30115
+ if (!this.docdbClient) {
30116
+ this.docdbClient = new DocDBClient(
30050
30117
  this.providerRegion ? { region: this.providerRegion } : {}
30051
30118
  );
30052
30119
  }
30053
- return this.route53Client;
30120
+ return this.docdbClient;
30054
30121
  }
30055
30122
  // ─── Dispatch ─────────────────────────────────────────────────────
30056
30123
  async create(logicalId, resourceType, properties) {
30057
30124
  switch (resourceType) {
30058
- case "AWS::Route53::HostedZone":
30059
- return this.createHostedZone(logicalId, resourceType, properties);
30060
- case "AWS::Route53::RecordSet":
30061
- return this.createRecordSet(logicalId, resourceType, properties);
30125
+ case "AWS::DocDB::DBSubnetGroup":
30126
+ return this.createDBSubnetGroup(logicalId, resourceType, properties);
30127
+ case "AWS::DocDB::DBCluster":
30128
+ return this.createDBCluster(logicalId, resourceType, properties);
30129
+ case "AWS::DocDB::DBInstance":
30130
+ return this.createDBInstance(logicalId, resourceType, properties);
30062
30131
  default:
30063
30132
  throw new ProvisioningError(
30064
30133
  `Unsupported resource type: ${resourceType}`,
@@ -30067,12 +30136,32 @@ var Route53Provider = class {
30067
30136
  );
30068
30137
  }
30069
30138
  }
30070
- async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
30139
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
30071
30140
  switch (resourceType) {
30072
- case "AWS::Route53::HostedZone":
30073
- return this.updateHostedZone(logicalId, physicalId, resourceType, properties);
30074
- case "AWS::Route53::RecordSet":
30075
- return this.updateRecordSet(logicalId, physicalId, resourceType, properties);
30141
+ case "AWS::DocDB::DBSubnetGroup":
30142
+ return this.updateDBSubnetGroup(
30143
+ logicalId,
30144
+ physicalId,
30145
+ resourceType,
30146
+ properties,
30147
+ previousProperties
30148
+ );
30149
+ case "AWS::DocDB::DBCluster":
30150
+ return this.updateDBCluster(
30151
+ logicalId,
30152
+ physicalId,
30153
+ resourceType,
30154
+ properties,
30155
+ previousProperties
30156
+ );
30157
+ case "AWS::DocDB::DBInstance":
30158
+ return this.updateDBInstance(
30159
+ logicalId,
30160
+ physicalId,
30161
+ resourceType,
30162
+ properties,
30163
+ previousProperties
30164
+ );
30076
30165
  default:
30077
30166
  throw new ProvisioningError(
30078
30167
  `Unsupported resource type: ${resourceType}`,
@@ -30082,12 +30171,14 @@ var Route53Provider = class {
30082
30171
  );
30083
30172
  }
30084
30173
  }
30085
- async delete(logicalId, physicalId, resourceType, properties, context) {
30174
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
30086
30175
  switch (resourceType) {
30087
- case "AWS::Route53::HostedZone":
30088
- return this.deleteHostedZone(logicalId, physicalId, resourceType, context);
30089
- case "AWS::Route53::RecordSet":
30090
- return this.deleteRecordSet(logicalId, physicalId, resourceType, properties, context);
30176
+ case "AWS::DocDB::DBSubnetGroup":
30177
+ return this.deleteDBSubnetGroup(logicalId, physicalId, resourceType, context);
30178
+ case "AWS::DocDB::DBCluster":
30179
+ return this.deleteDBCluster(logicalId, physicalId, resourceType, context);
30180
+ case "AWS::DocDB::DBInstance":
30181
+ return this.deleteDBInstance(logicalId, physicalId, resourceType, context);
30091
30182
  default:
30092
30183
  throw new ProvisioningError(
30093
30184
  `Unsupported resource type: ${resourceType}`,
@@ -30097,123 +30188,74 @@ var Route53Provider = class {
30097
30188
  );
30098
30189
  }
30099
30190
  }
30100
- async getAttribute(physicalId, resourceType, attributeName) {
30101
- switch (resourceType) {
30102
- case "AWS::Route53::HostedZone":
30103
- return this.getHostedZoneAttribute(physicalId, attributeName);
30104
- default:
30105
- return void 0;
30106
- }
30107
- }
30108
- // ─── AWS::Route53::HostedZone ──────────────────────────────────────
30109
- async createHostedZone(logicalId, resourceType, properties) {
30110
- this.logger.debug(`Creating Route 53 hosted zone ${logicalId}`);
30111
- const name = properties["Name"];
30112
- if (!name) {
30113
- throw new ProvisioningError(
30114
- `Name is required for hosted zone ${logicalId}`,
30115
- resourceType,
30116
- logicalId
30117
- );
30118
- }
30191
+ // ─── DBSubnetGroup ────────────────────────────────────────────────
30192
+ async createDBSubnetGroup(logicalId, resourceType, properties) {
30193
+ this.logger.debug(`Creating DocDB DBSubnetGroup ${logicalId}`);
30194
+ const dbSubnetGroupName = properties["DBSubnetGroupName"] || generateResourceName(logicalId, { maxLength: 255, lowercase: true });
30119
30195
  try {
30120
- const hostedZoneConfig = properties["HostedZoneConfig"];
30121
- const vpcs = properties["VPCs"];
30122
- const firstVpc = vpcs && vpcs.length > 0 ? vpcs[0] : void 0;
30123
- const response = await this.getClient().send(
30124
- new CreateHostedZoneCommand({
30125
- Name: name,
30126
- CallerReference: `${logicalId}-${Date.now()}`,
30127
- ...hostedZoneConfig && hostedZoneConfig["Comment"] ? {
30128
- HostedZoneConfig: {
30129
- Comment: hostedZoneConfig["Comment"],
30130
- // When VPCs are specified, this is a private hosted zone
30131
- ...firstVpc ? { PrivateZone: true } : {}
30132
- }
30133
- } : firstVpc ? { HostedZoneConfig: { PrivateZone: true } } : {},
30134
- ...firstVpc ? {
30135
- VPC: {
30136
- VPCId: firstVpc["VPCId"],
30137
- VPCRegion: firstVpc["VPCRegion"]
30138
- }
30139
- } : {}
30196
+ const tags = this.buildTags(properties);
30197
+ await this.getClient().send(
30198
+ new CreateDBSubnetGroupCommand2({
30199
+ DBSubnetGroupName: dbSubnetGroupName,
30200
+ DBSubnetGroupDescription: properties["DBSubnetGroupDescription"] || `Subnet group for ${logicalId}`,
30201
+ SubnetIds: properties["SubnetIds"],
30202
+ ...tags.length > 0 && { Tags: tags }
30140
30203
  })
30141
30204
  );
30142
- const hostedZone = response.HostedZone;
30143
- if (!hostedZone?.Id) {
30144
- throw new Error("CreateHostedZone did not return HostedZone.Id");
30145
- }
30146
- const zoneId = hostedZone.Id.replace("/hostedzone/", "");
30147
- if (vpcs && vpcs.length > 1) {
30148
- for (let i = 1; i < vpcs.length; i++) {
30149
- const additionalVpc = vpcs[i];
30150
- this.logger.debug(
30151
- `Associating additional VPC ${String(additionalVpc["VPCId"])} with hosted zone ${zoneId}`
30152
- );
30153
- await this.getClient().send(
30154
- new AssociateVPCWithHostedZoneCommand({
30155
- HostedZoneId: zoneId,
30156
- VPC: {
30157
- VPCId: additionalVpc["VPCId"],
30158
- VPCRegion: additionalVpc["VPCRegion"]
30159
- }
30160
- })
30161
- );
30162
- }
30163
- }
30164
- await this.applyHostedZoneTags(zoneId, properties, logicalId);
30165
- await this.applyQueryLoggingConfig(zoneId, properties, logicalId);
30166
- const nameServers = response.DelegationSet?.NameServers ?? [];
30167
- this.logger.debug(`Successfully created hosted zone ${logicalId}: ${zoneId}`);
30205
+ this.logger.debug(
30206
+ `Successfully created DocDB DBSubnetGroup ${logicalId}: ${dbSubnetGroupName}`
30207
+ );
30168
30208
  return {
30169
- physicalId: zoneId,
30209
+ physicalId: dbSubnetGroupName,
30170
30210
  attributes: {
30171
- Id: zoneId,
30172
- NameServers: nameServers.join(",")
30211
+ DBSubnetGroupName: dbSubnetGroupName
30173
30212
  }
30174
30213
  };
30175
30214
  } catch (error) {
30176
- if (error instanceof ProvisioningError)
30177
- throw error;
30178
30215
  const cause = error instanceof Error ? error : void 0;
30179
30216
  throw new ProvisioningError(
30180
- `Failed to create hosted zone ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30217
+ `Failed to create DocDB DBSubnetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30181
30218
  resourceType,
30182
30219
  logicalId,
30183
- void 0,
30220
+ dbSubnetGroupName,
30184
30221
  cause
30185
30222
  );
30186
30223
  }
30187
30224
  }
30188
- async updateHostedZone(logicalId, physicalId, resourceType, properties) {
30189
- this.logger.debug(`Updating Route 53 hosted zone ${logicalId}: ${physicalId}`);
30225
+ async updateDBSubnetGroup(logicalId, physicalId, resourceType, properties, previousProperties) {
30226
+ this.logger.debug(`Updating DocDB DBSubnetGroup ${logicalId}: ${physicalId}`);
30190
30227
  try {
30191
- const hostedZoneConfig = properties["HostedZoneConfig"];
30192
- const comment = hostedZoneConfig?.["Comment"] ?? "";
30193
- await this.getClient().send(
30194
- new UpdateHostedZoneCommentCommand({
30195
- Id: physicalId,
30196
- Comment: comment
30197
- })
30228
+ const subnetIds = properties["SubnetIds"];
30229
+ const sendSubnetIds = subnetIds !== void 0 && subnetIds.length > 0;
30230
+ const modifyInput = {
30231
+ DBSubnetGroupName: physicalId,
30232
+ DBSubnetGroupDescription: properties["DBSubnetGroupDescription"],
30233
+ ...sendSubnetIds && { SubnetIds: subnetIds }
30234
+ };
30235
+ await this.getClient().send(new ModifyDBSubnetGroupCommand2(modifyInput));
30236
+ const desc = await this.getClient().send(
30237
+ new DescribeDBSubnetGroupsCommand2({ DBSubnetGroupName: physicalId })
30198
30238
  );
30199
- await this.applyHostedZoneTags(physicalId, properties, logicalId);
30200
- await this.applyQueryLoggingConfig(physicalId, properties, logicalId);
30201
- await this.syncVPCAssociations(physicalId, properties, logicalId);
30202
- const getResponse = await this.getClient().send(new GetHostedZoneCommand2({ Id: physicalId }));
30203
- const nameServers = getResponse.DelegationSet?.NameServers ?? [];
30204
- this.logger.debug(`Successfully updated hosted zone ${logicalId}`);
30239
+ const arn = desc.DBSubnetGroups?.[0]?.DBSubnetGroupArn;
30240
+ if (arn) {
30241
+ await this.applyTagDiff(
30242
+ arn,
30243
+ previousProperties["Tags"],
30244
+ properties["Tags"]
30245
+ );
30246
+ }
30247
+ this.logger.debug(`Successfully updated DocDB DBSubnetGroup ${logicalId}`);
30205
30248
  return {
30206
30249
  physicalId,
30207
30250
  wasReplaced: false,
30208
30251
  attributes: {
30209
- Id: physicalId,
30210
- NameServers: nameServers.join(",")
30252
+ DBSubnetGroupName: physicalId
30211
30253
  }
30212
30254
  };
30213
30255
  } catch (error) {
30214
30256
  const cause = error instanceof Error ? error : void 0;
30215
30257
  throw new ProvisioningError(
30216
- `Failed to update hosted zone ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30258
+ `Failed to update DocDB DBSubnetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30217
30259
  resourceType,
30218
30260
  logicalId,
30219
30261
  physicalId,
@@ -30221,14 +30263,17 @@ var Route53Provider = class {
30221
30263
  );
30222
30264
  }
30223
30265
  }
30224
- async deleteHostedZone(logicalId, physicalId, resourceType, context) {
30225
- this.logger.debug(`Deleting Route 53 hosted zone ${logicalId}: ${physicalId}`);
30266
+ async deleteDBSubnetGroup(logicalId, physicalId, resourceType, context) {
30267
+ this.logger.debug(`Deleting DocDB DBSubnetGroup ${logicalId}: ${physicalId}`);
30226
30268
  try {
30227
- await this.deleteQueryLoggingConfigForZone(physicalId, logicalId);
30228
- await this.getClient().send(new DeleteHostedZoneCommand({ Id: physicalId }));
30229
- this.logger.debug(`Successfully deleted hosted zone ${logicalId}`);
30269
+ await this.getClient().send(
30270
+ new DeleteDBSubnetGroupCommand2({
30271
+ DBSubnetGroupName: physicalId
30272
+ })
30273
+ );
30274
+ this.logger.debug(`Successfully deleted DocDB DBSubnetGroup ${logicalId}`);
30230
30275
  } catch (error) {
30231
- if (error instanceof Error && error.name === "NoSuchHostedZone") {
30276
+ if (this.isNotFoundError(error, "DBSubnetGroupNotFoundFault")) {
30232
30277
  const clientRegion = await this.getClient().config.region();
30233
30278
  assertRegionMatch(
30234
30279
  clientRegion,
@@ -30237,12 +30282,12 @@ var Route53Provider = class {
30237
30282
  logicalId,
30238
30283
  physicalId
30239
30284
  );
30240
- this.logger.debug(`Hosted zone ${physicalId} does not exist, skipping deletion`);
30285
+ this.logger.debug(`DocDB DBSubnetGroup ${physicalId} does not exist, skipping deletion`);
30241
30286
  return;
30242
30287
  }
30243
30288
  const cause = error instanceof Error ? error : void 0;
30244
30289
  throw new ProvisioningError(
30245
- `Failed to delete hosted zone ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30290
+ `Failed to delete DocDB DBSubnetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30246
30291
  resourceType,
30247
30292
  logicalId,
30248
30293
  physicalId,
@@ -30250,98 +30295,111 @@ var Route53Provider = class {
30250
30295
  );
30251
30296
  }
30252
30297
  }
30253
- async getHostedZoneAttribute(physicalId, attributeName) {
30254
- switch (attributeName) {
30255
- case "Id":
30256
- return physicalId;
30257
- case "NameServers": {
30258
- const response = await this.getClient().send(new GetHostedZoneCommand2({ Id: physicalId }));
30259
- return (response.DelegationSet?.NameServers ?? []).join(",");
30260
- }
30261
- default:
30262
- return void 0;
30263
- }
30264
- }
30265
- // ─── AWS::Route53::RecordSet ───────────────────────────────────────
30266
- async createRecordSet(logicalId, resourceType, properties) {
30267
- this.logger.debug(`Creating Route 53 record set ${logicalId}`);
30268
- const hostedZoneId = await this.resolveHostedZoneId(properties, logicalId, resourceType);
30269
- const recordName = properties["Name"];
30270
- const recordType = properties["Type"];
30298
+ // ─── DBCluster ────────────────────────────────────────────────────
30299
+ async createDBCluster(logicalId, resourceType, properties) {
30300
+ this.logger.debug(`Creating DocDB DBCluster ${logicalId}`);
30301
+ const dbClusterIdentifier = properties["DBClusterIdentifier"] || generateResourceName(logicalId, { maxLength: 63, lowercase: true });
30271
30302
  try {
30272
- const resourceRecordSet = this.buildResourceRecordSet(properties);
30273
- const comment = properties["Comment"];
30274
- await this.getClient().send(
30275
- new ChangeResourceRecordSetsCommand({
30276
- HostedZoneId: hostedZoneId,
30277
- ChangeBatch: {
30278
- ...comment ? { Comment: comment } : {},
30279
- Changes: [
30280
- {
30281
- Action: "CREATE",
30282
- ResourceRecordSet: resourceRecordSet
30283
- }
30284
- ]
30285
- }
30303
+ const tags = this.buildTags(properties);
30304
+ const response = await this.getClient().send(
30305
+ new CreateDBClusterCommand2({
30306
+ DBClusterIdentifier: dbClusterIdentifier,
30307
+ // DocDB engine value is fixed: only `docdb` is accepted.
30308
+ Engine: "docdb",
30309
+ EngineVersion: properties["EngineVersion"],
30310
+ MasterUsername: properties["MasterUsername"],
30311
+ MasterUserPassword: properties["MasterUserPassword"],
30312
+ Port: properties["Port"] != null ? Number(properties["Port"]) : void 0,
30313
+ VpcSecurityGroupIds: properties["VpcSecurityGroupIds"],
30314
+ DBSubnetGroupName: properties["DBSubnetGroupName"],
30315
+ StorageEncrypted: properties["StorageEncrypted"],
30316
+ KmsKeyId: properties["KmsKeyId"],
30317
+ BackupRetentionPeriod: properties["BackupRetentionPeriod"] != null ? Number(properties["BackupRetentionPeriod"]) : void 0,
30318
+ PreferredBackupWindow: properties["PreferredBackupWindow"],
30319
+ PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
30320
+ DBClusterParameterGroupName: properties["DBClusterParameterGroupName"],
30321
+ DeletionProtection: properties["DeletionProtection"],
30322
+ ...tags.length > 0 && { Tags: tags }
30286
30323
  })
30287
30324
  );
30288
- const compositeId = `${hostedZoneId}|${recordName}|${recordType}`;
30289
- this.logger.debug(`Successfully created record set ${logicalId}: ${compositeId}`);
30325
+ const cluster = response.DBCluster;
30326
+ if (!cluster) {
30327
+ throw new Error("CreateDBCluster did not return DBCluster");
30328
+ }
30329
+ this.logger.debug(
30330
+ `Successfully created DocDB DBCluster ${logicalId}: ${dbClusterIdentifier}`
30331
+ );
30332
+ if (process.env["CDKD_NO_WAIT"] !== "true") {
30333
+ await this.waitForClusterAvailable(dbClusterIdentifier);
30334
+ }
30335
+ const described = await this.describeDBCluster(dbClusterIdentifier);
30290
30336
  return {
30291
- physicalId: compositeId,
30292
- attributes: {}
30337
+ physicalId: dbClusterIdentifier,
30338
+ attributes: {
30339
+ "Endpoint.Address": described?.Endpoint ?? "",
30340
+ "Endpoint.Port": String(described?.Port ?? ""),
30341
+ "ReadEndpoint.Address": described?.ReaderEndpoint ?? "",
30342
+ Arn: described?.DBClusterArn ?? "",
30343
+ ClusterResourceId: described?.DbClusterResourceId ?? ""
30344
+ }
30293
30345
  };
30294
30346
  } catch (error) {
30295
30347
  if (error instanceof ProvisioningError)
30296
30348
  throw error;
30297
30349
  const cause = error instanceof Error ? error : void 0;
30298
30350
  throw new ProvisioningError(
30299
- `Failed to create record set ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30351
+ `Failed to create DocDB DBCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30300
30352
  resourceType,
30301
30353
  logicalId,
30302
- void 0,
30354
+ dbClusterIdentifier,
30303
30355
  cause
30304
30356
  );
30305
30357
  }
30306
30358
  }
30307
- async updateRecordSet(logicalId, physicalId, resourceType, properties) {
30308
- this.logger.debug(`Updating Route 53 record set ${logicalId}: ${physicalId}`);
30309
- const hostedZoneId = await this.resolveHostedZoneId(
30310
- properties,
30311
- logicalId,
30312
- resourceType,
30313
- physicalId
30314
- );
30315
- const recordName = properties["Name"];
30316
- const recordType = properties["Type"];
30359
+ async updateDBCluster(logicalId, physicalId, resourceType, properties, previousProperties) {
30360
+ this.logger.debug(`Updating DocDB DBCluster ${logicalId}: ${physicalId}`);
30317
30361
  try {
30318
- const resourceRecordSet = this.buildResourceRecordSet(properties);
30319
- const comment = properties["Comment"];
30362
+ const vpcSgIds = properties["VpcSecurityGroupIds"];
30363
+ const sendVpcSgIds = vpcSgIds !== void 0 && vpcSgIds.length > 0;
30320
30364
  await this.getClient().send(
30321
- new ChangeResourceRecordSetsCommand({
30322
- HostedZoneId: hostedZoneId,
30323
- ChangeBatch: {
30324
- ...comment ? { Comment: comment } : {},
30325
- Changes: [
30326
- {
30327
- Action: "UPSERT",
30328
- ResourceRecordSet: resourceRecordSet
30329
- }
30330
- ]
30331
- }
30365
+ new ModifyDBClusterCommand2({
30366
+ DBClusterIdentifier: physicalId,
30367
+ EngineVersion: properties["EngineVersion"],
30368
+ DeletionProtection: properties["DeletionProtection"],
30369
+ BackupRetentionPeriod: properties["BackupRetentionPeriod"] != null ? Number(properties["BackupRetentionPeriod"]) : void 0,
30370
+ PreferredBackupWindow: properties["PreferredBackupWindow"],
30371
+ PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
30372
+ DBClusterParameterGroupName: properties["DBClusterParameterGroupName"],
30373
+ ...sendVpcSgIds && { VpcSecurityGroupIds: vpcSgIds },
30374
+ MasterUserPassword: properties["MasterUserPassword"],
30375
+ Port: properties["Port"] != null ? Number(properties["Port"]) : void 0,
30376
+ ApplyImmediately: true
30332
30377
  })
30333
30378
  );
30334
- const compositeId = `${hostedZoneId}|${recordName}|${recordType}`;
30335
- this.logger.debug(`Successfully updated record set ${logicalId}`);
30379
+ this.logger.debug(`Successfully updated DocDB DBCluster ${logicalId}`);
30380
+ const described = await this.describeDBCluster(physicalId);
30381
+ if (described?.DBClusterArn) {
30382
+ await this.applyTagDiff(
30383
+ described.DBClusterArn,
30384
+ previousProperties["Tags"],
30385
+ properties["Tags"]
30386
+ );
30387
+ }
30336
30388
  return {
30337
- physicalId: compositeId,
30389
+ physicalId,
30338
30390
  wasReplaced: false,
30339
- attributes: {}
30391
+ attributes: {
30392
+ "Endpoint.Address": described?.Endpoint ?? "",
30393
+ "Endpoint.Port": String(described?.Port ?? ""),
30394
+ "ReadEndpoint.Address": described?.ReaderEndpoint ?? "",
30395
+ Arn: described?.DBClusterArn ?? "",
30396
+ ClusterResourceId: described?.DbClusterResourceId ?? ""
30397
+ }
30340
30398
  };
30341
30399
  } catch (error) {
30342
30400
  const cause = error instanceof Error ? error : void 0;
30343
30401
  throw new ProvisioningError(
30344
- `Failed to update record set ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30402
+ `Failed to update DocDB DBCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30345
30403
  resourceType,
30346
30404
  logicalId,
30347
30405
  physicalId,
@@ -30349,44 +30407,39 @@ var Route53Provider = class {
30349
30407
  );
30350
30408
  }
30351
30409
  }
30352
- async deleteRecordSet(logicalId, physicalId, resourceType, properties, context) {
30353
- this.logger.debug(`Deleting Route 53 record set ${logicalId}: ${physicalId}`);
30354
- const parts = physicalId.split("|");
30355
- if (parts.length !== 3) {
30356
- throw new ProvisioningError(
30357
- `Invalid record set physical ID format: ${physicalId}`,
30358
- resourceType,
30359
- logicalId,
30360
- physicalId
30361
- );
30362
- }
30363
- const [hostedZoneId] = parts;
30364
- if (!properties) {
30365
- throw new ProvisioningError(
30366
- `Properties required to delete record set ${logicalId}`,
30367
- resourceType,
30368
- logicalId,
30369
- physicalId
30370
- );
30371
- }
30410
+ async deleteDBCluster(logicalId, physicalId, resourceType, context) {
30411
+ this.logger.debug(`Deleting DocDB DBCluster ${logicalId}: ${physicalId}`);
30372
30412
  try {
30373
- const resourceRecordSet = this.buildResourceRecordSet(properties);
30374
- await this.getClient().send(
30375
- new ChangeResourceRecordSetsCommand({
30376
- HostedZoneId: hostedZoneId,
30377
- ChangeBatch: {
30378
- Changes: [
30379
- {
30380
- Action: "DELETE",
30381
- ResourceRecordSet: resourceRecordSet
30382
- }
30383
- ]
30413
+ if (context?.removeProtection === true) {
30414
+ try {
30415
+ await this.getClient().send(
30416
+ new ModifyDBClusterCommand2({
30417
+ DBClusterIdentifier: physicalId,
30418
+ DeletionProtection: false,
30419
+ ApplyImmediately: true
30420
+ })
30421
+ );
30422
+ this.logger.debug(
30423
+ `Disabled DeletionProtection on DocDB DBCluster ${logicalId} before delete`
30424
+ );
30425
+ } catch (disableError) {
30426
+ if (!this.isNotFoundError(disableError, "DBClusterNotFoundFault")) {
30427
+ this.logger.debug(
30428
+ `Could not disable deletion protection for ${physicalId}: ${disableError instanceof Error ? disableError.message : String(disableError)}`
30429
+ );
30384
30430
  }
30431
+ }
30432
+ }
30433
+ await this.getClient().send(
30434
+ new DeleteDBClusterCommand2({
30435
+ DBClusterIdentifier: physicalId,
30436
+ SkipFinalSnapshot: true
30385
30437
  })
30386
30438
  );
30387
- this.logger.debug(`Successfully deleted record set ${logicalId}`);
30439
+ this.logger.debug(`Successfully initiated deletion of DocDB DBCluster ${logicalId}`);
30440
+ await this.waitForClusterDeleted(physicalId);
30388
30441
  } catch (error) {
30389
- if (error instanceof Error && (error.name === "InvalidChangeBatch" || error.message.includes("it was not found"))) {
30442
+ if (this.isNotFoundError(error, "DBClusterNotFoundFault")) {
30390
30443
  const clientRegion = await this.getClient().config.region();
30391
30444
  assertRegionMatch(
30392
30445
  clientRegion,
@@ -30395,18 +30448,12 @@ var Route53Provider = class {
30395
30448
  logicalId,
30396
30449
  physicalId
30397
30450
  );
30398
- this.logger.debug(`Record set ${physicalId} does not exist, skipping deletion`);
30399
- return;
30400
- }
30401
- if (error instanceof Error && error.name === "NoSuchHostedZone") {
30402
- this.logger.debug(
30403
- `Hosted zone for record set ${physicalId} does not exist, skipping deletion`
30404
- );
30451
+ this.logger.debug(`DocDB DBCluster ${physicalId} does not exist, skipping deletion`);
30405
30452
  return;
30406
30453
  }
30407
30454
  const cause = error instanceof Error ? error : void 0;
30408
30455
  throw new ProvisioningError(
30409
- `Failed to delete record set ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30456
+ `Failed to delete DocDB DBCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30410
30457
  resourceType,
30411
30458
  logicalId,
30412
30459
  physicalId,
@@ -30414,49 +30461,2011 @@ var Route53Provider = class {
30414
30461
  );
30415
30462
  }
30416
30463
  }
30417
- // ─── Helpers ───────────────────────────────────────────────────────
30418
- /**
30419
- * Build a ResourceRecordSet object from CDK properties.
30420
- *
30421
- * Handles conversion of CDK-style ResourceRecords (array of strings)
30422
- * to SDK-style ResourceRecords (array of {Value}).
30423
- * Also handles routing policy properties: Weight, Region, Failover,
30424
- * MultiValueAnswer, GeoLocation, SetIdentifier, and HealthCheckId.
30425
- */
30426
- buildResourceRecordSet(properties) {
30427
- const name = properties["Name"];
30428
- const type = properties["Type"];
30429
- const ttl = properties["TTL"];
30430
- const resourceRecords = properties["ResourceRecords"];
30431
- const aliasTarget = properties["AliasTarget"];
30432
- const recordSet = {
30433
- Name: name,
30434
- Type: type
30435
- };
30436
- if (aliasTarget) {
30437
- recordSet.AliasTarget = {
30438
- HostedZoneId: aliasTarget["HostedZoneId"],
30439
- DNSName: aliasTarget["DNSName"],
30440
- EvaluateTargetHealth: aliasTarget["EvaluateTargetHealth"] ?? false
30441
- };
30442
- } else {
30443
- if (ttl !== void 0) {
30444
- recordSet.TTL = Number(ttl);
30445
- }
30446
- if (resourceRecords) {
30447
- recordSet.ResourceRecords = resourceRecords.map((record) => {
30448
- if (typeof record === "string") {
30449
- return { Value: record };
30450
- }
30451
- return record;
30452
- });
30453
- }
30454
- }
30455
- const setIdentifier = properties["SetIdentifier"];
30456
- if (setIdentifier !== void 0) {
30457
- recordSet.SetIdentifier = setIdentifier;
30458
- }
30459
- const weight = properties["Weight"];
30464
+ // ─── DBInstance ───────────────────────────────────────────────────
30465
+ async createDBInstance(logicalId, resourceType, properties) {
30466
+ this.logger.debug(`Creating DocDB DBInstance ${logicalId}`);
30467
+ const dbInstanceIdentifier = properties["DBInstanceIdentifier"] || generateResourceName(logicalId, { maxLength: 63, lowercase: true });
30468
+ try {
30469
+ const tags = this.buildTags(properties);
30470
+ const response = await this.getClient().send(
30471
+ new CreateDBInstanceCommand2({
30472
+ DBInstanceIdentifier: dbInstanceIdentifier,
30473
+ DBInstanceClass: properties["DBInstanceClass"],
30474
+ // DocDB engine value is fixed: only `docdb` is accepted.
30475
+ Engine: "docdb",
30476
+ DBClusterIdentifier: properties["DBClusterIdentifier"],
30477
+ AvailabilityZone: properties["AvailabilityZone"],
30478
+ PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
30479
+ AutoMinorVersionUpgrade: properties["AutoMinorVersionUpgrade"],
30480
+ ...tags.length > 0 && { Tags: tags }
30481
+ })
30482
+ );
30483
+ const instance = response.DBInstance;
30484
+ if (!instance) {
30485
+ throw new Error("CreateDBInstance did not return DBInstance");
30486
+ }
30487
+ this.logger.debug(
30488
+ `Successfully created DocDB DBInstance ${logicalId}: ${dbInstanceIdentifier}`
30489
+ );
30490
+ if (process.env["CDKD_NO_WAIT"] !== "true") {
30491
+ await this.waitForInstanceAvailable(dbInstanceIdentifier);
30492
+ }
30493
+ const described = await this.describeDBInstance(dbInstanceIdentifier);
30494
+ return {
30495
+ physicalId: dbInstanceIdentifier,
30496
+ attributes: {
30497
+ "Endpoint.Address": described?.Endpoint?.Address ?? "",
30498
+ "Endpoint.Port": String(described?.Endpoint?.Port ?? ""),
30499
+ Arn: described?.DBInstanceArn ?? ""
30500
+ }
30501
+ };
30502
+ } catch (error) {
30503
+ if (error instanceof ProvisioningError)
30504
+ throw error;
30505
+ const cause = error instanceof Error ? error : void 0;
30506
+ throw new ProvisioningError(
30507
+ `Failed to create DocDB DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30508
+ resourceType,
30509
+ logicalId,
30510
+ dbInstanceIdentifier,
30511
+ cause
30512
+ );
30513
+ }
30514
+ }
30515
+ async updateDBInstance(logicalId, physicalId, resourceType, properties, previousProperties) {
30516
+ this.logger.debug(`Updating DocDB DBInstance ${logicalId}: ${physicalId}`);
30517
+ try {
30518
+ await this.getClient().send(
30519
+ new ModifyDBInstanceCommand2({
30520
+ DBInstanceIdentifier: physicalId,
30521
+ DBInstanceClass: properties["DBInstanceClass"],
30522
+ PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
30523
+ AutoMinorVersionUpgrade: properties["AutoMinorVersionUpgrade"],
30524
+ ApplyImmediately: true
30525
+ })
30526
+ );
30527
+ this.logger.debug(`Successfully updated DocDB DBInstance ${logicalId}`);
30528
+ const described = await this.describeDBInstance(physicalId);
30529
+ if (described?.DBInstanceArn) {
30530
+ await this.applyTagDiff(
30531
+ described.DBInstanceArn,
30532
+ previousProperties["Tags"],
30533
+ properties["Tags"]
30534
+ );
30535
+ }
30536
+ return {
30537
+ physicalId,
30538
+ wasReplaced: false,
30539
+ attributes: {
30540
+ "Endpoint.Address": described?.Endpoint?.Address ?? "",
30541
+ "Endpoint.Port": String(described?.Endpoint?.Port ?? ""),
30542
+ Arn: described?.DBInstanceArn ?? ""
30543
+ }
30544
+ };
30545
+ } catch (error) {
30546
+ const cause = error instanceof Error ? error : void 0;
30547
+ throw new ProvisioningError(
30548
+ `Failed to update DocDB DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30549
+ resourceType,
30550
+ logicalId,
30551
+ physicalId,
30552
+ cause
30553
+ );
30554
+ }
30555
+ }
30556
+ async deleteDBInstance(logicalId, physicalId, resourceType, context) {
30557
+ this.logger.debug(`Deleting DocDB DBInstance ${logicalId}: ${physicalId}`);
30558
+ try {
30559
+ await this.getClient().send(
30560
+ new DeleteDBInstanceCommand2({
30561
+ DBInstanceIdentifier: physicalId
30562
+ })
30563
+ );
30564
+ this.logger.debug(`Successfully initiated deletion of DocDB DBInstance ${logicalId}`);
30565
+ await this.waitForInstanceDeleted(physicalId);
30566
+ } catch (error) {
30567
+ if (this.isNotFoundError(error, "DBInstanceNotFoundFault")) {
30568
+ const clientRegion = await this.getClient().config.region();
30569
+ assertRegionMatch(
30570
+ clientRegion,
30571
+ context?.expectedRegion,
30572
+ resourceType,
30573
+ logicalId,
30574
+ physicalId
30575
+ );
30576
+ this.logger.debug(`DocDB DBInstance ${physicalId} does not exist, skipping deletion`);
30577
+ return;
30578
+ }
30579
+ const cause = error instanceof Error ? error : void 0;
30580
+ throw new ProvisioningError(
30581
+ `Failed to delete DocDB DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
30582
+ resourceType,
30583
+ logicalId,
30584
+ physicalId,
30585
+ cause
30586
+ );
30587
+ }
30588
+ }
30589
+ // ─── Helpers ──────────────────────────────────────────────────────
30590
+ /**
30591
+ * Apply a diff between old and new CFn-shape Tags arrays via DocDB's
30592
+ * `AddTagsToResource` / `RemoveTagsFromResource` APIs (keyed by
30593
+ * `ResourceName=arn`).
30594
+ */
30595
+ async applyTagDiff(arn, oldTagsRaw, newTagsRaw) {
30596
+ const toMap = (tags) => {
30597
+ const m = /* @__PURE__ */ new Map();
30598
+ for (const t of tags ?? []) {
30599
+ if (t.Key !== void 0 && t.Value !== void 0)
30600
+ m.set(t.Key, t.Value);
30601
+ }
30602
+ return m;
30603
+ };
30604
+ const oldMap = toMap(oldTagsRaw);
30605
+ const newMap = toMap(newTagsRaw);
30606
+ const tagsToAdd = [];
30607
+ for (const [k, v] of newMap) {
30608
+ if (oldMap.get(k) !== v)
30609
+ tagsToAdd.push({ Key: k, Value: v });
30610
+ }
30611
+ const tagsToRemove = [];
30612
+ for (const k of oldMap.keys()) {
30613
+ if (!newMap.has(k))
30614
+ tagsToRemove.push(k);
30615
+ }
30616
+ if (tagsToRemove.length > 0) {
30617
+ await this.getClient().send(
30618
+ new RemoveTagsFromResourceCommand3({ ResourceName: arn, TagKeys: tagsToRemove })
30619
+ );
30620
+ this.logger.debug(`Removed ${tagsToRemove.length} tag(s) from DocDB resource ${arn}`);
30621
+ }
30622
+ if (tagsToAdd.length > 0) {
30623
+ await this.getClient().send(
30624
+ new AddTagsToResourceCommand3({ ResourceName: arn, Tags: tagsToAdd })
30625
+ );
30626
+ this.logger.debug(`Added/updated ${tagsToAdd.length} tag(s) on DocDB resource ${arn}`);
30627
+ }
30628
+ }
30629
+ buildTags(properties) {
30630
+ if (!properties["Tags"])
30631
+ return [];
30632
+ return properties["Tags"];
30633
+ }
30634
+ isNotFoundError(error, faultName) {
30635
+ if (!(error instanceof Error))
30636
+ return false;
30637
+ const name = error.name ?? "";
30638
+ const message = error.message.toLowerCase();
30639
+ return name === faultName || message.includes("not found") || message.includes("does not exist");
30640
+ }
30641
+ async describeDBCluster(dbClusterIdentifier) {
30642
+ const response = await this.getClient().send(
30643
+ new DescribeDBClustersCommand2({
30644
+ DBClusterIdentifier: dbClusterIdentifier
30645
+ })
30646
+ );
30647
+ return response.DBClusters?.[0];
30648
+ }
30649
+ async describeDBInstance(dbInstanceIdentifier) {
30650
+ const response = await this.getClient().send(
30651
+ new DescribeDBInstancesCommand2({
30652
+ DBInstanceIdentifier: dbInstanceIdentifier
30653
+ })
30654
+ );
30655
+ return response.DBInstances?.[0];
30656
+ }
30657
+ /**
30658
+ * Wait for a DBCluster to become available. DocDB's SDK does not ship a
30659
+ * `waitUntilDBClusterAvailable` waiter (only DBInstance has waiters),
30660
+ * so we poll Status manually with exponential backoff.
30661
+ */
30662
+ async waitForClusterAvailable(dbClusterIdentifier, maxWaitMs = 18e5) {
30663
+ const startTime = Date.now();
30664
+ let delay = 5e3;
30665
+ while (Date.now() - startTime < maxWaitMs) {
30666
+ const cluster = await this.describeDBCluster(dbClusterIdentifier);
30667
+ const status = cluster?.Status;
30668
+ this.logger.debug(`DocDB DBCluster ${dbClusterIdentifier} status: ${status}`);
30669
+ if (status === "available")
30670
+ return;
30671
+ await this.sleep(delay);
30672
+ delay = Math.min(delay * 2, 3e4);
30673
+ }
30674
+ throw new Error(
30675
+ `Timed out waiting for DocDB DBCluster ${dbClusterIdentifier} to become available`
30676
+ );
30677
+ }
30678
+ /**
30679
+ * Wait for a DBCluster to be deleted (no SDK waiter — manual poll).
30680
+ */
30681
+ async waitForClusterDeleted(dbClusterIdentifier, maxWaitMs = 18e5) {
30682
+ const startTime = Date.now();
30683
+ let delay = 5e3;
30684
+ while (Date.now() - startTime < maxWaitMs) {
30685
+ try {
30686
+ const cluster = await this.describeDBCluster(dbClusterIdentifier);
30687
+ const status = cluster?.Status;
30688
+ this.logger.debug(`DocDB DBCluster ${dbClusterIdentifier} status: ${status}`);
30689
+ if (!cluster)
30690
+ return;
30691
+ } catch (error) {
30692
+ if (this.isNotFoundError(error, "DBClusterNotFoundFault")) {
30693
+ return;
30694
+ }
30695
+ throw error;
30696
+ }
30697
+ await this.sleep(delay);
30698
+ delay = Math.min(delay * 2, 3e4);
30699
+ }
30700
+ throw new Error(`Timed out waiting for DocDB DBCluster ${dbClusterIdentifier} to be deleted`);
30701
+ }
30702
+ /**
30703
+ * Wait for a DBInstance to become available (manual poll — matches RDS).
30704
+ */
30705
+ async waitForInstanceAvailable(dbInstanceIdentifier, maxWaitMs = 18e5) {
30706
+ const startTime = Date.now();
30707
+ let delay = 1e4;
30708
+ while (Date.now() - startTime < maxWaitMs) {
30709
+ const instance = await this.describeDBInstance(dbInstanceIdentifier);
30710
+ const status = instance?.DBInstanceStatus;
30711
+ this.logger.debug(`DocDB DBInstance ${dbInstanceIdentifier} status: ${status}`);
30712
+ if (status === "available")
30713
+ return;
30714
+ await this.sleep(delay);
30715
+ delay = Math.min(delay * 2, 3e4);
30716
+ }
30717
+ throw new Error(
30718
+ `Timed out waiting for DocDB DBInstance ${dbInstanceIdentifier} to become available`
30719
+ );
30720
+ }
30721
+ async waitForInstanceDeleted(dbInstanceIdentifier, maxWaitMs = 18e5) {
30722
+ const startTime = Date.now();
30723
+ let delay = 1e4;
30724
+ while (Date.now() - startTime < maxWaitMs) {
30725
+ try {
30726
+ const instance = await this.describeDBInstance(dbInstanceIdentifier);
30727
+ const status = instance?.DBInstanceStatus;
30728
+ this.logger.debug(`DocDB DBInstance ${dbInstanceIdentifier} status: ${status}`);
30729
+ if (!instance)
30730
+ return;
30731
+ } catch (error) {
30732
+ if (this.isNotFoundError(error, "DBInstanceNotFoundFault")) {
30733
+ return;
30734
+ }
30735
+ throw error;
30736
+ }
30737
+ await this.sleep(delay);
30738
+ delay = Math.min(delay * 2, 3e4);
30739
+ }
30740
+ throw new Error(`Timed out waiting for DocDB DBInstance ${dbInstanceIdentifier} to be deleted`);
30741
+ }
30742
+ sleep(ms) {
30743
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
30744
+ }
30745
+ /**
30746
+ * Adopt an existing DocDB resource into cdkd state.
30747
+ *
30748
+ * Supported types: `AWS::DocDB::DBInstance`, `AWS::DocDB::DBCluster`,
30749
+ * `AWS::DocDB::DBSubnetGroup`. Identifier name properties (`DBInstance
30750
+ * Identifier` / `DBClusterIdentifier` / `DBSubnetGroupName`) are usually
30751
+ * present in CDK templates; fall back to `aws:cdk:path` tag lookup via
30752
+ * the corresponding `Describe*` + `ListTagsForResource` pair otherwise.
30753
+ */
30754
+ async import(input) {
30755
+ switch (input.resourceType) {
30756
+ case "AWS::DocDB::DBInstance":
30757
+ return this.importDBInstance(input);
30758
+ case "AWS::DocDB::DBCluster":
30759
+ return this.importDBCluster(input);
30760
+ case "AWS::DocDB::DBSubnetGroup":
30761
+ return this.importDBSubnetGroup(input);
30762
+ default:
30763
+ return null;
30764
+ }
30765
+ }
30766
+ /**
30767
+ * Read the AWS-current DocDB resource configuration in CFn-property shape.
30768
+ *
30769
+ * Each branch surfaces only the keys cdkd's `create()` accepts. Sensitive
30770
+ * fields like `MasterUserPassword` are NEVER surfaced (DocDB does not
30771
+ * return them in the Describe responses). `Tags` are surfaced via a
30772
+ * follow-up `ListTagsForResource(ResourceName=arn)` call.
30773
+ *
30774
+ * Returns `undefined` when the resource is gone (`*NotFoundFault`).
30775
+ */
30776
+ async readCurrentState(physicalId, _logicalId, resourceType) {
30777
+ switch (resourceType) {
30778
+ case "AWS::DocDB::DBInstance":
30779
+ return this.readCurrentStateDBInstance(physicalId);
30780
+ case "AWS::DocDB::DBCluster":
30781
+ return this.readCurrentStateDBCluster(physicalId);
30782
+ case "AWS::DocDB::DBSubnetGroup":
30783
+ return this.readCurrentStateDBSubnetGroup(physicalId);
30784
+ default:
30785
+ return void 0;
30786
+ }
30787
+ }
30788
+ async readCurrentStateDBInstance(physicalId) {
30789
+ let inst;
30790
+ try {
30791
+ inst = await this.describeDBInstance(physicalId);
30792
+ } catch (err) {
30793
+ if (this.isNotFoundError(err, "DBInstanceNotFoundFault"))
30794
+ return void 0;
30795
+ throw err;
30796
+ }
30797
+ if (!inst)
30798
+ return void 0;
30799
+ const result = {};
30800
+ if (inst.DBInstanceIdentifier !== void 0) {
30801
+ result["DBInstanceIdentifier"] = inst.DBInstanceIdentifier;
30802
+ }
30803
+ if (inst.DBInstanceClass !== void 0)
30804
+ result["DBInstanceClass"] = inst.DBInstanceClass;
30805
+ if (inst.DBClusterIdentifier !== void 0) {
30806
+ result["DBClusterIdentifier"] = inst.DBClusterIdentifier;
30807
+ }
30808
+ if (inst.AvailabilityZone !== void 0)
30809
+ result["AvailabilityZone"] = inst.AvailabilityZone;
30810
+ if (inst.PreferredMaintenanceWindow !== void 0) {
30811
+ result["PreferredMaintenanceWindow"] = inst.PreferredMaintenanceWindow;
30812
+ }
30813
+ if (inst.AutoMinorVersionUpgrade !== void 0) {
30814
+ result["AutoMinorVersionUpgrade"] = inst.AutoMinorVersionUpgrade;
30815
+ }
30816
+ if (inst.DBInstanceArn)
30817
+ await this.attachTags(result, inst.DBInstanceArn);
30818
+ return result;
30819
+ }
30820
+ async readCurrentStateDBCluster(physicalId) {
30821
+ let cluster;
30822
+ try {
30823
+ cluster = await this.describeDBCluster(physicalId);
30824
+ } catch (err) {
30825
+ if (this.isNotFoundError(err, "DBClusterNotFoundFault"))
30826
+ return void 0;
30827
+ throw err;
30828
+ }
30829
+ if (!cluster)
30830
+ return void 0;
30831
+ const result = {};
30832
+ if (cluster.DBClusterIdentifier !== void 0) {
30833
+ result["DBClusterIdentifier"] = cluster.DBClusterIdentifier;
30834
+ }
30835
+ if (cluster.EngineVersion !== void 0)
30836
+ result["EngineVersion"] = cluster.EngineVersion;
30837
+ if (cluster.MasterUsername !== void 0)
30838
+ result["MasterUsername"] = cluster.MasterUsername;
30839
+ if (cluster.Port !== void 0)
30840
+ result["Port"] = cluster.Port;
30841
+ result["VpcSecurityGroupIds"] = (cluster.VpcSecurityGroups ?? []).map((sg) => sg.VpcSecurityGroupId).filter((id) => !!id);
30842
+ if (cluster.DBSubnetGroup !== void 0)
30843
+ result["DBSubnetGroupName"] = cluster.DBSubnetGroup;
30844
+ if (cluster.StorageEncrypted !== void 0) {
30845
+ result["StorageEncrypted"] = cluster.StorageEncrypted;
30846
+ }
30847
+ if (cluster.KmsKeyId !== void 0)
30848
+ result["KmsKeyId"] = cluster.KmsKeyId;
30849
+ if (cluster.BackupRetentionPeriod !== void 0) {
30850
+ result["BackupRetentionPeriod"] = cluster.BackupRetentionPeriod;
30851
+ }
30852
+ if (cluster.PreferredBackupWindow !== void 0) {
30853
+ result["PreferredBackupWindow"] = cluster.PreferredBackupWindow;
30854
+ }
30855
+ if (cluster.PreferredMaintenanceWindow !== void 0) {
30856
+ result["PreferredMaintenanceWindow"] = cluster.PreferredMaintenanceWindow;
30857
+ }
30858
+ if (cluster.DBClusterParameterGroup !== void 0) {
30859
+ result["DBClusterParameterGroupName"] = cluster.DBClusterParameterGroup;
30860
+ }
30861
+ if (cluster.DeletionProtection !== void 0) {
30862
+ result["DeletionProtection"] = cluster.DeletionProtection;
30863
+ }
30864
+ if (cluster.DBClusterArn)
30865
+ await this.attachTags(result, cluster.DBClusterArn);
30866
+ return result;
30867
+ }
30868
+ async readCurrentStateDBSubnetGroup(physicalId) {
30869
+ let resp;
30870
+ try {
30871
+ resp = await this.getClient().send(
30872
+ new DescribeDBSubnetGroupsCommand2({ DBSubnetGroupName: physicalId })
30873
+ );
30874
+ } catch (err) {
30875
+ if (this.isNotFoundError(err, "DBSubnetGroupNotFoundFault"))
30876
+ return void 0;
30877
+ throw err;
30878
+ }
30879
+ const sg = resp.DBSubnetGroups?.[0];
30880
+ if (!sg)
30881
+ return void 0;
30882
+ const result = {};
30883
+ if (sg.DBSubnetGroupName !== void 0)
30884
+ result["DBSubnetGroupName"] = sg.DBSubnetGroupName;
30885
+ if (sg.DBSubnetGroupDescription !== void 0) {
30886
+ result["DBSubnetGroupDescription"] = sg.DBSubnetGroupDescription;
30887
+ }
30888
+ result["SubnetIds"] = (sg.Subnets ?? []).map((s) => s.SubnetIdentifier).filter((id) => !!id);
30889
+ if (sg.DBSubnetGroupArn)
30890
+ await this.attachTags(result, sg.DBSubnetGroupArn);
30891
+ return result;
30892
+ }
30893
+ /**
30894
+ * Fetch tags via `ListTagsForResource(ResourceName=arn)` and merge them
30895
+ * into the result under `Tags` (CFn shape, `aws:*` filtered out, omitted
30896
+ * when empty). Best-effort: tag-fetch failures are logged at debug and
30897
+ * the key is simply left out — drift detection on configuration is more
30898
+ * important than fail-closing on a missing tag permission.
30899
+ */
30900
+ async attachTags(result, arn) {
30901
+ try {
30902
+ const tagsResp = await this.getClient().send(
30903
+ new ListTagsForResourceCommand11({ ResourceName: arn })
30904
+ );
30905
+ const tags = normalizeAwsTagsToCfn(tagsResp.TagList);
30906
+ result["Tags"] = tags;
30907
+ } catch (err) {
30908
+ this.logger.debug(
30909
+ `DocDB ListTagsForResource(${arn}) failed: ${err instanceof Error ? err.message : String(err)}`
30910
+ );
30911
+ }
30912
+ }
30913
+ async importDBInstance(input) {
30914
+ const explicit = resolveExplicitPhysicalId(input, "DBInstanceIdentifier");
30915
+ if (explicit) {
30916
+ try {
30917
+ await this.getClient().send(
30918
+ new DescribeDBInstancesCommand2({ DBInstanceIdentifier: explicit })
30919
+ );
30920
+ return { physicalId: explicit, attributes: {} };
30921
+ } catch (err) {
30922
+ if (err.name === "DBInstanceNotFoundFault")
30923
+ return null;
30924
+ throw err;
30925
+ }
30926
+ }
30927
+ if (!input.cdkPath)
30928
+ return null;
30929
+ let marker;
30930
+ do {
30931
+ const list = await this.getClient().send(
30932
+ new DescribeDBInstancesCommand2({ ...marker && { Marker: marker } })
30933
+ );
30934
+ for (const inst of list.DBInstances ?? []) {
30935
+ if (!inst.DBInstanceIdentifier || !inst.DBInstanceArn)
30936
+ continue;
30937
+ const tagsResp = await this.getClient().send(
30938
+ new ListTagsForResourceCommand11({ ResourceName: inst.DBInstanceArn })
30939
+ );
30940
+ if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
30941
+ return { physicalId: inst.DBInstanceIdentifier, attributes: {} };
30942
+ }
30943
+ }
30944
+ marker = list.Marker;
30945
+ } while (marker);
30946
+ return null;
30947
+ }
30948
+ async importDBCluster(input) {
30949
+ const explicit = resolveExplicitPhysicalId(input, "DBClusterIdentifier");
30950
+ if (explicit) {
30951
+ try {
30952
+ await this.getClient().send(
30953
+ new DescribeDBClustersCommand2({ DBClusterIdentifier: explicit })
30954
+ );
30955
+ return { physicalId: explicit, attributes: {} };
30956
+ } catch (err) {
30957
+ if (err.name === "DBClusterNotFoundFault")
30958
+ return null;
30959
+ throw err;
30960
+ }
30961
+ }
30962
+ if (!input.cdkPath)
30963
+ return null;
30964
+ let marker;
30965
+ do {
30966
+ const list = await this.getClient().send(
30967
+ new DescribeDBClustersCommand2({ ...marker && { Marker: marker } })
30968
+ );
30969
+ for (const c of list.DBClusters ?? []) {
30970
+ if (!c.DBClusterIdentifier || !c.DBClusterArn)
30971
+ continue;
30972
+ const tagsResp = await this.getClient().send(
30973
+ new ListTagsForResourceCommand11({ ResourceName: c.DBClusterArn })
30974
+ );
30975
+ if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
30976
+ return { physicalId: c.DBClusterIdentifier, attributes: {} };
30977
+ }
30978
+ }
30979
+ marker = list.Marker;
30980
+ } while (marker);
30981
+ return null;
30982
+ }
30983
+ async importDBSubnetGroup(input) {
30984
+ const explicit = resolveExplicitPhysicalId(input, "DBSubnetGroupName");
30985
+ if (explicit) {
30986
+ try {
30987
+ await this.getClient().send(
30988
+ new DescribeDBSubnetGroupsCommand2({ DBSubnetGroupName: explicit })
30989
+ );
30990
+ return { physicalId: explicit, attributes: {} };
30991
+ } catch (err) {
30992
+ if (err.name === "DBSubnetGroupNotFoundFault")
30993
+ return null;
30994
+ throw err;
30995
+ }
30996
+ }
30997
+ if (!input.cdkPath)
30998
+ return null;
30999
+ let marker;
31000
+ do {
31001
+ const list = await this.getClient().send(
31002
+ new DescribeDBSubnetGroupsCommand2({ ...marker && { Marker: marker } })
31003
+ );
31004
+ for (const sg of list.DBSubnetGroups ?? []) {
31005
+ if (!sg.DBSubnetGroupName || !sg.DBSubnetGroupArn)
31006
+ continue;
31007
+ const tagsResp = await this.getClient().send(
31008
+ new ListTagsForResourceCommand11({ ResourceName: sg.DBSubnetGroupArn })
31009
+ );
31010
+ if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
31011
+ return { physicalId: sg.DBSubnetGroupName, attributes: {} };
31012
+ }
31013
+ }
31014
+ marker = list.Marker;
31015
+ } while (marker);
31016
+ return null;
31017
+ }
31018
+ };
31019
+
31020
+ // src/provisioning/providers/neptune-provider.ts
31021
+ import {
31022
+ NeptuneClient,
31023
+ CreateDBClusterCommand as CreateDBClusterCommand3,
31024
+ DeleteDBClusterCommand as DeleteDBClusterCommand3,
31025
+ ModifyDBClusterCommand as ModifyDBClusterCommand3,
31026
+ DescribeDBClustersCommand as DescribeDBClustersCommand3,
31027
+ CreateDBInstanceCommand as CreateDBInstanceCommand3,
31028
+ DeleteDBInstanceCommand as DeleteDBInstanceCommand3,
31029
+ ModifyDBInstanceCommand as ModifyDBInstanceCommand3,
31030
+ DescribeDBInstancesCommand as DescribeDBInstancesCommand3,
31031
+ CreateDBSubnetGroupCommand as CreateDBSubnetGroupCommand3,
31032
+ DeleteDBSubnetGroupCommand as DeleteDBSubnetGroupCommand3,
31033
+ DescribeDBSubnetGroupsCommand as DescribeDBSubnetGroupsCommand3,
31034
+ ModifyDBSubnetGroupCommand as ModifyDBSubnetGroupCommand3,
31035
+ ListTagsForResourceCommand as ListTagsForResourceCommand12,
31036
+ AddTagsToResourceCommand as AddTagsToResourceCommand4,
31037
+ RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand4
31038
+ } from "@aws-sdk/client-neptune";
31039
+ var NeptuneProvider = class {
31040
+ neptuneClient;
31041
+ providerRegion = process.env["AWS_REGION"];
31042
+ logger = getLogger().child("NeptuneProvider");
31043
+ handledProperties = /* @__PURE__ */ new Map([
31044
+ [
31045
+ "AWS::Neptune::DBSubnetGroup",
31046
+ /* @__PURE__ */ new Set(["DBSubnetGroupName", "DBSubnetGroupDescription", "SubnetIds", "Tags"])
31047
+ ],
31048
+ [
31049
+ "AWS::Neptune::DBCluster",
31050
+ /* @__PURE__ */ new Set([
31051
+ "DBClusterIdentifier",
31052
+ "EngineVersion",
31053
+ "Port",
31054
+ "VpcSecurityGroupIds",
31055
+ "DBSubnetGroupName",
31056
+ "StorageEncrypted",
31057
+ "KmsKeyId",
31058
+ "BackupRetentionPeriod",
31059
+ "PreferredBackupWindow",
31060
+ "PreferredMaintenanceWindow",
31061
+ "DBClusterParameterGroupName",
31062
+ "IamAuthEnabled",
31063
+ "DeletionProtection",
31064
+ "Tags"
31065
+ ])
31066
+ ],
31067
+ [
31068
+ "AWS::Neptune::DBInstance",
31069
+ /* @__PURE__ */ new Set([
31070
+ "DBInstanceIdentifier",
31071
+ "DBInstanceClass",
31072
+ "DBClusterIdentifier",
31073
+ "DBSubnetGroupName",
31074
+ "DBParameterGroupName",
31075
+ "AvailabilityZone",
31076
+ "PreferredMaintenanceWindow",
31077
+ "AutoMinorVersionUpgrade",
31078
+ "DeletionProtection",
31079
+ "Tags"
31080
+ ])
31081
+ ]
31082
+ ]);
31083
+ getClient() {
31084
+ if (!this.neptuneClient) {
31085
+ this.neptuneClient = new NeptuneClient(
31086
+ this.providerRegion ? { region: this.providerRegion } : {}
31087
+ );
31088
+ }
31089
+ return this.neptuneClient;
31090
+ }
31091
+ // ─── Dispatch ─────────────────────────────────────────────────────
31092
+ async create(logicalId, resourceType, properties) {
31093
+ switch (resourceType) {
31094
+ case "AWS::Neptune::DBSubnetGroup":
31095
+ return this.createDBSubnetGroup(logicalId, resourceType, properties);
31096
+ case "AWS::Neptune::DBCluster":
31097
+ return this.createDBCluster(logicalId, resourceType, properties);
31098
+ case "AWS::Neptune::DBInstance":
31099
+ return this.createDBInstance(logicalId, resourceType, properties);
31100
+ default:
31101
+ throw new ProvisioningError(
31102
+ `Unsupported resource type: ${resourceType}`,
31103
+ resourceType,
31104
+ logicalId
31105
+ );
31106
+ }
31107
+ }
31108
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
31109
+ switch (resourceType) {
31110
+ case "AWS::Neptune::DBSubnetGroup":
31111
+ return this.updateDBSubnetGroup(
31112
+ logicalId,
31113
+ physicalId,
31114
+ resourceType,
31115
+ properties,
31116
+ previousProperties
31117
+ );
31118
+ case "AWS::Neptune::DBCluster":
31119
+ return this.updateDBCluster(
31120
+ logicalId,
31121
+ physicalId,
31122
+ resourceType,
31123
+ properties,
31124
+ previousProperties
31125
+ );
31126
+ case "AWS::Neptune::DBInstance":
31127
+ return this.updateDBInstance(
31128
+ logicalId,
31129
+ physicalId,
31130
+ resourceType,
31131
+ properties,
31132
+ previousProperties
31133
+ );
31134
+ default:
31135
+ throw new ProvisioningError(
31136
+ `Unsupported resource type: ${resourceType}`,
31137
+ resourceType,
31138
+ logicalId,
31139
+ physicalId
31140
+ );
31141
+ }
31142
+ }
31143
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
31144
+ switch (resourceType) {
31145
+ case "AWS::Neptune::DBSubnetGroup":
31146
+ return this.deleteDBSubnetGroup(logicalId, physicalId, resourceType, context);
31147
+ case "AWS::Neptune::DBCluster":
31148
+ return this.deleteDBCluster(logicalId, physicalId, resourceType, context);
31149
+ case "AWS::Neptune::DBInstance":
31150
+ return this.deleteDBInstance(logicalId, physicalId, resourceType, context);
31151
+ default:
31152
+ throw new ProvisioningError(
31153
+ `Unsupported resource type: ${resourceType}`,
31154
+ resourceType,
31155
+ logicalId,
31156
+ physicalId
31157
+ );
31158
+ }
31159
+ }
31160
+ // ─── DBSubnetGroup ────────────────────────────────────────────────
31161
+ async createDBSubnetGroup(logicalId, resourceType, properties) {
31162
+ this.logger.debug(`Creating Neptune DBSubnetGroup ${logicalId}`);
31163
+ const dbSubnetGroupName = properties["DBSubnetGroupName"] || generateResourceName(logicalId, { maxLength: 255, lowercase: true });
31164
+ try {
31165
+ const tags = this.buildTags(properties);
31166
+ await this.getClient().send(
31167
+ new CreateDBSubnetGroupCommand3({
31168
+ DBSubnetGroupName: dbSubnetGroupName,
31169
+ DBSubnetGroupDescription: properties["DBSubnetGroupDescription"] || `Subnet group for ${logicalId}`,
31170
+ SubnetIds: properties["SubnetIds"],
31171
+ ...tags.length > 0 && { Tags: tags }
31172
+ })
31173
+ );
31174
+ this.logger.debug(
31175
+ `Successfully created Neptune DBSubnetGroup ${logicalId}: ${dbSubnetGroupName}`
31176
+ );
31177
+ return {
31178
+ physicalId: dbSubnetGroupName,
31179
+ attributes: {
31180
+ DBSubnetGroupName: dbSubnetGroupName
31181
+ }
31182
+ };
31183
+ } catch (error) {
31184
+ const cause = error instanceof Error ? error : void 0;
31185
+ throw new ProvisioningError(
31186
+ `Failed to create Neptune DBSubnetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31187
+ resourceType,
31188
+ logicalId,
31189
+ dbSubnetGroupName,
31190
+ cause
31191
+ );
31192
+ }
31193
+ }
31194
+ async updateDBSubnetGroup(logicalId, physicalId, resourceType, properties, previousProperties) {
31195
+ this.logger.debug(`Updating Neptune DBSubnetGroup ${logicalId}: ${physicalId}`);
31196
+ try {
31197
+ const subnetIds = properties["SubnetIds"];
31198
+ const sendSubnetIds = subnetIds !== void 0 && subnetIds.length > 0;
31199
+ const modifyInput = {
31200
+ DBSubnetGroupName: physicalId,
31201
+ DBSubnetGroupDescription: properties["DBSubnetGroupDescription"],
31202
+ ...sendSubnetIds && { SubnetIds: subnetIds }
31203
+ };
31204
+ await this.getClient().send(new ModifyDBSubnetGroupCommand3(modifyInput));
31205
+ const desc = await this.getClient().send(
31206
+ new DescribeDBSubnetGroupsCommand3({ DBSubnetGroupName: physicalId })
31207
+ );
31208
+ const arn = desc.DBSubnetGroups?.[0]?.DBSubnetGroupArn;
31209
+ if (arn) {
31210
+ await this.applyTagDiff(
31211
+ arn,
31212
+ previousProperties["Tags"],
31213
+ properties["Tags"]
31214
+ );
31215
+ }
31216
+ this.logger.debug(`Successfully updated Neptune DBSubnetGroup ${logicalId}`);
31217
+ return {
31218
+ physicalId,
31219
+ wasReplaced: false,
31220
+ attributes: {
31221
+ DBSubnetGroupName: physicalId
31222
+ }
31223
+ };
31224
+ } catch (error) {
31225
+ const cause = error instanceof Error ? error : void 0;
31226
+ throw new ProvisioningError(
31227
+ `Failed to update Neptune DBSubnetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31228
+ resourceType,
31229
+ logicalId,
31230
+ physicalId,
31231
+ cause
31232
+ );
31233
+ }
31234
+ }
31235
+ async deleteDBSubnetGroup(logicalId, physicalId, resourceType, context) {
31236
+ this.logger.debug(`Deleting Neptune DBSubnetGroup ${logicalId}: ${physicalId}`);
31237
+ try {
31238
+ await this.getClient().send(
31239
+ new DeleteDBSubnetGroupCommand3({
31240
+ DBSubnetGroupName: physicalId
31241
+ })
31242
+ );
31243
+ this.logger.debug(`Successfully deleted Neptune DBSubnetGroup ${logicalId}`);
31244
+ } catch (error) {
31245
+ if (this.isNotFoundError(error, "DBSubnetGroupNotFoundFault")) {
31246
+ const clientRegion = await this.getClient().config.region();
31247
+ assertRegionMatch(
31248
+ clientRegion,
31249
+ context?.expectedRegion,
31250
+ resourceType,
31251
+ logicalId,
31252
+ physicalId
31253
+ );
31254
+ this.logger.debug(`Neptune DBSubnetGroup ${physicalId} does not exist, skipping deletion`);
31255
+ return;
31256
+ }
31257
+ const cause = error instanceof Error ? error : void 0;
31258
+ throw new ProvisioningError(
31259
+ `Failed to delete Neptune DBSubnetGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31260
+ resourceType,
31261
+ logicalId,
31262
+ physicalId,
31263
+ cause
31264
+ );
31265
+ }
31266
+ }
31267
+ // ─── DBCluster ────────────────────────────────────────────────────
31268
+ async createDBCluster(logicalId, resourceType, properties) {
31269
+ this.logger.debug(`Creating Neptune DBCluster ${logicalId}`);
31270
+ const dbClusterIdentifier = properties["DBClusterIdentifier"] || generateResourceName(logicalId, { maxLength: 63, lowercase: true });
31271
+ try {
31272
+ const tags = this.buildTags(properties);
31273
+ const response = await this.getClient().send(
31274
+ new CreateDBClusterCommand3({
31275
+ DBClusterIdentifier: dbClusterIdentifier,
31276
+ // Neptune engine value is fixed: only `neptune` is accepted.
31277
+ Engine: "neptune",
31278
+ EngineVersion: properties["EngineVersion"],
31279
+ Port: properties["Port"] != null ? Number(properties["Port"]) : void 0,
31280
+ VpcSecurityGroupIds: properties["VpcSecurityGroupIds"],
31281
+ DBSubnetGroupName: properties["DBSubnetGroupName"],
31282
+ StorageEncrypted: properties["StorageEncrypted"],
31283
+ KmsKeyId: properties["KmsKeyId"],
31284
+ BackupRetentionPeriod: properties["BackupRetentionPeriod"] != null ? Number(properties["BackupRetentionPeriod"]) : void 0,
31285
+ PreferredBackupWindow: properties["PreferredBackupWindow"],
31286
+ PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
31287
+ DBClusterParameterGroupName: properties["DBClusterParameterGroupName"],
31288
+ EnableIAMDatabaseAuthentication: properties["IamAuthEnabled"],
31289
+ DeletionProtection: properties["DeletionProtection"],
31290
+ ...tags.length > 0 && { Tags: tags }
31291
+ })
31292
+ );
31293
+ const cluster = response.DBCluster;
31294
+ if (!cluster) {
31295
+ throw new Error("CreateDBCluster did not return DBCluster");
31296
+ }
31297
+ this.logger.debug(
31298
+ `Successfully created Neptune DBCluster ${logicalId}: ${dbClusterIdentifier}`
31299
+ );
31300
+ if (process.env["CDKD_NO_WAIT"] !== "true") {
31301
+ await this.waitForClusterAvailable(dbClusterIdentifier);
31302
+ }
31303
+ const described = await this.describeDBCluster(dbClusterIdentifier);
31304
+ return {
31305
+ physicalId: dbClusterIdentifier,
31306
+ attributes: {
31307
+ "Endpoint.Address": described?.Endpoint ?? "",
31308
+ "Endpoint.Port": String(described?.Port ?? ""),
31309
+ "ReadEndpoint.Address": described?.ReaderEndpoint ?? "",
31310
+ ClusterResourceId: described?.DbClusterResourceId ?? ""
31311
+ }
31312
+ };
31313
+ } catch (error) {
31314
+ if (error instanceof ProvisioningError)
31315
+ throw error;
31316
+ const cause = error instanceof Error ? error : void 0;
31317
+ throw new ProvisioningError(
31318
+ `Failed to create Neptune DBCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31319
+ resourceType,
31320
+ logicalId,
31321
+ dbClusterIdentifier,
31322
+ cause
31323
+ );
31324
+ }
31325
+ }
31326
+ async updateDBCluster(logicalId, physicalId, resourceType, properties, previousProperties) {
31327
+ this.logger.debug(`Updating Neptune DBCluster ${logicalId}: ${physicalId}`);
31328
+ try {
31329
+ const vpcSgIds = properties["VpcSecurityGroupIds"];
31330
+ const sendVpcSgIds = vpcSgIds !== void 0 && vpcSgIds.length > 0;
31331
+ await this.getClient().send(
31332
+ new ModifyDBClusterCommand3({
31333
+ DBClusterIdentifier: physicalId,
31334
+ EngineVersion: properties["EngineVersion"],
31335
+ DeletionProtection: properties["DeletionProtection"],
31336
+ BackupRetentionPeriod: properties["BackupRetentionPeriod"] != null ? Number(properties["BackupRetentionPeriod"]) : void 0,
31337
+ PreferredBackupWindow: properties["PreferredBackupWindow"],
31338
+ PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
31339
+ DBClusterParameterGroupName: properties["DBClusterParameterGroupName"],
31340
+ EnableIAMDatabaseAuthentication: properties["IamAuthEnabled"],
31341
+ ...sendVpcSgIds && { VpcSecurityGroupIds: vpcSgIds },
31342
+ Port: properties["Port"] != null ? Number(properties["Port"]) : void 0,
31343
+ ApplyImmediately: true
31344
+ })
31345
+ );
31346
+ this.logger.debug(`Successfully updated Neptune DBCluster ${logicalId}`);
31347
+ const described = await this.describeDBCluster(physicalId);
31348
+ if (described?.DBClusterArn) {
31349
+ await this.applyTagDiff(
31350
+ described.DBClusterArn,
31351
+ previousProperties["Tags"],
31352
+ properties["Tags"]
31353
+ );
31354
+ }
31355
+ return {
31356
+ physicalId,
31357
+ wasReplaced: false,
31358
+ attributes: {
31359
+ "Endpoint.Address": described?.Endpoint ?? "",
31360
+ "Endpoint.Port": String(described?.Port ?? ""),
31361
+ "ReadEndpoint.Address": described?.ReaderEndpoint ?? "",
31362
+ ClusterResourceId: described?.DbClusterResourceId ?? ""
31363
+ }
31364
+ };
31365
+ } catch (error) {
31366
+ const cause = error instanceof Error ? error : void 0;
31367
+ throw new ProvisioningError(
31368
+ `Failed to update Neptune DBCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31369
+ resourceType,
31370
+ logicalId,
31371
+ physicalId,
31372
+ cause
31373
+ );
31374
+ }
31375
+ }
31376
+ async deleteDBCluster(logicalId, physicalId, resourceType, context) {
31377
+ this.logger.debug(`Deleting Neptune DBCluster ${logicalId}: ${physicalId}`);
31378
+ try {
31379
+ if (context?.removeProtection === true) {
31380
+ try {
31381
+ await this.getClient().send(
31382
+ new ModifyDBClusterCommand3({
31383
+ DBClusterIdentifier: physicalId,
31384
+ DeletionProtection: false,
31385
+ ApplyImmediately: true
31386
+ })
31387
+ );
31388
+ this.logger.debug(
31389
+ `Disabled DeletionProtection on Neptune DBCluster ${logicalId} before delete`
31390
+ );
31391
+ } catch (disableError) {
31392
+ if (!this.isNotFoundError(disableError, "DBClusterNotFoundFault")) {
31393
+ this.logger.debug(
31394
+ `Could not disable deletion protection for ${physicalId}: ${disableError instanceof Error ? disableError.message : String(disableError)}`
31395
+ );
31396
+ }
31397
+ }
31398
+ }
31399
+ await this.getClient().send(
31400
+ new DeleteDBClusterCommand3({
31401
+ DBClusterIdentifier: physicalId,
31402
+ SkipFinalSnapshot: true
31403
+ })
31404
+ );
31405
+ this.logger.debug(`Successfully initiated deletion of Neptune DBCluster ${logicalId}`);
31406
+ await this.waitForClusterDeleted(physicalId);
31407
+ } catch (error) {
31408
+ if (this.isNotFoundError(error, "DBClusterNotFoundFault")) {
31409
+ const clientRegion = await this.getClient().config.region();
31410
+ assertRegionMatch(
31411
+ clientRegion,
31412
+ context?.expectedRegion,
31413
+ resourceType,
31414
+ logicalId,
31415
+ physicalId
31416
+ );
31417
+ this.logger.debug(`Neptune DBCluster ${physicalId} does not exist, skipping deletion`);
31418
+ return;
31419
+ }
31420
+ const cause = error instanceof Error ? error : void 0;
31421
+ throw new ProvisioningError(
31422
+ `Failed to delete Neptune DBCluster ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31423
+ resourceType,
31424
+ logicalId,
31425
+ physicalId,
31426
+ cause
31427
+ );
31428
+ }
31429
+ }
31430
+ // ─── DBInstance ───────────────────────────────────────────────────
31431
+ async createDBInstance(logicalId, resourceType, properties) {
31432
+ this.logger.debug(`Creating Neptune DBInstance ${logicalId}`);
31433
+ const dbInstanceIdentifier = properties["DBInstanceIdentifier"] || generateResourceName(logicalId, { maxLength: 63, lowercase: true });
31434
+ try {
31435
+ const tags = this.buildTags(properties);
31436
+ const response = await this.getClient().send(
31437
+ new CreateDBInstanceCommand3({
31438
+ DBInstanceIdentifier: dbInstanceIdentifier,
31439
+ DBInstanceClass: properties["DBInstanceClass"],
31440
+ // Neptune engine value is fixed: only `neptune` is accepted.
31441
+ Engine: "neptune",
31442
+ DBClusterIdentifier: properties["DBClusterIdentifier"],
31443
+ DBSubnetGroupName: properties["DBSubnetGroupName"],
31444
+ DBParameterGroupName: properties["DBParameterGroupName"],
31445
+ AvailabilityZone: properties["AvailabilityZone"],
31446
+ PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
31447
+ AutoMinorVersionUpgrade: properties["AutoMinorVersionUpgrade"],
31448
+ DeletionProtection: properties["DeletionProtection"],
31449
+ ...tags.length > 0 && { Tags: tags }
31450
+ })
31451
+ );
31452
+ const instance = response.DBInstance;
31453
+ if (!instance) {
31454
+ throw new Error("CreateDBInstance did not return DBInstance");
31455
+ }
31456
+ this.logger.debug(
31457
+ `Successfully created Neptune DBInstance ${logicalId}: ${dbInstanceIdentifier}`
31458
+ );
31459
+ if (process.env["CDKD_NO_WAIT"] !== "true") {
31460
+ await this.waitForInstanceAvailable(dbInstanceIdentifier);
31461
+ }
31462
+ const described = await this.describeDBInstance(dbInstanceIdentifier);
31463
+ return {
31464
+ physicalId: dbInstanceIdentifier,
31465
+ attributes: {
31466
+ "Endpoint.Address": described?.Endpoint?.Address ?? "",
31467
+ "Endpoint.Port": String(described?.Endpoint?.Port ?? "")
31468
+ }
31469
+ };
31470
+ } catch (error) {
31471
+ if (error instanceof ProvisioningError)
31472
+ throw error;
31473
+ const cause = error instanceof Error ? error : void 0;
31474
+ throw new ProvisioningError(
31475
+ `Failed to create Neptune DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31476
+ resourceType,
31477
+ logicalId,
31478
+ dbInstanceIdentifier,
31479
+ cause
31480
+ );
31481
+ }
31482
+ }
31483
+ async updateDBInstance(logicalId, physicalId, resourceType, properties, previousProperties) {
31484
+ this.logger.debug(`Updating Neptune DBInstance ${logicalId}: ${physicalId}`);
31485
+ try {
31486
+ await this.getClient().send(
31487
+ new ModifyDBInstanceCommand3({
31488
+ DBInstanceIdentifier: physicalId,
31489
+ DBInstanceClass: properties["DBInstanceClass"],
31490
+ DBParameterGroupName: properties["DBParameterGroupName"],
31491
+ PreferredMaintenanceWindow: properties["PreferredMaintenanceWindow"],
31492
+ AutoMinorVersionUpgrade: properties["AutoMinorVersionUpgrade"],
31493
+ DeletionProtection: properties["DeletionProtection"],
31494
+ ApplyImmediately: true
31495
+ })
31496
+ );
31497
+ this.logger.debug(`Successfully updated Neptune DBInstance ${logicalId}`);
31498
+ const described = await this.describeDBInstance(physicalId);
31499
+ if (described?.DBInstanceArn) {
31500
+ await this.applyTagDiff(
31501
+ described.DBInstanceArn,
31502
+ previousProperties["Tags"],
31503
+ properties["Tags"]
31504
+ );
31505
+ }
31506
+ return {
31507
+ physicalId,
31508
+ wasReplaced: false,
31509
+ attributes: {
31510
+ "Endpoint.Address": described?.Endpoint?.Address ?? "",
31511
+ "Endpoint.Port": String(described?.Endpoint?.Port ?? "")
31512
+ }
31513
+ };
31514
+ } catch (error) {
31515
+ const cause = error instanceof Error ? error : void 0;
31516
+ throw new ProvisioningError(
31517
+ `Failed to update Neptune DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31518
+ resourceType,
31519
+ logicalId,
31520
+ physicalId,
31521
+ cause
31522
+ );
31523
+ }
31524
+ }
31525
+ async deleteDBInstance(logicalId, physicalId, resourceType, context) {
31526
+ this.logger.debug(`Deleting Neptune DBInstance ${logicalId}: ${physicalId}`);
31527
+ try {
31528
+ if (context?.removeProtection === true) {
31529
+ try {
31530
+ await this.getClient().send(
31531
+ new ModifyDBInstanceCommand3({
31532
+ DBInstanceIdentifier: physicalId,
31533
+ DeletionProtection: false,
31534
+ ApplyImmediately: true
31535
+ })
31536
+ );
31537
+ this.logger.debug(
31538
+ `Disabled DeletionProtection on Neptune DBInstance ${logicalId} before delete`
31539
+ );
31540
+ } catch (disableError) {
31541
+ if (!this.isNotFoundError(disableError, "DBInstanceNotFoundFault")) {
31542
+ this.logger.debug(
31543
+ `Could not disable deletion protection for ${physicalId}: ${disableError instanceof Error ? disableError.message : String(disableError)}`
31544
+ );
31545
+ }
31546
+ }
31547
+ }
31548
+ await this.getClient().send(
31549
+ new DeleteDBInstanceCommand3({
31550
+ DBInstanceIdentifier: physicalId,
31551
+ SkipFinalSnapshot: true
31552
+ })
31553
+ );
31554
+ this.logger.debug(`Successfully initiated deletion of Neptune DBInstance ${logicalId}`);
31555
+ await this.waitForInstanceDeleted(physicalId);
31556
+ } catch (error) {
31557
+ if (this.isNotFoundError(error, "DBInstanceNotFoundFault")) {
31558
+ const clientRegion = await this.getClient().config.region();
31559
+ assertRegionMatch(
31560
+ clientRegion,
31561
+ context?.expectedRegion,
31562
+ resourceType,
31563
+ logicalId,
31564
+ physicalId
31565
+ );
31566
+ this.logger.debug(`Neptune DBInstance ${physicalId} does not exist, skipping deletion`);
31567
+ return;
31568
+ }
31569
+ const cause = error instanceof Error ? error : void 0;
31570
+ throw new ProvisioningError(
31571
+ `Failed to delete Neptune DBInstance ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
31572
+ resourceType,
31573
+ logicalId,
31574
+ physicalId,
31575
+ cause
31576
+ );
31577
+ }
31578
+ }
31579
+ // ─── Helpers ──────────────────────────────────────────────────────
31580
+ /**
31581
+ * Apply a diff between old and new CFn-shape Tags arrays via Neptune's
31582
+ * `AddTagsToResource` / `RemoveTagsFromResource` APIs (keyed by
31583
+ * `ResourceName=arn`).
31584
+ */
31585
+ async applyTagDiff(arn, oldTagsRaw, newTagsRaw) {
31586
+ const toMap = (tags) => {
31587
+ const m = /* @__PURE__ */ new Map();
31588
+ for (const t of tags ?? []) {
31589
+ if (t.Key !== void 0 && t.Value !== void 0)
31590
+ m.set(t.Key, t.Value);
31591
+ }
31592
+ return m;
31593
+ };
31594
+ const oldMap = toMap(oldTagsRaw);
31595
+ const newMap = toMap(newTagsRaw);
31596
+ const tagsToAdd = [];
31597
+ for (const [k, v] of newMap) {
31598
+ if (oldMap.get(k) !== v)
31599
+ tagsToAdd.push({ Key: k, Value: v });
31600
+ }
31601
+ const tagsToRemove = [];
31602
+ for (const k of oldMap.keys()) {
31603
+ if (!newMap.has(k))
31604
+ tagsToRemove.push(k);
31605
+ }
31606
+ if (tagsToRemove.length > 0) {
31607
+ await this.getClient().send(
31608
+ new RemoveTagsFromResourceCommand4({ ResourceName: arn, TagKeys: tagsToRemove })
31609
+ );
31610
+ this.logger.debug(`Removed ${tagsToRemove.length} tag(s) from Neptune resource ${arn}`);
31611
+ }
31612
+ if (tagsToAdd.length > 0) {
31613
+ await this.getClient().send(
31614
+ new AddTagsToResourceCommand4({ ResourceName: arn, Tags: tagsToAdd })
31615
+ );
31616
+ this.logger.debug(`Added/updated ${tagsToAdd.length} tag(s) on Neptune resource ${arn}`);
31617
+ }
31618
+ }
31619
+ buildTags(properties) {
31620
+ if (!properties["Tags"])
31621
+ return [];
31622
+ return properties["Tags"];
31623
+ }
31624
+ isNotFoundError(error, faultName) {
31625
+ if (!(error instanceof Error))
31626
+ return false;
31627
+ const name = error.name ?? "";
31628
+ const message = error.message.toLowerCase();
31629
+ return name === faultName || message.includes("not found") || message.includes("does not exist");
31630
+ }
31631
+ async describeDBCluster(dbClusterIdentifier) {
31632
+ const response = await this.getClient().send(
31633
+ new DescribeDBClustersCommand3({
31634
+ DBClusterIdentifier: dbClusterIdentifier
31635
+ })
31636
+ );
31637
+ return response.DBClusters?.[0];
31638
+ }
31639
+ async describeDBInstance(dbInstanceIdentifier) {
31640
+ const response = await this.getClient().send(
31641
+ new DescribeDBInstancesCommand3({
31642
+ DBInstanceIdentifier: dbInstanceIdentifier
31643
+ })
31644
+ );
31645
+ return response.DBInstances?.[0];
31646
+ }
31647
+ /**
31648
+ * Wait for a DBCluster to become available (no SDK waiter — manual poll).
31649
+ */
31650
+ async waitForClusterAvailable(dbClusterIdentifier, maxWaitMs = 18e5) {
31651
+ const startTime = Date.now();
31652
+ let delay = 5e3;
31653
+ while (Date.now() - startTime < maxWaitMs) {
31654
+ const cluster = await this.describeDBCluster(dbClusterIdentifier);
31655
+ const status = cluster?.Status;
31656
+ this.logger.debug(`Neptune DBCluster ${dbClusterIdentifier} status: ${status}`);
31657
+ if (status === "available")
31658
+ return;
31659
+ await this.sleep(delay);
31660
+ delay = Math.min(delay * 2, 3e4);
31661
+ }
31662
+ throw new Error(
31663
+ `Timed out waiting for Neptune DBCluster ${dbClusterIdentifier} to become available`
31664
+ );
31665
+ }
31666
+ /**
31667
+ * Wait for a DBCluster to be deleted (no SDK waiter — manual poll).
31668
+ */
31669
+ async waitForClusterDeleted(dbClusterIdentifier, maxWaitMs = 18e5) {
31670
+ const startTime = Date.now();
31671
+ let delay = 5e3;
31672
+ while (Date.now() - startTime < maxWaitMs) {
31673
+ try {
31674
+ const cluster = await this.describeDBCluster(dbClusterIdentifier);
31675
+ const status = cluster?.Status;
31676
+ this.logger.debug(`Neptune DBCluster ${dbClusterIdentifier} status: ${status}`);
31677
+ if (!cluster)
31678
+ return;
31679
+ } catch (error) {
31680
+ if (this.isNotFoundError(error, "DBClusterNotFoundFault")) {
31681
+ return;
31682
+ }
31683
+ throw error;
31684
+ }
31685
+ await this.sleep(delay);
31686
+ delay = Math.min(delay * 2, 3e4);
31687
+ }
31688
+ throw new Error(`Timed out waiting for Neptune DBCluster ${dbClusterIdentifier} to be deleted`);
31689
+ }
31690
+ /**
31691
+ * Wait for a DBInstance to become available (manual poll).
31692
+ */
31693
+ async waitForInstanceAvailable(dbInstanceIdentifier, maxWaitMs = 18e5) {
31694
+ const startTime = Date.now();
31695
+ let delay = 1e4;
31696
+ while (Date.now() - startTime < maxWaitMs) {
31697
+ const instance = await this.describeDBInstance(dbInstanceIdentifier);
31698
+ const status = instance?.DBInstanceStatus;
31699
+ this.logger.debug(`Neptune DBInstance ${dbInstanceIdentifier} status: ${status}`);
31700
+ if (status === "available")
31701
+ return;
31702
+ await this.sleep(delay);
31703
+ delay = Math.min(delay * 2, 3e4);
31704
+ }
31705
+ throw new Error(
31706
+ `Timed out waiting for Neptune DBInstance ${dbInstanceIdentifier} to become available`
31707
+ );
31708
+ }
31709
+ async waitForInstanceDeleted(dbInstanceIdentifier, maxWaitMs = 18e5) {
31710
+ const startTime = Date.now();
31711
+ let delay = 1e4;
31712
+ while (Date.now() - startTime < maxWaitMs) {
31713
+ try {
31714
+ const instance = await this.describeDBInstance(dbInstanceIdentifier);
31715
+ const status = instance?.DBInstanceStatus;
31716
+ this.logger.debug(`Neptune DBInstance ${dbInstanceIdentifier} status: ${status}`);
31717
+ if (!instance)
31718
+ return;
31719
+ } catch (error) {
31720
+ if (this.isNotFoundError(error, "DBInstanceNotFoundFault")) {
31721
+ return;
31722
+ }
31723
+ throw error;
31724
+ }
31725
+ await this.sleep(delay);
31726
+ delay = Math.min(delay * 2, 3e4);
31727
+ }
31728
+ throw new Error(
31729
+ `Timed out waiting for Neptune DBInstance ${dbInstanceIdentifier} to be deleted`
31730
+ );
31731
+ }
31732
+ sleep(ms) {
31733
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
31734
+ }
31735
+ /**
31736
+ * Adopt an existing Neptune resource into cdkd state.
31737
+ */
31738
+ async import(input) {
31739
+ switch (input.resourceType) {
31740
+ case "AWS::Neptune::DBInstance":
31741
+ return this.importDBInstance(input);
31742
+ case "AWS::Neptune::DBCluster":
31743
+ return this.importDBCluster(input);
31744
+ case "AWS::Neptune::DBSubnetGroup":
31745
+ return this.importDBSubnetGroup(input);
31746
+ default:
31747
+ return null;
31748
+ }
31749
+ }
31750
+ /**
31751
+ * Read the AWS-current Neptune resource configuration in CFn-property
31752
+ * shape. Each branch surfaces only the keys cdkd's `create()` accepts.
31753
+ * Sensitive fields are NEVER surfaced. `Tags` are surfaced via a
31754
+ * follow-up `ListTagsForResource(ResourceName=arn)` call.
31755
+ */
31756
+ async readCurrentState(physicalId, _logicalId, resourceType) {
31757
+ switch (resourceType) {
31758
+ case "AWS::Neptune::DBInstance":
31759
+ return this.readCurrentStateDBInstance(physicalId);
31760
+ case "AWS::Neptune::DBCluster":
31761
+ return this.readCurrentStateDBCluster(physicalId);
31762
+ case "AWS::Neptune::DBSubnetGroup":
31763
+ return this.readCurrentStateDBSubnetGroup(physicalId);
31764
+ default:
31765
+ return void 0;
31766
+ }
31767
+ }
31768
+ async readCurrentStateDBInstance(physicalId) {
31769
+ let inst;
31770
+ try {
31771
+ inst = await this.describeDBInstance(physicalId);
31772
+ } catch (err) {
31773
+ if (this.isNotFoundError(err, "DBInstanceNotFoundFault"))
31774
+ return void 0;
31775
+ throw err;
31776
+ }
31777
+ if (!inst)
31778
+ return void 0;
31779
+ const result = {};
31780
+ if (inst.DBInstanceIdentifier !== void 0) {
31781
+ result["DBInstanceIdentifier"] = inst.DBInstanceIdentifier;
31782
+ }
31783
+ if (inst.DBInstanceClass !== void 0)
31784
+ result["DBInstanceClass"] = inst.DBInstanceClass;
31785
+ if (inst.DBClusterIdentifier !== void 0) {
31786
+ result["DBClusterIdentifier"] = inst.DBClusterIdentifier;
31787
+ }
31788
+ if (inst.DBSubnetGroup?.DBSubnetGroupName !== void 0) {
31789
+ result["DBSubnetGroupName"] = inst.DBSubnetGroup.DBSubnetGroupName;
31790
+ }
31791
+ if (inst.AvailabilityZone !== void 0)
31792
+ result["AvailabilityZone"] = inst.AvailabilityZone;
31793
+ if (inst.PreferredMaintenanceWindow !== void 0) {
31794
+ result["PreferredMaintenanceWindow"] = inst.PreferredMaintenanceWindow;
31795
+ }
31796
+ if (inst.AutoMinorVersionUpgrade !== void 0) {
31797
+ result["AutoMinorVersionUpgrade"] = inst.AutoMinorVersionUpgrade;
31798
+ }
31799
+ if (inst.DeletionProtection !== void 0) {
31800
+ result["DeletionProtection"] = inst.DeletionProtection;
31801
+ }
31802
+ const pg = inst.DBParameterGroups?.[0]?.DBParameterGroupName;
31803
+ if (pg !== void 0)
31804
+ result["DBParameterGroupName"] = pg;
31805
+ if (inst.DBInstanceArn)
31806
+ await this.attachTags(result, inst.DBInstanceArn);
31807
+ return result;
31808
+ }
31809
+ async readCurrentStateDBCluster(physicalId) {
31810
+ let cluster;
31811
+ try {
31812
+ cluster = await this.describeDBCluster(physicalId);
31813
+ } catch (err) {
31814
+ if (this.isNotFoundError(err, "DBClusterNotFoundFault"))
31815
+ return void 0;
31816
+ throw err;
31817
+ }
31818
+ if (!cluster)
31819
+ return void 0;
31820
+ const result = {};
31821
+ if (cluster.DBClusterIdentifier !== void 0) {
31822
+ result["DBClusterIdentifier"] = cluster.DBClusterIdentifier;
31823
+ }
31824
+ if (cluster.EngineVersion !== void 0)
31825
+ result["EngineVersion"] = cluster.EngineVersion;
31826
+ if (cluster.Port !== void 0)
31827
+ result["Port"] = cluster.Port;
31828
+ result["VpcSecurityGroupIds"] = (cluster.VpcSecurityGroups ?? []).map((sg) => sg.VpcSecurityGroupId).filter((id) => !!id);
31829
+ if (cluster.DBSubnetGroup !== void 0)
31830
+ result["DBSubnetGroupName"] = cluster.DBSubnetGroup;
31831
+ if (cluster.StorageEncrypted !== void 0) {
31832
+ result["StorageEncrypted"] = cluster.StorageEncrypted;
31833
+ }
31834
+ if (cluster.KmsKeyId !== void 0)
31835
+ result["KmsKeyId"] = cluster.KmsKeyId;
31836
+ if (cluster.BackupRetentionPeriod !== void 0) {
31837
+ result["BackupRetentionPeriod"] = cluster.BackupRetentionPeriod;
31838
+ }
31839
+ if (cluster.PreferredBackupWindow !== void 0) {
31840
+ result["PreferredBackupWindow"] = cluster.PreferredBackupWindow;
31841
+ }
31842
+ if (cluster.PreferredMaintenanceWindow !== void 0) {
31843
+ result["PreferredMaintenanceWindow"] = cluster.PreferredMaintenanceWindow;
31844
+ }
31845
+ if (cluster.DBClusterParameterGroup !== void 0) {
31846
+ result["DBClusterParameterGroupName"] = cluster.DBClusterParameterGroup;
31847
+ }
31848
+ if (cluster.IAMDatabaseAuthenticationEnabled !== void 0) {
31849
+ result["IamAuthEnabled"] = cluster.IAMDatabaseAuthenticationEnabled;
31850
+ }
31851
+ if (cluster.DeletionProtection !== void 0) {
31852
+ result["DeletionProtection"] = cluster.DeletionProtection;
31853
+ }
31854
+ if (cluster.DBClusterArn)
31855
+ await this.attachTags(result, cluster.DBClusterArn);
31856
+ return result;
31857
+ }
31858
+ async readCurrentStateDBSubnetGroup(physicalId) {
31859
+ let resp;
31860
+ try {
31861
+ resp = await this.getClient().send(
31862
+ new DescribeDBSubnetGroupsCommand3({ DBSubnetGroupName: physicalId })
31863
+ );
31864
+ } catch (err) {
31865
+ if (this.isNotFoundError(err, "DBSubnetGroupNotFoundFault"))
31866
+ return void 0;
31867
+ throw err;
31868
+ }
31869
+ const sg = resp.DBSubnetGroups?.[0];
31870
+ if (!sg)
31871
+ return void 0;
31872
+ const result = {};
31873
+ if (sg.DBSubnetGroupName !== void 0)
31874
+ result["DBSubnetGroupName"] = sg.DBSubnetGroupName;
31875
+ if (sg.DBSubnetGroupDescription !== void 0) {
31876
+ result["DBSubnetGroupDescription"] = sg.DBSubnetGroupDescription;
31877
+ }
31878
+ result["SubnetIds"] = (sg.Subnets ?? []).map((s) => s.SubnetIdentifier).filter((id) => !!id);
31879
+ if (sg.DBSubnetGroupArn)
31880
+ await this.attachTags(result, sg.DBSubnetGroupArn);
31881
+ return result;
31882
+ }
31883
+ /**
31884
+ * Fetch tags via `ListTagsForResource` and merge them into the result
31885
+ * under `Tags` (CFn shape). Best-effort.
31886
+ */
31887
+ async attachTags(result, arn) {
31888
+ try {
31889
+ const tagsResp = await this.getClient().send(
31890
+ new ListTagsForResourceCommand12({ ResourceName: arn })
31891
+ );
31892
+ const tags = normalizeAwsTagsToCfn(tagsResp.TagList);
31893
+ result["Tags"] = tags;
31894
+ } catch (err) {
31895
+ this.logger.debug(
31896
+ `Neptune ListTagsForResource(${arn}) failed: ${err instanceof Error ? err.message : String(err)}`
31897
+ );
31898
+ }
31899
+ }
31900
+ async importDBInstance(input) {
31901
+ const explicit = resolveExplicitPhysicalId(input, "DBInstanceIdentifier");
31902
+ if (explicit) {
31903
+ try {
31904
+ await this.getClient().send(
31905
+ new DescribeDBInstancesCommand3({ DBInstanceIdentifier: explicit })
31906
+ );
31907
+ return { physicalId: explicit, attributes: {} };
31908
+ } catch (err) {
31909
+ if (err.name === "DBInstanceNotFoundFault")
31910
+ return null;
31911
+ throw err;
31912
+ }
31913
+ }
31914
+ if (!input.cdkPath)
31915
+ return null;
31916
+ let marker;
31917
+ do {
31918
+ const list = await this.getClient().send(
31919
+ new DescribeDBInstancesCommand3({ ...marker && { Marker: marker } })
31920
+ );
31921
+ for (const inst of list.DBInstances ?? []) {
31922
+ if (!inst.DBInstanceIdentifier || !inst.DBInstanceArn)
31923
+ continue;
31924
+ const tagsResp = await this.getClient().send(
31925
+ new ListTagsForResourceCommand12({ ResourceName: inst.DBInstanceArn })
31926
+ );
31927
+ if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
31928
+ return { physicalId: inst.DBInstanceIdentifier, attributes: {} };
31929
+ }
31930
+ }
31931
+ marker = list.Marker;
31932
+ } while (marker);
31933
+ return null;
31934
+ }
31935
+ async importDBCluster(input) {
31936
+ const explicit = resolveExplicitPhysicalId(input, "DBClusterIdentifier");
31937
+ if (explicit) {
31938
+ try {
31939
+ await this.getClient().send(
31940
+ new DescribeDBClustersCommand3({ DBClusterIdentifier: explicit })
31941
+ );
31942
+ return { physicalId: explicit, attributes: {} };
31943
+ } catch (err) {
31944
+ if (err.name === "DBClusterNotFoundFault")
31945
+ return null;
31946
+ throw err;
31947
+ }
31948
+ }
31949
+ if (!input.cdkPath)
31950
+ return null;
31951
+ let marker;
31952
+ do {
31953
+ const list = await this.getClient().send(
31954
+ new DescribeDBClustersCommand3({ ...marker && { Marker: marker } })
31955
+ );
31956
+ for (const c of list.DBClusters ?? []) {
31957
+ if (!c.DBClusterIdentifier || !c.DBClusterArn)
31958
+ continue;
31959
+ const tagsResp = await this.getClient().send(
31960
+ new ListTagsForResourceCommand12({ ResourceName: c.DBClusterArn })
31961
+ );
31962
+ if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
31963
+ return { physicalId: c.DBClusterIdentifier, attributes: {} };
31964
+ }
31965
+ }
31966
+ marker = list.Marker;
31967
+ } while (marker);
31968
+ return null;
31969
+ }
31970
+ async importDBSubnetGroup(input) {
31971
+ const explicit = resolveExplicitPhysicalId(input, "DBSubnetGroupName");
31972
+ if (explicit) {
31973
+ try {
31974
+ await this.getClient().send(
31975
+ new DescribeDBSubnetGroupsCommand3({ DBSubnetGroupName: explicit })
31976
+ );
31977
+ return { physicalId: explicit, attributes: {} };
31978
+ } catch (err) {
31979
+ if (err.name === "DBSubnetGroupNotFoundFault")
31980
+ return null;
31981
+ throw err;
31982
+ }
31983
+ }
31984
+ if (!input.cdkPath)
31985
+ return null;
31986
+ let marker;
31987
+ do {
31988
+ const list = await this.getClient().send(
31989
+ new DescribeDBSubnetGroupsCommand3({ ...marker && { Marker: marker } })
31990
+ );
31991
+ for (const sg of list.DBSubnetGroups ?? []) {
31992
+ if (!sg.DBSubnetGroupName || !sg.DBSubnetGroupArn)
31993
+ continue;
31994
+ const tagsResp = await this.getClient().send(
31995
+ new ListTagsForResourceCommand12({ ResourceName: sg.DBSubnetGroupArn })
31996
+ );
31997
+ if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
31998
+ return { physicalId: sg.DBSubnetGroupName, attributes: {} };
31999
+ }
32000
+ }
32001
+ marker = list.Marker;
32002
+ } while (marker);
32003
+ return null;
32004
+ }
32005
+ };
32006
+
32007
+ // src/provisioning/providers/route53-provider.ts
32008
+ import {
32009
+ Route53Client as Route53Client2,
32010
+ CreateHostedZoneCommand,
32011
+ DeleteHostedZoneCommand,
32012
+ GetHostedZoneCommand as GetHostedZoneCommand2,
32013
+ ChangeResourceRecordSetsCommand,
32014
+ UpdateHostedZoneCommentCommand,
32015
+ ChangeTagsForResourceCommand,
32016
+ AssociateVPCWithHostedZoneCommand,
32017
+ DisassociateVPCFromHostedZoneCommand,
32018
+ CreateQueryLoggingConfigCommand,
32019
+ DeleteQueryLoggingConfigCommand,
32020
+ ListQueryLoggingConfigsCommand,
32021
+ ListHostedZonesByNameCommand as ListHostedZonesByNameCommand2,
32022
+ ListHostedZonesCommand,
32023
+ ListResourceRecordSetsCommand,
32024
+ ListTagsForResourceCommand as ListTagsForResourceCommand13
32025
+ } from "@aws-sdk/client-route-53";
32026
+ var Route53Provider = class {
32027
+ route53Client;
32028
+ providerRegion = process.env["AWS_REGION"];
32029
+ logger = getLogger().child("Route53Provider");
32030
+ handledProperties = /* @__PURE__ */ new Map([
32031
+ [
32032
+ "AWS::Route53::HostedZone",
32033
+ /* @__PURE__ */ new Set(["Name", "HostedZoneConfig", "HostedZoneTags", "VPCs", "QueryLoggingConfig"])
32034
+ ],
32035
+ [
32036
+ "AWS::Route53::RecordSet",
32037
+ /* @__PURE__ */ new Set([
32038
+ "HostedZoneId",
32039
+ "HostedZoneName",
32040
+ "Name",
32041
+ "Type",
32042
+ "TTL",
32043
+ "ResourceRecords",
32044
+ "AliasTarget",
32045
+ "SetIdentifier",
32046
+ "Weight",
32047
+ "Region",
32048
+ "Failover",
32049
+ "MultiValueAnswer",
32050
+ "HealthCheckId",
32051
+ "Comment",
32052
+ "GeoLocation"
32053
+ ])
32054
+ ]
32055
+ ]);
32056
+ getClient() {
32057
+ if (!this.route53Client) {
32058
+ this.route53Client = new Route53Client2(
32059
+ this.providerRegion ? { region: this.providerRegion } : {}
32060
+ );
32061
+ }
32062
+ return this.route53Client;
32063
+ }
32064
+ // ─── Dispatch ─────────────────────────────────────────────────────
32065
+ async create(logicalId, resourceType, properties) {
32066
+ switch (resourceType) {
32067
+ case "AWS::Route53::HostedZone":
32068
+ return this.createHostedZone(logicalId, resourceType, properties);
32069
+ case "AWS::Route53::RecordSet":
32070
+ return this.createRecordSet(logicalId, resourceType, properties);
32071
+ default:
32072
+ throw new ProvisioningError(
32073
+ `Unsupported resource type: ${resourceType}`,
32074
+ resourceType,
32075
+ logicalId
32076
+ );
32077
+ }
32078
+ }
32079
+ async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
32080
+ switch (resourceType) {
32081
+ case "AWS::Route53::HostedZone":
32082
+ return this.updateHostedZone(logicalId, physicalId, resourceType, properties);
32083
+ case "AWS::Route53::RecordSet":
32084
+ return this.updateRecordSet(logicalId, physicalId, resourceType, properties);
32085
+ default:
32086
+ throw new ProvisioningError(
32087
+ `Unsupported resource type: ${resourceType}`,
32088
+ resourceType,
32089
+ logicalId,
32090
+ physicalId
32091
+ );
32092
+ }
32093
+ }
32094
+ async delete(logicalId, physicalId, resourceType, properties, context) {
32095
+ switch (resourceType) {
32096
+ case "AWS::Route53::HostedZone":
32097
+ return this.deleteHostedZone(logicalId, physicalId, resourceType, context);
32098
+ case "AWS::Route53::RecordSet":
32099
+ return this.deleteRecordSet(logicalId, physicalId, resourceType, properties, context);
32100
+ default:
32101
+ throw new ProvisioningError(
32102
+ `Unsupported resource type: ${resourceType}`,
32103
+ resourceType,
32104
+ logicalId,
32105
+ physicalId
32106
+ );
32107
+ }
32108
+ }
32109
+ async getAttribute(physicalId, resourceType, attributeName) {
32110
+ switch (resourceType) {
32111
+ case "AWS::Route53::HostedZone":
32112
+ return this.getHostedZoneAttribute(physicalId, attributeName);
32113
+ default:
32114
+ return void 0;
32115
+ }
32116
+ }
32117
+ // ─── AWS::Route53::HostedZone ──────────────────────────────────────
32118
+ async createHostedZone(logicalId, resourceType, properties) {
32119
+ this.logger.debug(`Creating Route 53 hosted zone ${logicalId}`);
32120
+ const name = properties["Name"];
32121
+ if (!name) {
32122
+ throw new ProvisioningError(
32123
+ `Name is required for hosted zone ${logicalId}`,
32124
+ resourceType,
32125
+ logicalId
32126
+ );
32127
+ }
32128
+ try {
32129
+ const hostedZoneConfig = properties["HostedZoneConfig"];
32130
+ const vpcs = properties["VPCs"];
32131
+ const firstVpc = vpcs && vpcs.length > 0 ? vpcs[0] : void 0;
32132
+ const response = await this.getClient().send(
32133
+ new CreateHostedZoneCommand({
32134
+ Name: name,
32135
+ CallerReference: `${logicalId}-${Date.now()}`,
32136
+ ...hostedZoneConfig && hostedZoneConfig["Comment"] ? {
32137
+ HostedZoneConfig: {
32138
+ Comment: hostedZoneConfig["Comment"],
32139
+ // When VPCs are specified, this is a private hosted zone
32140
+ ...firstVpc ? { PrivateZone: true } : {}
32141
+ }
32142
+ } : firstVpc ? { HostedZoneConfig: { PrivateZone: true } } : {},
32143
+ ...firstVpc ? {
32144
+ VPC: {
32145
+ VPCId: firstVpc["VPCId"],
32146
+ VPCRegion: firstVpc["VPCRegion"]
32147
+ }
32148
+ } : {}
32149
+ })
32150
+ );
32151
+ const hostedZone = response.HostedZone;
32152
+ if (!hostedZone?.Id) {
32153
+ throw new Error("CreateHostedZone did not return HostedZone.Id");
32154
+ }
32155
+ const zoneId = hostedZone.Id.replace("/hostedzone/", "");
32156
+ if (vpcs && vpcs.length > 1) {
32157
+ for (let i = 1; i < vpcs.length; i++) {
32158
+ const additionalVpc = vpcs[i];
32159
+ this.logger.debug(
32160
+ `Associating additional VPC ${String(additionalVpc["VPCId"])} with hosted zone ${zoneId}`
32161
+ );
32162
+ await this.getClient().send(
32163
+ new AssociateVPCWithHostedZoneCommand({
32164
+ HostedZoneId: zoneId,
32165
+ VPC: {
32166
+ VPCId: additionalVpc["VPCId"],
32167
+ VPCRegion: additionalVpc["VPCRegion"]
32168
+ }
32169
+ })
32170
+ );
32171
+ }
32172
+ }
32173
+ await this.applyHostedZoneTags(zoneId, properties, logicalId);
32174
+ await this.applyQueryLoggingConfig(zoneId, properties, logicalId);
32175
+ const nameServers = response.DelegationSet?.NameServers ?? [];
32176
+ this.logger.debug(`Successfully created hosted zone ${logicalId}: ${zoneId}`);
32177
+ return {
32178
+ physicalId: zoneId,
32179
+ attributes: {
32180
+ Id: zoneId,
32181
+ NameServers: nameServers.join(",")
32182
+ }
32183
+ };
32184
+ } catch (error) {
32185
+ if (error instanceof ProvisioningError)
32186
+ throw error;
32187
+ const cause = error instanceof Error ? error : void 0;
32188
+ throw new ProvisioningError(
32189
+ `Failed to create hosted zone ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
32190
+ resourceType,
32191
+ logicalId,
32192
+ void 0,
32193
+ cause
32194
+ );
32195
+ }
32196
+ }
32197
+ async updateHostedZone(logicalId, physicalId, resourceType, properties) {
32198
+ this.logger.debug(`Updating Route 53 hosted zone ${logicalId}: ${physicalId}`);
32199
+ try {
32200
+ const hostedZoneConfig = properties["HostedZoneConfig"];
32201
+ const comment = hostedZoneConfig?.["Comment"] ?? "";
32202
+ await this.getClient().send(
32203
+ new UpdateHostedZoneCommentCommand({
32204
+ Id: physicalId,
32205
+ Comment: comment
32206
+ })
32207
+ );
32208
+ await this.applyHostedZoneTags(physicalId, properties, logicalId);
32209
+ await this.applyQueryLoggingConfig(physicalId, properties, logicalId);
32210
+ await this.syncVPCAssociations(physicalId, properties, logicalId);
32211
+ const getResponse = await this.getClient().send(new GetHostedZoneCommand2({ Id: physicalId }));
32212
+ const nameServers = getResponse.DelegationSet?.NameServers ?? [];
32213
+ this.logger.debug(`Successfully updated hosted zone ${logicalId}`);
32214
+ return {
32215
+ physicalId,
32216
+ wasReplaced: false,
32217
+ attributes: {
32218
+ Id: physicalId,
32219
+ NameServers: nameServers.join(",")
32220
+ }
32221
+ };
32222
+ } catch (error) {
32223
+ const cause = error instanceof Error ? error : void 0;
32224
+ throw new ProvisioningError(
32225
+ `Failed to update hosted zone ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
32226
+ resourceType,
32227
+ logicalId,
32228
+ physicalId,
32229
+ cause
32230
+ );
32231
+ }
32232
+ }
32233
+ async deleteHostedZone(logicalId, physicalId, resourceType, context) {
32234
+ this.logger.debug(`Deleting Route 53 hosted zone ${logicalId}: ${physicalId}`);
32235
+ try {
32236
+ await this.deleteQueryLoggingConfigForZone(physicalId, logicalId);
32237
+ await this.getClient().send(new DeleteHostedZoneCommand({ Id: physicalId }));
32238
+ this.logger.debug(`Successfully deleted hosted zone ${logicalId}`);
32239
+ } catch (error) {
32240
+ if (error instanceof Error && error.name === "NoSuchHostedZone") {
32241
+ const clientRegion = await this.getClient().config.region();
32242
+ assertRegionMatch(
32243
+ clientRegion,
32244
+ context?.expectedRegion,
32245
+ resourceType,
32246
+ logicalId,
32247
+ physicalId
32248
+ );
32249
+ this.logger.debug(`Hosted zone ${physicalId} does not exist, skipping deletion`);
32250
+ return;
32251
+ }
32252
+ const cause = error instanceof Error ? error : void 0;
32253
+ throw new ProvisioningError(
32254
+ `Failed to delete hosted zone ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
32255
+ resourceType,
32256
+ logicalId,
32257
+ physicalId,
32258
+ cause
32259
+ );
32260
+ }
32261
+ }
32262
+ async getHostedZoneAttribute(physicalId, attributeName) {
32263
+ switch (attributeName) {
32264
+ case "Id":
32265
+ return physicalId;
32266
+ case "NameServers": {
32267
+ const response = await this.getClient().send(new GetHostedZoneCommand2({ Id: physicalId }));
32268
+ return (response.DelegationSet?.NameServers ?? []).join(",");
32269
+ }
32270
+ default:
32271
+ return void 0;
32272
+ }
32273
+ }
32274
+ // ─── AWS::Route53::RecordSet ───────────────────────────────────────
32275
+ async createRecordSet(logicalId, resourceType, properties) {
32276
+ this.logger.debug(`Creating Route 53 record set ${logicalId}`);
32277
+ const hostedZoneId = await this.resolveHostedZoneId(properties, logicalId, resourceType);
32278
+ const recordName = properties["Name"];
32279
+ const recordType = properties["Type"];
32280
+ try {
32281
+ const resourceRecordSet = this.buildResourceRecordSet(properties);
32282
+ const comment = properties["Comment"];
32283
+ await this.getClient().send(
32284
+ new ChangeResourceRecordSetsCommand({
32285
+ HostedZoneId: hostedZoneId,
32286
+ ChangeBatch: {
32287
+ ...comment ? { Comment: comment } : {},
32288
+ Changes: [
32289
+ {
32290
+ Action: "CREATE",
32291
+ ResourceRecordSet: resourceRecordSet
32292
+ }
32293
+ ]
32294
+ }
32295
+ })
32296
+ );
32297
+ const compositeId = `${hostedZoneId}|${recordName}|${recordType}`;
32298
+ this.logger.debug(`Successfully created record set ${logicalId}: ${compositeId}`);
32299
+ return {
32300
+ physicalId: compositeId,
32301
+ attributes: {}
32302
+ };
32303
+ } catch (error) {
32304
+ if (error instanceof ProvisioningError)
32305
+ throw error;
32306
+ const cause = error instanceof Error ? error : void 0;
32307
+ throw new ProvisioningError(
32308
+ `Failed to create record set ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
32309
+ resourceType,
32310
+ logicalId,
32311
+ void 0,
32312
+ cause
32313
+ );
32314
+ }
32315
+ }
32316
+ async updateRecordSet(logicalId, physicalId, resourceType, properties) {
32317
+ this.logger.debug(`Updating Route 53 record set ${logicalId}: ${physicalId}`);
32318
+ const hostedZoneId = await this.resolveHostedZoneId(
32319
+ properties,
32320
+ logicalId,
32321
+ resourceType,
32322
+ physicalId
32323
+ );
32324
+ const recordName = properties["Name"];
32325
+ const recordType = properties["Type"];
32326
+ try {
32327
+ const resourceRecordSet = this.buildResourceRecordSet(properties);
32328
+ const comment = properties["Comment"];
32329
+ await this.getClient().send(
32330
+ new ChangeResourceRecordSetsCommand({
32331
+ HostedZoneId: hostedZoneId,
32332
+ ChangeBatch: {
32333
+ ...comment ? { Comment: comment } : {},
32334
+ Changes: [
32335
+ {
32336
+ Action: "UPSERT",
32337
+ ResourceRecordSet: resourceRecordSet
32338
+ }
32339
+ ]
32340
+ }
32341
+ })
32342
+ );
32343
+ const compositeId = `${hostedZoneId}|${recordName}|${recordType}`;
32344
+ this.logger.debug(`Successfully updated record set ${logicalId}`);
32345
+ return {
32346
+ physicalId: compositeId,
32347
+ wasReplaced: false,
32348
+ attributes: {}
32349
+ };
32350
+ } catch (error) {
32351
+ const cause = error instanceof Error ? error : void 0;
32352
+ throw new ProvisioningError(
32353
+ `Failed to update record set ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
32354
+ resourceType,
32355
+ logicalId,
32356
+ physicalId,
32357
+ cause
32358
+ );
32359
+ }
32360
+ }
32361
+ async deleteRecordSet(logicalId, physicalId, resourceType, properties, context) {
32362
+ this.logger.debug(`Deleting Route 53 record set ${logicalId}: ${physicalId}`);
32363
+ const parts = physicalId.split("|");
32364
+ if (parts.length !== 3) {
32365
+ throw new ProvisioningError(
32366
+ `Invalid record set physical ID format: ${physicalId}`,
32367
+ resourceType,
32368
+ logicalId,
32369
+ physicalId
32370
+ );
32371
+ }
32372
+ const [hostedZoneId] = parts;
32373
+ if (!properties) {
32374
+ throw new ProvisioningError(
32375
+ `Properties required to delete record set ${logicalId}`,
32376
+ resourceType,
32377
+ logicalId,
32378
+ physicalId
32379
+ );
32380
+ }
32381
+ try {
32382
+ const resourceRecordSet = this.buildResourceRecordSet(properties);
32383
+ await this.getClient().send(
32384
+ new ChangeResourceRecordSetsCommand({
32385
+ HostedZoneId: hostedZoneId,
32386
+ ChangeBatch: {
32387
+ Changes: [
32388
+ {
32389
+ Action: "DELETE",
32390
+ ResourceRecordSet: resourceRecordSet
32391
+ }
32392
+ ]
32393
+ }
32394
+ })
32395
+ );
32396
+ this.logger.debug(`Successfully deleted record set ${logicalId}`);
32397
+ } catch (error) {
32398
+ if (error instanceof Error && (error.name === "InvalidChangeBatch" || error.message.includes("it was not found"))) {
32399
+ const clientRegion = await this.getClient().config.region();
32400
+ assertRegionMatch(
32401
+ clientRegion,
32402
+ context?.expectedRegion,
32403
+ resourceType,
32404
+ logicalId,
32405
+ physicalId
32406
+ );
32407
+ this.logger.debug(`Record set ${physicalId} does not exist, skipping deletion`);
32408
+ return;
32409
+ }
32410
+ if (error instanceof Error && error.name === "NoSuchHostedZone") {
32411
+ this.logger.debug(
32412
+ `Hosted zone for record set ${physicalId} does not exist, skipping deletion`
32413
+ );
32414
+ return;
32415
+ }
32416
+ const cause = error instanceof Error ? error : void 0;
32417
+ throw new ProvisioningError(
32418
+ `Failed to delete record set ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
32419
+ resourceType,
32420
+ logicalId,
32421
+ physicalId,
32422
+ cause
32423
+ );
32424
+ }
32425
+ }
32426
+ // ─── Helpers ───────────────────────────────────────────────────────
32427
+ /**
32428
+ * Build a ResourceRecordSet object from CDK properties.
32429
+ *
32430
+ * Handles conversion of CDK-style ResourceRecords (array of strings)
32431
+ * to SDK-style ResourceRecords (array of {Value}).
32432
+ * Also handles routing policy properties: Weight, Region, Failover,
32433
+ * MultiValueAnswer, GeoLocation, SetIdentifier, and HealthCheckId.
32434
+ */
32435
+ buildResourceRecordSet(properties) {
32436
+ const name = properties["Name"];
32437
+ const type = properties["Type"];
32438
+ const ttl = properties["TTL"];
32439
+ const resourceRecords = properties["ResourceRecords"];
32440
+ const aliasTarget = properties["AliasTarget"];
32441
+ const recordSet = {
32442
+ Name: name,
32443
+ Type: type
32444
+ };
32445
+ if (aliasTarget) {
32446
+ recordSet.AliasTarget = {
32447
+ HostedZoneId: aliasTarget["HostedZoneId"],
32448
+ DNSName: aliasTarget["DNSName"],
32449
+ EvaluateTargetHealth: aliasTarget["EvaluateTargetHealth"] ?? false
32450
+ };
32451
+ } else {
32452
+ if (ttl !== void 0) {
32453
+ recordSet.TTL = Number(ttl);
32454
+ }
32455
+ if (resourceRecords) {
32456
+ recordSet.ResourceRecords = resourceRecords.map((record) => {
32457
+ if (typeof record === "string") {
32458
+ return { Value: record };
32459
+ }
32460
+ return record;
32461
+ });
32462
+ }
32463
+ }
32464
+ const setIdentifier = properties["SetIdentifier"];
32465
+ if (setIdentifier !== void 0) {
32466
+ recordSet.SetIdentifier = setIdentifier;
32467
+ }
32468
+ const weight = properties["Weight"];
30460
32469
  if (weight !== void 0) {
30461
32470
  recordSet.Weight = Number(weight);
30462
32471
  }
@@ -30726,7 +32735,7 @@ var Route53Provider = class {
30726
32735
  const idTail = physicalId.replace(/^\/hostedzone\//, "");
30727
32736
  try {
30728
32737
  const tagsResp = await this.getClient().send(
30729
- new ListTagsForResourceCommand11({ ResourceType: "hostedzone", ResourceId: idTail })
32738
+ new ListTagsForResourceCommand13({ ResourceType: "hostedzone", ResourceId: idTail })
30730
32739
  );
30731
32740
  const tags = normalizeAwsTagsToCfn(tagsResp.ResourceTagSet?.Tags);
30732
32741
  result["HostedZoneTags"] = tags;
@@ -30876,7 +32885,7 @@ var Route53Provider = class {
30876
32885
  const zoneId = zone.Id.replace("/hostedzone/", "");
30877
32886
  try {
30878
32887
  const tagsResp = await this.getClient().send(
30879
- new ListTagsForResourceCommand11({
32888
+ new ListTagsForResourceCommand13({
30880
32889
  ResourceType: "hostedzone",
30881
32890
  ResourceId: zoneId
30882
32891
  })
@@ -30904,7 +32913,7 @@ import {
30904
32913
  DeleteWebACLCommand,
30905
32914
  GetWebACLCommand,
30906
32915
  ListWebACLsCommand,
30907
- ListTagsForResourceCommand as ListTagsForResourceCommand12,
32916
+ ListTagsForResourceCommand as ListTagsForResourceCommand14,
30908
32917
  TagResourceCommand as TagResourceCommand13,
30909
32918
  UntagResourceCommand as UntagResourceCommand13,
30910
32919
  WAFNonexistentItemException
@@ -31232,7 +33241,7 @@ var WAFv2WebACLProvider = class {
31232
33241
  }
31233
33242
  try {
31234
33243
  const tagsResp = await this.getClient().send(
31235
- new ListTagsForResourceCommand12({ ResourceARN: physicalId })
33244
+ new ListTagsForResourceCommand14({ ResourceARN: physicalId })
31236
33245
  );
31237
33246
  const tags = normalizeAwsTagsToCfn(tagsResp.TagInfoForResource?.TagList);
31238
33247
  result["Tags"] = tags;
@@ -31280,7 +33289,7 @@ var WAFv2WebACLProvider = class {
31280
33289
  if (!item.ARN)
31281
33290
  continue;
31282
33291
  const tagsResp = await this.getClient().send(
31283
- new ListTagsForResourceCommand12({ ResourceARN: item.ARN })
33292
+ new ListTagsForResourceCommand14({ ResourceARN: item.ARN })
31284
33293
  );
31285
33294
  const tagList = tagsResp.TagInfoForResource?.TagList;
31286
33295
  if (matchesCdkPath(tagList, input.cdkPath)) {
@@ -31301,7 +33310,7 @@ import {
31301
33310
  UpdateUserPoolCommand,
31302
33311
  DescribeUserPoolCommand,
31303
33312
  ListUserPoolsCommand,
31304
- ListTagsForResourceCommand as ListTagsForResourceCommand13,
33313
+ ListTagsForResourceCommand as ListTagsForResourceCommand15,
31305
33314
  ResourceNotFoundException as ResourceNotFoundException12
31306
33315
  } from "@aws-sdk/client-cognito-identity-provider";
31307
33316
  function isEmptyObjectPlaceholder(value) {
@@ -31770,7 +33779,7 @@ var CognitoUserPoolProvider = class {
31770
33779
  if (!arn)
31771
33780
  continue;
31772
33781
  const tagsResp = await this.getClient().send(
31773
- new ListTagsForResourceCommand13({ ResourceArn: arn })
33782
+ new ListTagsForResourceCommand15({ ResourceArn: arn })
31774
33783
  );
31775
33784
  if (tagsResp.Tags?.[CDK_PATH_TAG] === input.cdkPath) {
31776
33785
  return { physicalId: pool.Id, attributes: {} };
@@ -31799,9 +33808,9 @@ import {
31799
33808
  DeleteCacheSubnetGroupCommand,
31800
33809
  ModifyCacheSubnetGroupCommand,
31801
33810
  ModifyCacheClusterCommand,
31802
- ListTagsForResourceCommand as ListTagsForResourceCommand14,
31803
- AddTagsToResourceCommand as AddTagsToResourceCommand3,
31804
- RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand3
33811
+ ListTagsForResourceCommand as ListTagsForResourceCommand16,
33812
+ AddTagsToResourceCommand as AddTagsToResourceCommand5,
33813
+ RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand5
31805
33814
  } from "@aws-sdk/client-elasticache";
31806
33815
  import { STSClient as STSClient6, GetCallerIdentityCommand as GetCallerIdentityCommand6 } from "@aws-sdk/client-sts";
31807
33816
  var ElastiCacheProvider = class {
@@ -32187,13 +34196,13 @@ var ElastiCacheProvider = class {
32187
34196
  }
32188
34197
  if (tagsToRemove.length > 0) {
32189
34198
  await this.getClient().send(
32190
- new RemoveTagsFromResourceCommand3({ ResourceName: arn, TagKeys: tagsToRemove })
34199
+ new RemoveTagsFromResourceCommand5({ ResourceName: arn, TagKeys: tagsToRemove })
32191
34200
  );
32192
34201
  this.logger.debug(`Removed ${tagsToRemove.length} tag(s) from ElastiCache resource ${arn}`);
32193
34202
  }
32194
34203
  if (tagsToAdd.length > 0) {
32195
34204
  await this.getClient().send(
32196
- new AddTagsToResourceCommand3({ ResourceName: arn, Tags: tagsToAdd })
34205
+ new AddTagsToResourceCommand5({ ResourceName: arn, Tags: tagsToAdd })
32197
34206
  );
32198
34207
  this.logger.debug(`Added/updated ${tagsToAdd.length} tag(s) on ElastiCache resource ${arn}`);
32199
34208
  }
@@ -32393,7 +34402,7 @@ var ElastiCacheProvider = class {
32393
34402
  async attachTags(result, arn) {
32394
34403
  try {
32395
34404
  const tagsResp = await this.getClient().send(
32396
- new ListTagsForResourceCommand14({ ResourceName: arn })
34405
+ new ListTagsForResourceCommand16({ ResourceName: arn })
32397
34406
  );
32398
34407
  const tags = normalizeAwsTagsToCfn(tagsResp.TagList);
32399
34408
  result["Tags"] = tags;
@@ -32455,7 +34464,7 @@ var ElastiCacheProvider = class {
32455
34464
  continue;
32456
34465
  const arn = c.ARN ?? await this.buildClusterArn(c.CacheClusterId);
32457
34466
  const tagsResp = await this.getClient().send(
32458
- new ListTagsForResourceCommand14({ ResourceName: arn })
34467
+ new ListTagsForResourceCommand16({ ResourceName: arn })
32459
34468
  );
32460
34469
  if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
32461
34470
  return { physicalId: c.CacheClusterId, attributes: {} };
@@ -32492,7 +34501,7 @@ var ElastiCacheProvider = class {
32492
34501
  continue;
32493
34502
  const arn = g.ARN ?? await this.buildSubnetGroupArn(g.CacheSubnetGroupName);
32494
34503
  const tagsResp = await this.getClient().send(
32495
- new ListTagsForResourceCommand14({ ResourceName: arn })
34504
+ new ListTagsForResourceCommand16({ ResourceName: arn })
32496
34505
  );
32497
34506
  if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
32498
34507
  return { physicalId: g.CacheSubnetGroupName, attributes: {} };
@@ -32545,7 +34554,7 @@ import {
32545
34554
  GetServiceCommand,
32546
34555
  ListNamespacesCommand,
32547
34556
  ListServicesCommand as ListServicesCommand2,
32548
- ListTagsForResourceCommand as ListTagsForResourceCommand15,
34557
+ ListTagsForResourceCommand as ListTagsForResourceCommand17,
32549
34558
  NamespaceNotFound,
32550
34559
  ServiceNotFound
32551
34560
  } from "@aws-sdk/client-servicediscovery";
@@ -33109,7 +35118,7 @@ var ServiceDiscoveryProvider = class {
33109
35118
  async attachTags(result, arn) {
33110
35119
  try {
33111
35120
  const tagsResp = await this.getClient().send(
33112
- new ListTagsForResourceCommand15({ ResourceARN: arn })
35121
+ new ListTagsForResourceCommand17({ ResourceARN: arn })
33113
35122
  );
33114
35123
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
33115
35124
  result["Tags"] = tags;
@@ -33155,7 +35164,7 @@ var ServiceDiscoveryProvider = class {
33155
35164
  if (input.cdkPath) {
33156
35165
  try {
33157
35166
  const tagsResp = await this.getClient().send(
33158
- new ListTagsForResourceCommand15({ ResourceARN: ns.Arn })
35167
+ new ListTagsForResourceCommand17({ ResourceARN: ns.Arn })
33159
35168
  );
33160
35169
  if (matchesCdkPath(tagsResp.Tags, input.cdkPath)) {
33161
35170
  return { physicalId: ns.Id, attributes: {} };
@@ -33194,7 +35203,7 @@ var ServiceDiscoveryProvider = class {
33194
35203
  continue;
33195
35204
  try {
33196
35205
  const tagsResp = await this.getClient().send(
33197
- new ListTagsForResourceCommand15({ ResourceARN: svc.Arn })
35206
+ new ListTagsForResourceCommand17({ ResourceARN: svc.Arn })
33198
35207
  );
33199
35208
  if (matchesCdkPath(tagsResp.Tags, input.cdkPath)) {
33200
35209
  return { physicalId: svc.Id, attributes: {} };
@@ -38392,7 +40401,7 @@ import {
38392
40401
  GetVectorBucketCommand,
38393
40402
  ListIndexesCommand,
38394
40403
  ListVectorBucketsCommand,
38395
- ListTagsForResourceCommand as ListTagsForResourceCommand16,
40404
+ ListTagsForResourceCommand as ListTagsForResourceCommand18,
38396
40405
  DeleteIndexCommand
38397
40406
  } from "@aws-sdk/client-s3vectors";
38398
40407
  var S3VectorsProvider = class {
@@ -38626,7 +40635,7 @@ var S3VectorsProvider = class {
38626
40635
  continue;
38627
40636
  try {
38628
40637
  const tagsResp = await this.getClient().send(
38629
- new ListTagsForResourceCommand16({ resourceArn: bucket.vectorBucketArn })
40638
+ new ListTagsForResourceCommand18({ resourceArn: bucket.vectorBucketArn })
38630
40639
  );
38631
40640
  if (tagsResp.tags?.[CDK_PATH_TAG] === input.cdkPath) {
38632
40641
  return { physicalId: bucket.vectorBucketName, attributes: {} };
@@ -38970,7 +40979,7 @@ import {
38970
40979
  ListNamespacesCommand as ListNamespacesCommand2,
38971
40980
  ListTablesCommand as ListTablesCommand2,
38972
40981
  ListTableBucketsCommand,
38973
- ListTagsForResourceCommand as ListTagsForResourceCommand17,
40982
+ ListTagsForResourceCommand as ListTagsForResourceCommand19,
38974
40983
  NotFoundException as NotFoundException6
38975
40984
  } from "@aws-sdk/client-s3tables";
38976
40985
  var S3TablesProvider = class {
@@ -39454,7 +41463,7 @@ var S3TablesProvider = class {
39454
41463
  if (input.cdkPath) {
39455
41464
  try {
39456
41465
  const tagsResp = await this.getClient().send(
39457
- new ListTagsForResourceCommand17({ resourceArn: bucket.arn })
41466
+ new ListTagsForResourceCommand19({ resourceArn: bucket.arn })
39458
41467
  );
39459
41468
  if (tagsResp.tags?.[CDK_PATH_TAG] === input.cdkPath) {
39460
41469
  return { physicalId: bucket.arn, attributes: {} };
@@ -39534,7 +41543,7 @@ var S3TablesProvider = class {
39534
41543
  continue;
39535
41544
  try {
39536
41545
  const tagsResp = await this.getClient().send(
39537
- new ListTagsForResourceCommand17({ resourceArn: table.tableARN })
41546
+ new ListTagsForResourceCommand19({ resourceArn: table.tableARN })
39538
41547
  );
39539
41548
  if (tagsResp.tags?.[CDK_PATH_TAG] === input.cdkPath) {
39540
41549
  return {
@@ -39620,7 +41629,7 @@ import {
39620
41629
  PutImageScanningConfigurationCommand,
39621
41630
  PutImageTagMutabilityCommand,
39622
41631
  TagResourceCommand as TagResourceCommand15,
39623
- ListTagsForResourceCommand as ListTagsForResourceCommand18,
41632
+ ListTagsForResourceCommand as ListTagsForResourceCommand20,
39624
41633
  LifecyclePolicyNotFoundException,
39625
41634
  RepositoryNotFoundException
39626
41635
  } from "@aws-sdk/client-ecr";
@@ -39950,7 +41959,7 @@ var ECRProvider = class {
39950
41959
  if (r.repositoryArn) {
39951
41960
  try {
39952
41961
  const tagsResp = await this.getClient().send(
39953
- new ListTagsForResourceCommand18({ resourceArn: r.repositoryArn })
41962
+ new ListTagsForResourceCommand20({ resourceArn: r.repositoryArn })
39954
41963
  );
39955
41964
  const tags = normalizeAwsTagsToCfn(tagsResp.tags);
39956
41965
  result["Tags"] = tags;
@@ -39996,7 +42005,7 @@ var ECRProvider = class {
39996
42005
  continue;
39997
42006
  try {
39998
42007
  const tagsResp = await this.getClient().send(
39999
- new ListTagsForResourceCommand18({ resourceArn: repo.repositoryArn })
42008
+ new ListTagsForResourceCommand20({ resourceArn: repo.repositoryArn })
40000
42009
  );
40001
42010
  if (matchesCdkPath(tagsResp.tags, input.cdkPath)) {
40002
42011
  return { physicalId: repo.repositoryName, attributes: {} };
@@ -40559,8 +42568,9 @@ var ASGProvider = class {
40559
42568
  out.LaunchTemplateId = lt.LaunchTemplateId;
40560
42569
  if (lt.LaunchTemplateName !== void 0)
40561
42570
  out.LaunchTemplateName = lt.LaunchTemplateName;
40562
- if (lt.Version !== void 0)
40563
- out.Version = lt.Version;
42571
+ if (lt.Version !== void 0) {
42572
+ out.Version = String(lt.Version);
42573
+ }
40564
42574
  if (out.LaunchTemplateId === void 0 && out.LaunchTemplateName === void 0) {
40565
42575
  return void 0;
40566
42576
  }
@@ -40724,6 +42734,14 @@ function registerAllProviders(registry) {
40724
42734
  registry.register("AWS::RDS::DBSubnetGroup", rdsProvider);
40725
42735
  registry.register("AWS::RDS::DBCluster", rdsProvider);
40726
42736
  registry.register("AWS::RDS::DBInstance", rdsProvider);
42737
+ const docdbProvider = new DocDBProvider();
42738
+ registry.register("AWS::DocDB::DBSubnetGroup", docdbProvider);
42739
+ registry.register("AWS::DocDB::DBCluster", docdbProvider);
42740
+ registry.register("AWS::DocDB::DBInstance", docdbProvider);
42741
+ const neptuneProvider = new NeptuneProvider();
42742
+ registry.register("AWS::Neptune::DBSubnetGroup", neptuneProvider);
42743
+ registry.register("AWS::Neptune::DBCluster", neptuneProvider);
42744
+ registry.register("AWS::Neptune::DBInstance", neptuneProvider);
40727
42745
  const route53Provider = new Route53Provider();
40728
42746
  registry.register("AWS::Route53::HostedZone", route53Provider);
40729
42747
  registry.register("AWS::Route53::RecordSet", route53Provider);
@@ -43725,6 +45743,14 @@ var PROTECTION_PROPERTY_BY_TYPE = {
43725
45743
  "AWS::Logs::LogGroup": "DeletionProtectionEnabled",
43726
45744
  "AWS::RDS::DBInstance": "DeletionProtection",
43727
45745
  "AWS::RDS::DBCluster": "DeletionProtection",
45746
+ // DocDB: cluster-level only. The DocDB DBInstance shape does NOT
45747
+ // expose a DeletionProtection field (verified against the
45748
+ // @aws-sdk/client-docdb CreateDBInstanceMessage type — the field is
45749
+ // absent), so there is nothing to flip on destroy of an instance.
45750
+ "AWS::DocDB::DBCluster": "DeletionProtection",
45751
+ // Neptune: both cluster and instance expose DeletionProtection.
45752
+ "AWS::Neptune::DBCluster": "DeletionProtection",
45753
+ "AWS::Neptune::DBInstance": "DeletionProtection",
43728
45754
  "AWS::DynamoDB::Table": "DeletionProtectionEnabled",
43729
45755
  "AWS::EC2::Instance": "DisableApiTermination",
43730
45756
  "AWS::Cognito::UserPool": "DeletionProtection",
@@ -46045,7 +48071,7 @@ function createStateDestroyCommand() {
46045
48071
  "Destroy a stack's AWS resources and remove its state record without requiring the CDK app. For removing only the state record (keeping AWS resources intact), use 'cdkd state orphan'."
46046
48072
  ).argument("[stacks...]", "Stack name(s) to destroy (physical CloudFormation names)").option("--all", "Destroy every stack in the state bucket", false).option(
46047
48073
  "--remove-protection",
46048
- "Bypass deletion protection on protected resources by flipping the per-resource protection flag off in-place before delete. Covers AWS::Logs::LogGroup, AWS::RDS::DBInstance, AWS::RDS::DBCluster, AWS::DynamoDB::Table, AWS::EC2::Instance, and AWS::ElasticLoadBalancingV2::LoadBalancer.",
48074
+ "Bypass deletion protection on protected resources by flipping the per-resource protection flag off in-place before delete. Covers AWS::Logs::LogGroup, AWS::RDS::DBInstance, AWS::RDS::DBCluster, AWS::DocDB::DBCluster, AWS::Neptune::DBCluster, AWS::Neptune::DBInstance, AWS::DynamoDB::Table, AWS::EC2::Instance, AWS::Cognito::UserPool, AWS::AutoScaling::AutoScalingGroup, and AWS::ElasticLoadBalancingV2::LoadBalancer.",
46049
48075
  false
46050
48076
  ).addOption(stackRegionOption2()).addHelpText(
46051
48077
  "after",
@@ -47187,7 +49213,7 @@ function reorderArgs(argv) {
47187
49213
  }
47188
49214
  async function main() {
47189
49215
  const program = new Command14();
47190
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.59.0");
49216
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.60.0");
47191
49217
  program.addCommand(createBootstrapCommand());
47192
49218
  program.addCommand(createSynthCommand());
47193
49219
  program.addCommand(createListCommand());