@go-to-k/cdkd 0.59.1 → 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 }
@@ -30019,70 +30045,89 @@ var RDSProvider = class {
30019
30045
  }
30020
30046
  };
30021
30047
 
30022
- // src/provisioning/providers/route53-provider.ts
30048
+ // src/provisioning/providers/docdb-provider.ts
30023
30049
  import {
30024
- Route53Client as Route53Client2,
30025
- CreateHostedZoneCommand,
30026
- DeleteHostedZoneCommand,
30027
- GetHostedZoneCommand as GetHostedZoneCommand2,
30028
- ChangeResourceRecordSetsCommand,
30029
- UpdateHostedZoneCommentCommand,
30030
- ChangeTagsForResourceCommand,
30031
- AssociateVPCWithHostedZoneCommand,
30032
- DisassociateVPCFromHostedZoneCommand,
30033
- CreateQueryLoggingConfigCommand,
30034
- DeleteQueryLoggingConfigCommand,
30035
- ListQueryLoggingConfigsCommand,
30036
- ListHostedZonesByNameCommand as ListHostedZonesByNameCommand2,
30037
- ListHostedZonesCommand,
30038
- ListResourceRecordSetsCommand,
30039
- ListTagsForResourceCommand as ListTagsForResourceCommand11
30040
- } from "@aws-sdk/client-route-53";
30041
- var Route53Provider = class {
30042
- 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;
30043
30069
  providerRegion = process.env["AWS_REGION"];
30044
- logger = getLogger().child("Route53Provider");
30070
+ logger = getLogger().child("DocDBProvider");
30045
30071
  handledProperties = /* @__PURE__ */ new Map([
30046
30072
  [
30047
- "AWS::Route53::HostedZone",
30048
- /* @__PURE__ */ new Set(["Name", "HostedZoneConfig", "HostedZoneTags", "VPCs", "QueryLoggingConfig"])
30073
+ "AWS::DocDB::DBSubnetGroup",
30074
+ /* @__PURE__ */ new Set(["DBSubnetGroupName", "DBSubnetGroupDescription", "SubnetIds", "Tags"])
30049
30075
  ],
30050
30076
  [
30051
- "AWS::Route53::RecordSet",
30077
+ "AWS::DocDB::DBCluster",
30052
30078
  /* @__PURE__ */ new Set([
30053
- "HostedZoneId",
30054
- "HostedZoneName",
30055
- "Name",
30056
- "Type",
30057
- "TTL",
30058
- "ResourceRecords",
30059
- "AliasTarget",
30060
- "SetIdentifier",
30061
- "Weight",
30062
- "Region",
30063
- "Failover",
30064
- "MultiValueAnswer",
30065
- "HealthCheckId",
30066
- "Comment",
30067
- "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"
30068
30111
  ])
30069
30112
  ]
30070
30113
  ]);
30071
30114
  getClient() {
30072
- if (!this.route53Client) {
30073
- this.route53Client = new Route53Client2(
30115
+ if (!this.docdbClient) {
30116
+ this.docdbClient = new DocDBClient(
30074
30117
  this.providerRegion ? { region: this.providerRegion } : {}
30075
30118
  );
30076
30119
  }
30077
- return this.route53Client;
30120
+ return this.docdbClient;
30078
30121
  }
30079
30122
  // ─── Dispatch ─────────────────────────────────────────────────────
30080
30123
  async create(logicalId, resourceType, properties) {
30081
30124
  switch (resourceType) {
30082
- case "AWS::Route53::HostedZone":
30083
- return this.createHostedZone(logicalId, resourceType, properties);
30084
- case "AWS::Route53::RecordSet":
30085
- 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);
30086
30131
  default:
30087
30132
  throw new ProvisioningError(
30088
30133
  `Unsupported resource type: ${resourceType}`,
@@ -30091,12 +30136,32 @@ var Route53Provider = class {
30091
30136
  );
30092
30137
  }
30093
30138
  }
30094
- async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
30139
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
30095
30140
  switch (resourceType) {
30096
- case "AWS::Route53::HostedZone":
30097
- return this.updateHostedZone(logicalId, physicalId, resourceType, properties);
30098
- case "AWS::Route53::RecordSet":
30099
- 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
+ );
30100
30165
  default:
30101
30166
  throw new ProvisioningError(
30102
30167
  `Unsupported resource type: ${resourceType}`,
@@ -30106,12 +30171,14 @@ var Route53Provider = class {
30106
30171
  );
30107
30172
  }
30108
30173
  }
30109
- async delete(logicalId, physicalId, resourceType, properties, context) {
30174
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
30110
30175
  switch (resourceType) {
30111
- case "AWS::Route53::HostedZone":
30112
- return this.deleteHostedZone(logicalId, physicalId, resourceType, context);
30113
- case "AWS::Route53::RecordSet":
30114
- 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);
30115
30182
  default:
30116
30183
  throw new ProvisioningError(
30117
30184
  `Unsupported resource type: ${resourceType}`,
@@ -30121,123 +30188,74 @@ var Route53Provider = class {
30121
30188
  );
30122
30189
  }
30123
30190
  }
30124
- async getAttribute(physicalId, resourceType, attributeName) {
30125
- switch (resourceType) {
30126
- case "AWS::Route53::HostedZone":
30127
- return this.getHostedZoneAttribute(physicalId, attributeName);
30128
- default:
30129
- return void 0;
30130
- }
30131
- }
30132
- // ─── AWS::Route53::HostedZone ──────────────────────────────────────
30133
- async createHostedZone(logicalId, resourceType, properties) {
30134
- this.logger.debug(`Creating Route 53 hosted zone ${logicalId}`);
30135
- const name = properties["Name"];
30136
- if (!name) {
30137
- throw new ProvisioningError(
30138
- `Name is required for hosted zone ${logicalId}`,
30139
- resourceType,
30140
- logicalId
30141
- );
30142
- }
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 });
30143
30195
  try {
30144
- const hostedZoneConfig = properties["HostedZoneConfig"];
30145
- const vpcs = properties["VPCs"];
30146
- const firstVpc = vpcs && vpcs.length > 0 ? vpcs[0] : void 0;
30147
- const response = await this.getClient().send(
30148
- new CreateHostedZoneCommand({
30149
- Name: name,
30150
- CallerReference: `${logicalId}-${Date.now()}`,
30151
- ...hostedZoneConfig && hostedZoneConfig["Comment"] ? {
30152
- HostedZoneConfig: {
30153
- Comment: hostedZoneConfig["Comment"],
30154
- // When VPCs are specified, this is a private hosted zone
30155
- ...firstVpc ? { PrivateZone: true } : {}
30156
- }
30157
- } : firstVpc ? { HostedZoneConfig: { PrivateZone: true } } : {},
30158
- ...firstVpc ? {
30159
- VPC: {
30160
- VPCId: firstVpc["VPCId"],
30161
- VPCRegion: firstVpc["VPCRegion"]
30162
- }
30163
- } : {}
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 }
30164
30203
  })
30165
30204
  );
30166
- const hostedZone = response.HostedZone;
30167
- if (!hostedZone?.Id) {
30168
- throw new Error("CreateHostedZone did not return HostedZone.Id");
30169
- }
30170
- const zoneId = hostedZone.Id.replace("/hostedzone/", "");
30171
- if (vpcs && vpcs.length > 1) {
30172
- for (let i = 1; i < vpcs.length; i++) {
30173
- const additionalVpc = vpcs[i];
30174
- this.logger.debug(
30175
- `Associating additional VPC ${String(additionalVpc["VPCId"])} with hosted zone ${zoneId}`
30176
- );
30177
- await this.getClient().send(
30178
- new AssociateVPCWithHostedZoneCommand({
30179
- HostedZoneId: zoneId,
30180
- VPC: {
30181
- VPCId: additionalVpc["VPCId"],
30182
- VPCRegion: additionalVpc["VPCRegion"]
30183
- }
30184
- })
30185
- );
30186
- }
30187
- }
30188
- await this.applyHostedZoneTags(zoneId, properties, logicalId);
30189
- await this.applyQueryLoggingConfig(zoneId, properties, logicalId);
30190
- const nameServers = response.DelegationSet?.NameServers ?? [];
30191
- this.logger.debug(`Successfully created hosted zone ${logicalId}: ${zoneId}`);
30205
+ this.logger.debug(
30206
+ `Successfully created DocDB DBSubnetGroup ${logicalId}: ${dbSubnetGroupName}`
30207
+ );
30192
30208
  return {
30193
- physicalId: zoneId,
30209
+ physicalId: dbSubnetGroupName,
30194
30210
  attributes: {
30195
- Id: zoneId,
30196
- NameServers: nameServers.join(",")
30211
+ DBSubnetGroupName: dbSubnetGroupName
30197
30212
  }
30198
30213
  };
30199
30214
  } catch (error) {
30200
- if (error instanceof ProvisioningError)
30201
- throw error;
30202
30215
  const cause = error instanceof Error ? error : void 0;
30203
30216
  throw new ProvisioningError(
30204
- `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)}`,
30205
30218
  resourceType,
30206
30219
  logicalId,
30207
- void 0,
30220
+ dbSubnetGroupName,
30208
30221
  cause
30209
30222
  );
30210
30223
  }
30211
30224
  }
30212
- async updateHostedZone(logicalId, physicalId, resourceType, properties) {
30213
- 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}`);
30214
30227
  try {
30215
- const hostedZoneConfig = properties["HostedZoneConfig"];
30216
- const comment = hostedZoneConfig?.["Comment"] ?? "";
30217
- await this.getClient().send(
30218
- new UpdateHostedZoneCommentCommand({
30219
- Id: physicalId,
30220
- Comment: comment
30221
- })
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 })
30222
30238
  );
30223
- await this.applyHostedZoneTags(physicalId, properties, logicalId);
30224
- await this.applyQueryLoggingConfig(physicalId, properties, logicalId);
30225
- await this.syncVPCAssociations(physicalId, properties, logicalId);
30226
- const getResponse = await this.getClient().send(new GetHostedZoneCommand2({ Id: physicalId }));
30227
- const nameServers = getResponse.DelegationSet?.NameServers ?? [];
30228
- 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}`);
30229
30248
  return {
30230
30249
  physicalId,
30231
30250
  wasReplaced: false,
30232
30251
  attributes: {
30233
- Id: physicalId,
30234
- NameServers: nameServers.join(",")
30252
+ DBSubnetGroupName: physicalId
30235
30253
  }
30236
30254
  };
30237
30255
  } catch (error) {
30238
30256
  const cause = error instanceof Error ? error : void 0;
30239
30257
  throw new ProvisioningError(
30240
- `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)}`,
30241
30259
  resourceType,
30242
30260
  logicalId,
30243
30261
  physicalId,
@@ -30245,14 +30263,17 @@ var Route53Provider = class {
30245
30263
  );
30246
30264
  }
30247
30265
  }
30248
- async deleteHostedZone(logicalId, physicalId, resourceType, context) {
30249
- 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}`);
30250
30268
  try {
30251
- await this.deleteQueryLoggingConfigForZone(physicalId, logicalId);
30252
- await this.getClient().send(new DeleteHostedZoneCommand({ Id: physicalId }));
30253
- 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}`);
30254
30275
  } catch (error) {
30255
- if (error instanceof Error && error.name === "NoSuchHostedZone") {
30276
+ if (this.isNotFoundError(error, "DBSubnetGroupNotFoundFault")) {
30256
30277
  const clientRegion = await this.getClient().config.region();
30257
30278
  assertRegionMatch(
30258
30279
  clientRegion,
@@ -30261,12 +30282,12 @@ var Route53Provider = class {
30261
30282
  logicalId,
30262
30283
  physicalId
30263
30284
  );
30264
- this.logger.debug(`Hosted zone ${physicalId} does not exist, skipping deletion`);
30285
+ this.logger.debug(`DocDB DBSubnetGroup ${physicalId} does not exist, skipping deletion`);
30265
30286
  return;
30266
30287
  }
30267
30288
  const cause = error instanceof Error ? error : void 0;
30268
30289
  throw new ProvisioningError(
30269
- `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)}`,
30270
30291
  resourceType,
30271
30292
  logicalId,
30272
30293
  physicalId,
@@ -30274,98 +30295,111 @@ var Route53Provider = class {
30274
30295
  );
30275
30296
  }
30276
30297
  }
30277
- async getHostedZoneAttribute(physicalId, attributeName) {
30278
- switch (attributeName) {
30279
- case "Id":
30280
- return physicalId;
30281
- case "NameServers": {
30282
- const response = await this.getClient().send(new GetHostedZoneCommand2({ Id: physicalId }));
30283
- return (response.DelegationSet?.NameServers ?? []).join(",");
30284
- }
30285
- default:
30286
- return void 0;
30287
- }
30288
- }
30289
- // ─── AWS::Route53::RecordSet ───────────────────────────────────────
30290
- async createRecordSet(logicalId, resourceType, properties) {
30291
- this.logger.debug(`Creating Route 53 record set ${logicalId}`);
30292
- const hostedZoneId = await this.resolveHostedZoneId(properties, logicalId, resourceType);
30293
- const recordName = properties["Name"];
30294
- 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 });
30295
30302
  try {
30296
- const resourceRecordSet = this.buildResourceRecordSet(properties);
30297
- const comment = properties["Comment"];
30298
- await this.getClient().send(
30299
- new ChangeResourceRecordSetsCommand({
30300
- HostedZoneId: hostedZoneId,
30301
- ChangeBatch: {
30302
- ...comment ? { Comment: comment } : {},
30303
- Changes: [
30304
- {
30305
- Action: "CREATE",
30306
- ResourceRecordSet: resourceRecordSet
30307
- }
30308
- ]
30309
- }
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 }
30310
30323
  })
30311
30324
  );
30312
- const compositeId = `${hostedZoneId}|${recordName}|${recordType}`;
30313
- 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);
30314
30336
  return {
30315
- physicalId: compositeId,
30316
- 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
+ }
30317
30345
  };
30318
30346
  } catch (error) {
30319
30347
  if (error instanceof ProvisioningError)
30320
30348
  throw error;
30321
30349
  const cause = error instanceof Error ? error : void 0;
30322
30350
  throw new ProvisioningError(
30323
- `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)}`,
30324
30352
  resourceType,
30325
30353
  logicalId,
30326
- void 0,
30354
+ dbClusterIdentifier,
30327
30355
  cause
30328
30356
  );
30329
30357
  }
30330
30358
  }
30331
- async updateRecordSet(logicalId, physicalId, resourceType, properties) {
30332
- this.logger.debug(`Updating Route 53 record set ${logicalId}: ${physicalId}`);
30333
- const hostedZoneId = await this.resolveHostedZoneId(
30334
- properties,
30335
- logicalId,
30336
- resourceType,
30337
- physicalId
30338
- );
30339
- const recordName = properties["Name"];
30340
- const recordType = properties["Type"];
30359
+ async updateDBCluster(logicalId, physicalId, resourceType, properties, previousProperties) {
30360
+ this.logger.debug(`Updating DocDB DBCluster ${logicalId}: ${physicalId}`);
30341
30361
  try {
30342
- const resourceRecordSet = this.buildResourceRecordSet(properties);
30343
- const comment = properties["Comment"];
30362
+ const vpcSgIds = properties["VpcSecurityGroupIds"];
30363
+ const sendVpcSgIds = vpcSgIds !== void 0 && vpcSgIds.length > 0;
30344
30364
  await this.getClient().send(
30345
- new ChangeResourceRecordSetsCommand({
30346
- HostedZoneId: hostedZoneId,
30347
- ChangeBatch: {
30348
- ...comment ? { Comment: comment } : {},
30349
- Changes: [
30350
- {
30351
- Action: "UPSERT",
30352
- ResourceRecordSet: resourceRecordSet
30353
- }
30354
- ]
30355
- }
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
30356
30377
  })
30357
30378
  );
30358
- const compositeId = `${hostedZoneId}|${recordName}|${recordType}`;
30359
- 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
+ }
30360
30388
  return {
30361
- physicalId: compositeId,
30389
+ physicalId,
30362
30390
  wasReplaced: false,
30363
- 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
+ }
30364
30398
  };
30365
30399
  } catch (error) {
30366
30400
  const cause = error instanceof Error ? error : void 0;
30367
30401
  throw new ProvisioningError(
30368
- `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)}`,
30369
30403
  resourceType,
30370
30404
  logicalId,
30371
30405
  physicalId,
@@ -30373,44 +30407,39 @@ var Route53Provider = class {
30373
30407
  );
30374
30408
  }
30375
30409
  }
30376
- async deleteRecordSet(logicalId, physicalId, resourceType, properties, context) {
30377
- this.logger.debug(`Deleting Route 53 record set ${logicalId}: ${physicalId}`);
30378
- const parts = physicalId.split("|");
30379
- if (parts.length !== 3) {
30380
- throw new ProvisioningError(
30381
- `Invalid record set physical ID format: ${physicalId}`,
30382
- resourceType,
30383
- logicalId,
30384
- physicalId
30385
- );
30386
- }
30387
- const [hostedZoneId] = parts;
30388
- if (!properties) {
30389
- throw new ProvisioningError(
30390
- `Properties required to delete record set ${logicalId}`,
30391
- resourceType,
30392
- logicalId,
30393
- physicalId
30394
- );
30395
- }
30410
+ async deleteDBCluster(logicalId, physicalId, resourceType, context) {
30411
+ this.logger.debug(`Deleting DocDB DBCluster ${logicalId}: ${physicalId}`);
30396
30412
  try {
30397
- const resourceRecordSet = this.buildResourceRecordSet(properties);
30398
- await this.getClient().send(
30399
- new ChangeResourceRecordSetsCommand({
30400
- HostedZoneId: hostedZoneId,
30401
- ChangeBatch: {
30402
- Changes: [
30403
- {
30404
- Action: "DELETE",
30405
- ResourceRecordSet: resourceRecordSet
30406
- }
30407
- ]
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
+ );
30408
30430
  }
30431
+ }
30432
+ }
30433
+ await this.getClient().send(
30434
+ new DeleteDBClusterCommand2({
30435
+ DBClusterIdentifier: physicalId,
30436
+ SkipFinalSnapshot: true
30409
30437
  })
30410
30438
  );
30411
- this.logger.debug(`Successfully deleted record set ${logicalId}`);
30439
+ this.logger.debug(`Successfully initiated deletion of DocDB DBCluster ${logicalId}`);
30440
+ await this.waitForClusterDeleted(physicalId);
30412
30441
  } catch (error) {
30413
- if (error instanceof Error && (error.name === "InvalidChangeBatch" || error.message.includes("it was not found"))) {
30442
+ if (this.isNotFoundError(error, "DBClusterNotFoundFault")) {
30414
30443
  const clientRegion = await this.getClient().config.region();
30415
30444
  assertRegionMatch(
30416
30445
  clientRegion,
@@ -30419,18 +30448,12 @@ var Route53Provider = class {
30419
30448
  logicalId,
30420
30449
  physicalId
30421
30450
  );
30422
- this.logger.debug(`Record set ${physicalId} does not exist, skipping deletion`);
30423
- return;
30424
- }
30425
- if (error instanceof Error && error.name === "NoSuchHostedZone") {
30426
- this.logger.debug(
30427
- `Hosted zone for record set ${physicalId} does not exist, skipping deletion`
30428
- );
30451
+ this.logger.debug(`DocDB DBCluster ${physicalId} does not exist, skipping deletion`);
30429
30452
  return;
30430
30453
  }
30431
30454
  const cause = error instanceof Error ? error : void 0;
30432
30455
  throw new ProvisioningError(
30433
- `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)}`,
30434
30457
  resourceType,
30435
30458
  logicalId,
30436
30459
  physicalId,
@@ -30438,45 +30461,2007 @@ var Route53Provider = class {
30438
30461
  );
30439
30462
  }
30440
30463
  }
30441
- // ─── Helpers ───────────────────────────────────────────────────────
30442
- /**
30443
- * Build a ResourceRecordSet object from CDK properties.
30444
- *
30445
- * Handles conversion of CDK-style ResourceRecords (array of strings)
30446
- * to SDK-style ResourceRecords (array of {Value}).
30447
- * Also handles routing policy properties: Weight, Region, Failover,
30448
- * MultiValueAnswer, GeoLocation, SetIdentifier, and HealthCheckId.
30449
- */
30450
- buildResourceRecordSet(properties) {
30451
- const name = properties["Name"];
30452
- const type = properties["Type"];
30453
- const ttl = properties["TTL"];
30454
- const resourceRecords = properties["ResourceRecords"];
30455
- const aliasTarget = properties["AliasTarget"];
30456
- const recordSet = {
30457
- Name: name,
30458
- Type: type
30459
- };
30460
- if (aliasTarget) {
30461
- recordSet.AliasTarget = {
30462
- HostedZoneId: aliasTarget["HostedZoneId"],
30463
- DNSName: aliasTarget["DNSName"],
30464
- EvaluateTargetHealth: aliasTarget["EvaluateTargetHealth"] ?? false
30465
- };
30466
- } else {
30467
- if (ttl !== void 0) {
30468
- recordSet.TTL = Number(ttl);
30469
- }
30470
- if (resourceRecords) {
30471
- recordSet.ResourceRecords = resourceRecords.map((record) => {
30472
- if (typeof record === "string") {
30473
- return { Value: record };
30474
- }
30475
- return record;
30476
- });
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");
30477
30486
  }
30478
- }
30479
- const setIdentifier = properties["SetIdentifier"];
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"];
30480
32465
  if (setIdentifier !== void 0) {
30481
32466
  recordSet.SetIdentifier = setIdentifier;
30482
32467
  }
@@ -30750,7 +32735,7 @@ var Route53Provider = class {
30750
32735
  const idTail = physicalId.replace(/^\/hostedzone\//, "");
30751
32736
  try {
30752
32737
  const tagsResp = await this.getClient().send(
30753
- new ListTagsForResourceCommand11({ ResourceType: "hostedzone", ResourceId: idTail })
32738
+ new ListTagsForResourceCommand13({ ResourceType: "hostedzone", ResourceId: idTail })
30754
32739
  );
30755
32740
  const tags = normalizeAwsTagsToCfn(tagsResp.ResourceTagSet?.Tags);
30756
32741
  result["HostedZoneTags"] = tags;
@@ -30900,7 +32885,7 @@ var Route53Provider = class {
30900
32885
  const zoneId = zone.Id.replace("/hostedzone/", "");
30901
32886
  try {
30902
32887
  const tagsResp = await this.getClient().send(
30903
- new ListTagsForResourceCommand11({
32888
+ new ListTagsForResourceCommand13({
30904
32889
  ResourceType: "hostedzone",
30905
32890
  ResourceId: zoneId
30906
32891
  })
@@ -30928,7 +32913,7 @@ import {
30928
32913
  DeleteWebACLCommand,
30929
32914
  GetWebACLCommand,
30930
32915
  ListWebACLsCommand,
30931
- ListTagsForResourceCommand as ListTagsForResourceCommand12,
32916
+ ListTagsForResourceCommand as ListTagsForResourceCommand14,
30932
32917
  TagResourceCommand as TagResourceCommand13,
30933
32918
  UntagResourceCommand as UntagResourceCommand13,
30934
32919
  WAFNonexistentItemException
@@ -31256,7 +33241,7 @@ var WAFv2WebACLProvider = class {
31256
33241
  }
31257
33242
  try {
31258
33243
  const tagsResp = await this.getClient().send(
31259
- new ListTagsForResourceCommand12({ ResourceARN: physicalId })
33244
+ new ListTagsForResourceCommand14({ ResourceARN: physicalId })
31260
33245
  );
31261
33246
  const tags = normalizeAwsTagsToCfn(tagsResp.TagInfoForResource?.TagList);
31262
33247
  result["Tags"] = tags;
@@ -31304,7 +33289,7 @@ var WAFv2WebACLProvider = class {
31304
33289
  if (!item.ARN)
31305
33290
  continue;
31306
33291
  const tagsResp = await this.getClient().send(
31307
- new ListTagsForResourceCommand12({ ResourceARN: item.ARN })
33292
+ new ListTagsForResourceCommand14({ ResourceARN: item.ARN })
31308
33293
  );
31309
33294
  const tagList = tagsResp.TagInfoForResource?.TagList;
31310
33295
  if (matchesCdkPath(tagList, input.cdkPath)) {
@@ -31325,7 +33310,7 @@ import {
31325
33310
  UpdateUserPoolCommand,
31326
33311
  DescribeUserPoolCommand,
31327
33312
  ListUserPoolsCommand,
31328
- ListTagsForResourceCommand as ListTagsForResourceCommand13,
33313
+ ListTagsForResourceCommand as ListTagsForResourceCommand15,
31329
33314
  ResourceNotFoundException as ResourceNotFoundException12
31330
33315
  } from "@aws-sdk/client-cognito-identity-provider";
31331
33316
  function isEmptyObjectPlaceholder(value) {
@@ -31794,7 +33779,7 @@ var CognitoUserPoolProvider = class {
31794
33779
  if (!arn)
31795
33780
  continue;
31796
33781
  const tagsResp = await this.getClient().send(
31797
- new ListTagsForResourceCommand13({ ResourceArn: arn })
33782
+ new ListTagsForResourceCommand15({ ResourceArn: arn })
31798
33783
  );
31799
33784
  if (tagsResp.Tags?.[CDK_PATH_TAG] === input.cdkPath) {
31800
33785
  return { physicalId: pool.Id, attributes: {} };
@@ -31823,9 +33808,9 @@ import {
31823
33808
  DeleteCacheSubnetGroupCommand,
31824
33809
  ModifyCacheSubnetGroupCommand,
31825
33810
  ModifyCacheClusterCommand,
31826
- ListTagsForResourceCommand as ListTagsForResourceCommand14,
31827
- AddTagsToResourceCommand as AddTagsToResourceCommand3,
31828
- RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand3
33811
+ ListTagsForResourceCommand as ListTagsForResourceCommand16,
33812
+ AddTagsToResourceCommand as AddTagsToResourceCommand5,
33813
+ RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand5
31829
33814
  } from "@aws-sdk/client-elasticache";
31830
33815
  import { STSClient as STSClient6, GetCallerIdentityCommand as GetCallerIdentityCommand6 } from "@aws-sdk/client-sts";
31831
33816
  var ElastiCacheProvider = class {
@@ -32211,13 +34196,13 @@ var ElastiCacheProvider = class {
32211
34196
  }
32212
34197
  if (tagsToRemove.length > 0) {
32213
34198
  await this.getClient().send(
32214
- new RemoveTagsFromResourceCommand3({ ResourceName: arn, TagKeys: tagsToRemove })
34199
+ new RemoveTagsFromResourceCommand5({ ResourceName: arn, TagKeys: tagsToRemove })
32215
34200
  );
32216
34201
  this.logger.debug(`Removed ${tagsToRemove.length} tag(s) from ElastiCache resource ${arn}`);
32217
34202
  }
32218
34203
  if (tagsToAdd.length > 0) {
32219
34204
  await this.getClient().send(
32220
- new AddTagsToResourceCommand3({ ResourceName: arn, Tags: tagsToAdd })
34205
+ new AddTagsToResourceCommand5({ ResourceName: arn, Tags: tagsToAdd })
32221
34206
  );
32222
34207
  this.logger.debug(`Added/updated ${tagsToAdd.length} tag(s) on ElastiCache resource ${arn}`);
32223
34208
  }
@@ -32417,7 +34402,7 @@ var ElastiCacheProvider = class {
32417
34402
  async attachTags(result, arn) {
32418
34403
  try {
32419
34404
  const tagsResp = await this.getClient().send(
32420
- new ListTagsForResourceCommand14({ ResourceName: arn })
34405
+ new ListTagsForResourceCommand16({ ResourceName: arn })
32421
34406
  );
32422
34407
  const tags = normalizeAwsTagsToCfn(tagsResp.TagList);
32423
34408
  result["Tags"] = tags;
@@ -32479,7 +34464,7 @@ var ElastiCacheProvider = class {
32479
34464
  continue;
32480
34465
  const arn = c.ARN ?? await this.buildClusterArn(c.CacheClusterId);
32481
34466
  const tagsResp = await this.getClient().send(
32482
- new ListTagsForResourceCommand14({ ResourceName: arn })
34467
+ new ListTagsForResourceCommand16({ ResourceName: arn })
32483
34468
  );
32484
34469
  if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
32485
34470
  return { physicalId: c.CacheClusterId, attributes: {} };
@@ -32516,7 +34501,7 @@ var ElastiCacheProvider = class {
32516
34501
  continue;
32517
34502
  const arn = g.ARN ?? await this.buildSubnetGroupArn(g.CacheSubnetGroupName);
32518
34503
  const tagsResp = await this.getClient().send(
32519
- new ListTagsForResourceCommand14({ ResourceName: arn })
34504
+ new ListTagsForResourceCommand16({ ResourceName: arn })
32520
34505
  );
32521
34506
  if (matchesCdkPath(tagsResp.TagList, input.cdkPath)) {
32522
34507
  return { physicalId: g.CacheSubnetGroupName, attributes: {} };
@@ -32569,7 +34554,7 @@ import {
32569
34554
  GetServiceCommand,
32570
34555
  ListNamespacesCommand,
32571
34556
  ListServicesCommand as ListServicesCommand2,
32572
- ListTagsForResourceCommand as ListTagsForResourceCommand15,
34557
+ ListTagsForResourceCommand as ListTagsForResourceCommand17,
32573
34558
  NamespaceNotFound,
32574
34559
  ServiceNotFound
32575
34560
  } from "@aws-sdk/client-servicediscovery";
@@ -33133,7 +35118,7 @@ var ServiceDiscoveryProvider = class {
33133
35118
  async attachTags(result, arn) {
33134
35119
  try {
33135
35120
  const tagsResp = await this.getClient().send(
33136
- new ListTagsForResourceCommand15({ ResourceARN: arn })
35121
+ new ListTagsForResourceCommand17({ ResourceARN: arn })
33137
35122
  );
33138
35123
  const tags = normalizeAwsTagsToCfn(tagsResp.Tags);
33139
35124
  result["Tags"] = tags;
@@ -33179,7 +35164,7 @@ var ServiceDiscoveryProvider = class {
33179
35164
  if (input.cdkPath) {
33180
35165
  try {
33181
35166
  const tagsResp = await this.getClient().send(
33182
- new ListTagsForResourceCommand15({ ResourceARN: ns.Arn })
35167
+ new ListTagsForResourceCommand17({ ResourceARN: ns.Arn })
33183
35168
  );
33184
35169
  if (matchesCdkPath(tagsResp.Tags, input.cdkPath)) {
33185
35170
  return { physicalId: ns.Id, attributes: {} };
@@ -33218,7 +35203,7 @@ var ServiceDiscoveryProvider = class {
33218
35203
  continue;
33219
35204
  try {
33220
35205
  const tagsResp = await this.getClient().send(
33221
- new ListTagsForResourceCommand15({ ResourceARN: svc.Arn })
35206
+ new ListTagsForResourceCommand17({ ResourceARN: svc.Arn })
33222
35207
  );
33223
35208
  if (matchesCdkPath(tagsResp.Tags, input.cdkPath)) {
33224
35209
  return { physicalId: svc.Id, attributes: {} };
@@ -38416,7 +40401,7 @@ import {
38416
40401
  GetVectorBucketCommand,
38417
40402
  ListIndexesCommand,
38418
40403
  ListVectorBucketsCommand,
38419
- ListTagsForResourceCommand as ListTagsForResourceCommand16,
40404
+ ListTagsForResourceCommand as ListTagsForResourceCommand18,
38420
40405
  DeleteIndexCommand
38421
40406
  } from "@aws-sdk/client-s3vectors";
38422
40407
  var S3VectorsProvider = class {
@@ -38650,7 +40635,7 @@ var S3VectorsProvider = class {
38650
40635
  continue;
38651
40636
  try {
38652
40637
  const tagsResp = await this.getClient().send(
38653
- new ListTagsForResourceCommand16({ resourceArn: bucket.vectorBucketArn })
40638
+ new ListTagsForResourceCommand18({ resourceArn: bucket.vectorBucketArn })
38654
40639
  );
38655
40640
  if (tagsResp.tags?.[CDK_PATH_TAG] === input.cdkPath) {
38656
40641
  return { physicalId: bucket.vectorBucketName, attributes: {} };
@@ -38994,7 +40979,7 @@ import {
38994
40979
  ListNamespacesCommand as ListNamespacesCommand2,
38995
40980
  ListTablesCommand as ListTablesCommand2,
38996
40981
  ListTableBucketsCommand,
38997
- ListTagsForResourceCommand as ListTagsForResourceCommand17,
40982
+ ListTagsForResourceCommand as ListTagsForResourceCommand19,
38998
40983
  NotFoundException as NotFoundException6
38999
40984
  } from "@aws-sdk/client-s3tables";
39000
40985
  var S3TablesProvider = class {
@@ -39478,7 +41463,7 @@ var S3TablesProvider = class {
39478
41463
  if (input.cdkPath) {
39479
41464
  try {
39480
41465
  const tagsResp = await this.getClient().send(
39481
- new ListTagsForResourceCommand17({ resourceArn: bucket.arn })
41466
+ new ListTagsForResourceCommand19({ resourceArn: bucket.arn })
39482
41467
  );
39483
41468
  if (tagsResp.tags?.[CDK_PATH_TAG] === input.cdkPath) {
39484
41469
  return { physicalId: bucket.arn, attributes: {} };
@@ -39558,7 +41543,7 @@ var S3TablesProvider = class {
39558
41543
  continue;
39559
41544
  try {
39560
41545
  const tagsResp = await this.getClient().send(
39561
- new ListTagsForResourceCommand17({ resourceArn: table.tableARN })
41546
+ new ListTagsForResourceCommand19({ resourceArn: table.tableARN })
39562
41547
  );
39563
41548
  if (tagsResp.tags?.[CDK_PATH_TAG] === input.cdkPath) {
39564
41549
  return {
@@ -39644,7 +41629,7 @@ import {
39644
41629
  PutImageScanningConfigurationCommand,
39645
41630
  PutImageTagMutabilityCommand,
39646
41631
  TagResourceCommand as TagResourceCommand15,
39647
- ListTagsForResourceCommand as ListTagsForResourceCommand18,
41632
+ ListTagsForResourceCommand as ListTagsForResourceCommand20,
39648
41633
  LifecyclePolicyNotFoundException,
39649
41634
  RepositoryNotFoundException
39650
41635
  } from "@aws-sdk/client-ecr";
@@ -39974,7 +41959,7 @@ var ECRProvider = class {
39974
41959
  if (r.repositoryArn) {
39975
41960
  try {
39976
41961
  const tagsResp = await this.getClient().send(
39977
- new ListTagsForResourceCommand18({ resourceArn: r.repositoryArn })
41962
+ new ListTagsForResourceCommand20({ resourceArn: r.repositoryArn })
39978
41963
  );
39979
41964
  const tags = normalizeAwsTagsToCfn(tagsResp.tags);
39980
41965
  result["Tags"] = tags;
@@ -40020,7 +42005,7 @@ var ECRProvider = class {
40020
42005
  continue;
40021
42006
  try {
40022
42007
  const tagsResp = await this.getClient().send(
40023
- new ListTagsForResourceCommand18({ resourceArn: repo.repositoryArn })
42008
+ new ListTagsForResourceCommand20({ resourceArn: repo.repositoryArn })
40024
42009
  );
40025
42010
  if (matchesCdkPath(tagsResp.tags, input.cdkPath)) {
40026
42011
  return { physicalId: repo.repositoryName, attributes: {} };
@@ -40749,6 +42734,14 @@ function registerAllProviders(registry) {
40749
42734
  registry.register("AWS::RDS::DBSubnetGroup", rdsProvider);
40750
42735
  registry.register("AWS::RDS::DBCluster", rdsProvider);
40751
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);
40752
42745
  const route53Provider = new Route53Provider();
40753
42746
  registry.register("AWS::Route53::HostedZone", route53Provider);
40754
42747
  registry.register("AWS::Route53::RecordSet", route53Provider);
@@ -43750,6 +45743,14 @@ var PROTECTION_PROPERTY_BY_TYPE = {
43750
45743
  "AWS::Logs::LogGroup": "DeletionProtectionEnabled",
43751
45744
  "AWS::RDS::DBInstance": "DeletionProtection",
43752
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",
43753
45754
  "AWS::DynamoDB::Table": "DeletionProtectionEnabled",
43754
45755
  "AWS::EC2::Instance": "DisableApiTermination",
43755
45756
  "AWS::Cognito::UserPool": "DeletionProtection",
@@ -46070,7 +48071,7 @@ function createStateDestroyCommand() {
46070
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'."
46071
48072
  ).argument("[stacks...]", "Stack name(s) to destroy (physical CloudFormation names)").option("--all", "Destroy every stack in the state bucket", false).option(
46072
48073
  "--remove-protection",
46073
- "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.",
46074
48075
  false
46075
48076
  ).addOption(stackRegionOption2()).addHelpText(
46076
48077
  "after",
@@ -47212,7 +49213,7 @@ function reorderArgs(argv) {
47212
49213
  }
47213
49214
  async function main() {
47214
49215
  const program = new Command14();
47215
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.59.1");
49216
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.60.0");
47216
49217
  program.addCommand(createBootstrapCommand());
47217
49218
  program.addCommand(createSynthCommand());
47218
49219
  program.addCommand(createListCommand());