@go-to-k/cdkd 0.264.2 → 0.265.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/README.md +4 -2
- package/dist/{asg-provider-Bxida3ei.js → asg-provider-Cvnv7x_Q.js} +2 -2
- package/dist/{asg-provider-Bxida3ei.js.map → asg-provider-Cvnv7x_Q.js.map} +1 -1
- package/dist/cli.js +3 -3
- package/dist/{deploy-engine-CaquB01e.js → deploy-engine-BIwwg2DG.js} +40 -6
- package/dist/deploy-engine-BIwwg2DG.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-CaquB01e.js.map +0 -1
package/README.md
CHANGED
|
@@ -458,8 +458,10 @@ journal records the exact operations that completed, so `cdkd rollback`
|
|
|
458
458
|
replays them in reverse (deleting created resources, restoring updated
|
|
459
459
|
ones) with no synth and no CDK app needed. It is **synth-free** on
|
|
460
460
|
purpose: a broken app is a common reason you want to roll back. The
|
|
461
|
-
journal is deleted automatically on the next successful deploy
|
|
462
|
-
clean rollback
|
|
461
|
+
journal is deleted automatically on the next successful deploy and by
|
|
462
|
+
`cdkd destroy`; after a clean automatic rollback it keeps only the
|
|
463
|
+
failed resource's record so `cdkd rollback --revert-failed` still works
|
|
464
|
+
in the default deploy flow.
|
|
463
465
|
|
|
464
466
|
Flags: `--force` (skip confirm), `--orphan <logicalId>` (repeatable —
|
|
465
467
|
leave the resource alone during replay, like `cdk rollback --orphan`),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as assertRegionMatch, M as disableInstanceApiTermination, Qt as ProvisioningError, R as normalizeAwsTagsToCfn, en as ResourceUpdateNotSupportedError, ln as __exportAll } from "./deploy-engine-
|
|
1
|
+
import { B as assertRegionMatch, M as disableInstanceApiTermination, Qt as ProvisioningError, R as normalizeAwsTagsToCfn, en as ResourceUpdateNotSupportedError, ln as __exportAll } from "./deploy-engine-BIwwg2DG.js";
|
|
2
2
|
import { n as getLogger, u as generateResourceName } from "./logger-BYMEE-BS.js";
|
|
3
3
|
import { EC2Client } from "@aws-sdk/client-ec2";
|
|
4
4
|
import { AttachLoadBalancerTargetGroupsCommand, AttachLoadBalancersCommand, AttachTrafficSourcesCommand, AutoScalingClient, CreateAutoScalingGroupCommand, CreateOrUpdateTagsCommand, DeleteAutoScalingGroupCommand, DeleteLifecycleHookCommand, DeleteNotificationConfigurationCommand, DeleteTagsCommand as DeleteTagsCommand$1, DescribeAutoScalingGroupsCommand, DescribeLifecycleHooksCommand, DescribeNotificationConfigurationsCommand, DescribeTrafficSourcesCommand, DetachLoadBalancerTargetGroupsCommand, DetachLoadBalancersCommand, DetachTrafficSourcesCommand, DisableMetricsCollectionCommand, EnableMetricsCollectionCommand, PutLifecycleHookCommand, PutNotificationConfigurationCommand, UpdateAutoScalingGroupCommand } from "@aws-sdk/client-auto-scaling";
|
|
@@ -787,4 +787,4 @@ function mapNotificationsToCfn(configurations) {
|
|
|
787
787
|
|
|
788
788
|
//#endregion
|
|
789
789
|
export { asg_provider_exports as n, ASGProvider as t };
|
|
790
|
-
//# sourceMappingURL=asg-provider-
|
|
790
|
+
//# sourceMappingURL=asg-provider-Cvnv7x_Q.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asg-provider-Bxida3ei.js","names":["DeleteTagsCommand"],"sources":["../src/provisioning/providers/asg-provider.ts"],"sourcesContent":["import {\n AutoScalingClient,\n CreateAutoScalingGroupCommand,\n UpdateAutoScalingGroupCommand,\n DeleteAutoScalingGroupCommand,\n DescribeAutoScalingGroupsCommand,\n type DescribeAutoScalingGroupsCommandOutput,\n DescribeLifecycleHooksCommand,\n DescribeTrafficSourcesCommand,\n DescribeNotificationConfigurationsCommand,\n EnableMetricsCollectionCommand,\n DisableMetricsCollectionCommand,\n PutLifecycleHookCommand,\n DeleteLifecycleHookCommand,\n AttachTrafficSourcesCommand,\n DetachTrafficSourcesCommand,\n PutNotificationConfigurationCommand,\n DeleteNotificationConfigurationCommand,\n CreateOrUpdateTagsCommand,\n DeleteTagsCommand,\n AttachLoadBalancersCommand,\n DetachLoadBalancersCommand,\n AttachLoadBalancerTargetGroupsCommand,\n DetachLoadBalancerTargetGroupsCommand,\n type Tag as ASGTag,\n type LaunchTemplateSpecification,\n} from '@aws-sdk/client-auto-scaling';\nimport { EC2Client } from '@aws-sdk/client-ec2';\nimport { getLogger } from '../../utils/logger.js';\nimport { ProvisioningError, ResourceUpdateNotSupportedError } from '../../utils/error-handler.js';\nimport { assertRegionMatch, type DeleteContext } from '../region-check.js';\nimport { disableInstanceApiTermination } from '../ec2-termination-protection.js';\nimport { generateResourceName } from '../resource-name.js';\nimport { normalizeAwsTagsToCfn } from '../import-helpers.js';\nimport type {\n ResourceProvider,\n ResourceCreateResult,\n ResourceUpdateResult,\n} from '../../types/resource.js';\n\n/**\n * AWS Auto Scaling Provider\n *\n * Implements resource provisioning for `AWS::AutoScaling::AutoScalingGroup`.\n *\n * WHY a dedicated SDK provider (instead of CC API fallback):\n * 1. Owns the `--remove-protection` flip-off: ASG protection has three\n * levels (`none` / `prevent-force-deletion` / `prevent-all-deletion`)\n * and the destroy path needs to (a) clear it via `UpdateAutoScalingGroup\n * ({DeletionProtection: 'none'})` before the actual delete and (b) set\n * `ForceDelete: true` on `DeleteAutoScalingGroup` so AWS terminates any\n * running instances as part of the delete (matches the user's \"I know\n * what I'm doing\" intent).\n * 2. Faster than CC API for the common case — direct Create/Update calls\n * with no eventual-consistency polling beyond what `DescribeAutoScaling\n * Groups` already provides.\n *\n * Update has narrower coverage than create: AWS does not support modifying\n * `AutoScalingGroupName` (immutable) — that diff still surfaces\n * `ResourceUpdateNotSupportedError` so the caller can `cdkd deploy\n * --replace`. The mutable fields handled in-place via\n * `UpdateAutoScalingGroup` include MinSize / MaxSize / DesiredCapacity /\n * VPCZoneIdentifier / HealthCheckType / HealthCheckGracePeriod /\n * DefaultCooldown / Cooldown / NewInstancesProtectedFromScaleIn /\n * MaxInstanceLifetime / TerminationPolicies / CapacityRebalance /\n * ServiceLinkedRoleARN / Context / DesiredCapacityType /\n * DefaultInstanceWarmup / AvailabilityZones / AvailabilityZoneDistribution\n * / AvailabilityZoneImpairmentPolicy / SkipZonalShiftValidation /\n * CapacityReservationSpecification / InstanceMaintenancePolicy /\n * DeletionProtection / MixedInstancesPolicy / LaunchTemplate.\n *\n * Sub-shape diffs are applied via dedicated AWS APIs before the main\n * `UpdateAutoScalingGroup` call:\n * - `Tags` → `CreateOrUpdateTags` / `DeleteTags` (#475)\n * - `LoadBalancerNames` → `AttachLoadBalancers` /\n * `DetachLoadBalancers` (#476)\n * - `TargetGroupARNs` → `AttachLoadBalancerTargetGroups` /\n * `DetachLoadBalancerTargetGroups` (#476)\n * - `MetricsCollection` → `EnableMetricsCollection` /\n * `DisableMetricsCollection`\n * - `LifecycleHookSpecificationList` → per-entry `PutLifecycleHook` /\n * `DeleteLifecycleHook`\n * - `TrafficSources` → `AttachTrafficSources` /\n * `DetachTrafficSources`\n * - `NotificationConfigurations` → per-topic\n * `PutNotificationConfiguration` /\n * `DeleteNotificationConfiguration`\n *\n * Each helper is a no-op when the before/after JSON is identical.\n */\nexport class ASGProvider implements ResourceProvider {\n private asgClient?: AutoScalingClient;\n private ec2Client?: EC2Client;\n private readonly providerRegion = process.env['AWS_REGION'];\n private logger = getLogger().child('ASGProvider');\n\n handledProperties = new Map<string, ReadonlySet<string>>([\n [\n 'AWS::AutoScaling::AutoScalingGroup',\n new Set([\n 'AutoScalingGroupName',\n 'LaunchTemplate',\n 'MinSize',\n 'MaxSize',\n 'DesiredCapacity',\n 'VPCZoneIdentifier',\n 'AvailabilityZones',\n 'HealthCheckType',\n 'HealthCheckGracePeriod',\n 'Cooldown',\n 'DefaultCooldown',\n 'Tags',\n 'TerminationPolicies',\n 'NewInstancesProtectedFromScaleIn',\n 'CapacityRebalance',\n 'ServiceLinkedRoleARN',\n 'MaxInstanceLifetime',\n 'LoadBalancerNames',\n 'TargetGroupARNs',\n 'MetricsCollection',\n 'LifecycleHookSpecificationList',\n 'MixedInstancesPolicy',\n 'Context',\n 'DesiredCapacityType',\n 'DefaultInstanceWarmup',\n 'TrafficSources',\n 'NotificationConfigurations',\n 'AvailabilityZoneDistribution',\n 'AvailabilityZoneImpairmentPolicy',\n 'SkipZonalShiftValidation',\n 'CapacityReservationSpecification',\n 'InstanceMaintenancePolicy',\n 'DeletionProtection',\n ]),\n ],\n ]);\n\n unhandledByDesign = new Map<string, ReadonlyMap<string, string>>([\n [\n 'AWS::AutoScaling::AutoScalingGroup',\n new Map<string, string>([\n [\n 'LaunchConfigurationName',\n 'AWS Launch Configurations end-of-life 2024-10; use LaunchTemplate instead',\n ],\n [\n 'NotificationConfiguration',\n 'Legacy singular form; use NotificationConfigurations (plural) which cdkd already wires',\n ],\n ]),\n ],\n ]);\n\n private getClient(): AutoScalingClient {\n if (!this.asgClient) {\n this.asgClient = new AutoScalingClient(\n this.providerRegion ? { region: this.providerRegion } : {}\n );\n }\n return this.asgClient;\n }\n\n private getEc2Client(): EC2Client {\n if (!this.ec2Client) {\n this.ec2Client = new EC2Client(this.providerRegion ? { region: this.providerRegion } : {});\n }\n return this.ec2Client;\n }\n\n // ─── Dispatch ─────────────────────────────────────────────────────\n\n async create(\n logicalId: string,\n resourceType: string,\n properties: Record<string, unknown>\n ): Promise<ResourceCreateResult> {\n if (resourceType !== 'AWS::AutoScaling::AutoScalingGroup') {\n throw new ProvisioningError(\n `Unsupported resource type: ${resourceType}`,\n resourceType,\n logicalId\n );\n }\n\n const groupName =\n (properties['AutoScalingGroupName'] as string | undefined) ||\n generateResourceName(logicalId, { maxLength: 255 });\n\n this.logger.debug(`Creating AutoScalingGroup ${logicalId}: ${groupName}`);\n\n try {\n const launchTemplate = this.buildLaunchTemplate(properties);\n const tags = this.buildTags(groupName, properties);\n const vpcZoneIdentifier = this.joinVpcZoneIdentifier(properties['VPCZoneIdentifier']);\n\n const minSize = properties['MinSize'] != null ? Number(properties['MinSize']) : 0;\n const maxSize = properties['MaxSize'] != null ? Number(properties['MaxSize']) : minSize;\n\n await this.getClient().send(\n new CreateAutoScalingGroupCommand({\n AutoScalingGroupName: groupName,\n MinSize: minSize,\n MaxSize: maxSize,\n ...(properties['DesiredCapacity'] != null && {\n DesiredCapacity: Number(properties['DesiredCapacity']),\n }),\n ...(launchTemplate && { LaunchTemplate: launchTemplate }),\n ...(properties['MixedInstancesPolicy'] !== undefined && {\n MixedInstancesPolicy: properties['MixedInstancesPolicy'] as never,\n }),\n ...(vpcZoneIdentifier !== undefined && { VPCZoneIdentifier: vpcZoneIdentifier }),\n ...(properties['AvailabilityZones'] !== undefined && {\n AvailabilityZones: properties['AvailabilityZones'] as string[],\n }),\n ...(properties['HealthCheckType'] !== undefined && {\n HealthCheckType: properties['HealthCheckType'] as string,\n }),\n ...(properties['HealthCheckGracePeriod'] != null && {\n HealthCheckGracePeriod: Number(properties['HealthCheckGracePeriod']),\n }),\n ...(properties['Cooldown'] != null && {\n DefaultCooldown: Number(properties['Cooldown']),\n }),\n ...(properties['DefaultCooldown'] != null && {\n DefaultCooldown: Number(properties['DefaultCooldown']),\n }),\n ...(properties['TerminationPolicies'] !== undefined && {\n TerminationPolicies: properties['TerminationPolicies'] as string[],\n }),\n ...(properties['NewInstancesProtectedFromScaleIn'] !== undefined && {\n NewInstancesProtectedFromScaleIn: properties[\n 'NewInstancesProtectedFromScaleIn'\n ] as boolean,\n }),\n ...(properties['CapacityRebalance'] !== undefined && {\n CapacityRebalance: properties['CapacityRebalance'] as boolean,\n }),\n ...(properties['ServiceLinkedRoleARN'] !== undefined && {\n ServiceLinkedRoleARN: properties['ServiceLinkedRoleARN'] as string,\n }),\n ...(properties['MaxInstanceLifetime'] != null && {\n MaxInstanceLifetime: Number(properties['MaxInstanceLifetime']),\n }),\n ...(properties['LoadBalancerNames'] !== undefined && {\n LoadBalancerNames: properties['LoadBalancerNames'] as string[],\n }),\n ...(properties['TargetGroupARNs'] !== undefined && {\n TargetGroupARNs: properties['TargetGroupARNs'] as string[],\n }),\n ...(properties['Context'] !== undefined && {\n Context: properties['Context'] as string,\n }),\n ...(properties['DesiredCapacityType'] !== undefined && {\n DesiredCapacityType: properties['DesiredCapacityType'] as string,\n }),\n ...(properties['DefaultInstanceWarmup'] != null && {\n DefaultInstanceWarmup: Number(properties['DefaultInstanceWarmup']),\n }),\n ...(properties['LifecycleHookSpecificationList'] !== undefined && {\n LifecycleHookSpecificationList: properties['LifecycleHookSpecificationList'] as never,\n }),\n ...(properties['TrafficSources'] !== undefined && {\n TrafficSources: properties['TrafficSources'] as never,\n }),\n ...(properties['AvailabilityZoneDistribution'] !== undefined && {\n AvailabilityZoneDistribution: properties['AvailabilityZoneDistribution'] as never,\n }),\n ...(properties['AvailabilityZoneImpairmentPolicy'] !== undefined && {\n AvailabilityZoneImpairmentPolicy: properties[\n 'AvailabilityZoneImpairmentPolicy'\n ] as never,\n }),\n ...(properties['SkipZonalShiftValidation'] !== undefined && {\n SkipZonalShiftValidation: properties['SkipZonalShiftValidation'] as boolean,\n }),\n ...(properties['CapacityReservationSpecification'] !== undefined && {\n CapacityReservationSpecification: properties[\n 'CapacityReservationSpecification'\n ] as never,\n }),\n ...(properties['InstanceMaintenancePolicy'] !== undefined && {\n InstanceMaintenancePolicy: properties['InstanceMaintenancePolicy'] as never,\n }),\n ...(properties['DeletionProtection'] !== undefined && {\n DeletionProtection: properties['DeletionProtection'] as never,\n }),\n ...(tags.length > 0 && { Tags: tags }),\n })\n );\n\n this.logger.debug(`Successfully created AutoScalingGroup ${logicalId}: ${groupName}`);\n\n const arn = await this.fetchArn(groupName);\n const attributes: Record<string, unknown> = {};\n if (arn) attributes['Arn'] = arn;\n if (launchTemplate?.LaunchTemplateId) {\n attributes['LaunchTemplateID'] = launchTemplate.LaunchTemplateId;\n }\n return { physicalId: groupName, attributes };\n } catch (error) {\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to create AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n groupName,\n cause\n );\n }\n }\n\n async update(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n properties: Record<string, unknown>,\n previousProperties: Record<string, unknown>\n ): Promise<ResourceUpdateResult> {\n if (resourceType !== 'AWS::AutoScaling::AutoScalingGroup') {\n throw new ProvisioningError(\n `Unsupported resource type: ${resourceType}`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n this.logger.debug(`Updating AutoScalingGroup ${logicalId}: ${physicalId}`);\n\n // Reject diffs on fields AWS does not support modifying via\n // UpdateAutoScalingGroup. The replacement-detection layer typically\n // catches AutoScalingGroupName changes earlier; this is defense-in-\n // depth + the only place to surface the equivalent error for\n // sub-resource fields the caller may reasonably expect to round-trip.\n const stringEq = (a: unknown, b: unknown): boolean => JSON.stringify(a) === JSON.stringify(b);\n if (!stringEq(properties['AutoScalingGroupName'], previousProperties['AutoScalingGroupName'])) {\n throw new ResourceUpdateNotSupportedError(\n resourceType,\n logicalId,\n 'AutoScalingGroupName is immutable on AWS — UpdateAutoScalingGroup does not accept a new name; the name is fixed at creation. Use cdkd deploy --replace to replace the group.'\n );\n }\n try {\n // Sub-shape diffs are applied via separate per-shape SDK calls\n // BEFORE the main UpdateAutoScalingGroup. AWS does not expose these\n // fields on UpdateAutoScalingGroup, so each one rides its own\n // dedicated API. Each per-shape helper is a no-op when the\n // before/after JSON is identical.\n await this.applyTagsDiff(physicalId, properties['Tags'], previousProperties['Tags']);\n await this.applyLoadBalancerNamesDiff(\n physicalId,\n properties['LoadBalancerNames'],\n previousProperties['LoadBalancerNames']\n );\n await this.applyTargetGroupArnsDiff(\n physicalId,\n properties['TargetGroupARNs'],\n previousProperties['TargetGroupARNs']\n );\n await this.applyMetricsCollectionDiff(\n physicalId,\n properties['MetricsCollection'],\n previousProperties['MetricsCollection']\n );\n await this.applyLifecycleHooksDiff(\n physicalId,\n properties['LifecycleHookSpecificationList'],\n previousProperties['LifecycleHookSpecificationList']\n );\n await this.applyTrafficSourcesDiff(\n physicalId,\n properties['TrafficSources'],\n previousProperties['TrafficSources']\n );\n await this.applyNotificationConfigurationsDiff(\n physicalId,\n properties['NotificationConfigurations'],\n previousProperties['NotificationConfigurations']\n );\n\n const launchTemplate = this.buildLaunchTemplate(properties);\n const vpcZoneIdentifier = this.joinVpcZoneIdentifier(properties['VPCZoneIdentifier']);\n\n await this.getClient().send(\n new UpdateAutoScalingGroupCommand({\n AutoScalingGroupName: physicalId,\n ...(properties['MinSize'] != null && { MinSize: Number(properties['MinSize']) }),\n ...(properties['MaxSize'] != null && { MaxSize: Number(properties['MaxSize']) }),\n ...(properties['DesiredCapacity'] != null && {\n DesiredCapacity: Number(properties['DesiredCapacity']),\n }),\n ...(launchTemplate && { LaunchTemplate: launchTemplate }),\n ...(properties['MixedInstancesPolicy'] !== undefined && {\n MixedInstancesPolicy: properties['MixedInstancesPolicy'] as never,\n }),\n ...(vpcZoneIdentifier !== undefined && { VPCZoneIdentifier: vpcZoneIdentifier }),\n ...(properties['AvailabilityZones'] !== undefined && {\n AvailabilityZones: properties['AvailabilityZones'] as string[],\n }),\n ...(properties['HealthCheckType'] !== undefined && {\n HealthCheckType: properties['HealthCheckType'] as string,\n }),\n ...(properties['HealthCheckGracePeriod'] != null && {\n HealthCheckGracePeriod: Number(properties['HealthCheckGracePeriod']),\n }),\n ...(properties['Cooldown'] != null && {\n DefaultCooldown: Number(properties['Cooldown']),\n }),\n ...(properties['DefaultCooldown'] != null && {\n DefaultCooldown: Number(properties['DefaultCooldown']),\n }),\n ...(properties['TerminationPolicies'] !== undefined && {\n TerminationPolicies: properties['TerminationPolicies'] as string[],\n }),\n ...(properties['NewInstancesProtectedFromScaleIn'] !== undefined && {\n NewInstancesProtectedFromScaleIn: properties[\n 'NewInstancesProtectedFromScaleIn'\n ] as boolean,\n }),\n ...(properties['CapacityRebalance'] !== undefined && {\n CapacityRebalance: properties['CapacityRebalance'] as boolean,\n }),\n ...(properties['ServiceLinkedRoleARN'] !== undefined && {\n ServiceLinkedRoleARN: properties['ServiceLinkedRoleARN'] as string,\n }),\n ...(properties['MaxInstanceLifetime'] != null && {\n MaxInstanceLifetime: Number(properties['MaxInstanceLifetime']),\n }),\n ...(properties['Context'] !== undefined && {\n Context: properties['Context'] as string,\n }),\n ...(properties['DesiredCapacityType'] !== undefined && {\n DesiredCapacityType: properties['DesiredCapacityType'] as string,\n }),\n ...(properties['DefaultInstanceWarmup'] != null && {\n DefaultInstanceWarmup: Number(properties['DefaultInstanceWarmup']),\n }),\n ...(properties['AvailabilityZoneDistribution'] !== undefined && {\n AvailabilityZoneDistribution: properties['AvailabilityZoneDistribution'] as never,\n }),\n ...(properties['AvailabilityZoneImpairmentPolicy'] !== undefined && {\n AvailabilityZoneImpairmentPolicy: properties[\n 'AvailabilityZoneImpairmentPolicy'\n ] as never,\n }),\n ...(properties['SkipZonalShiftValidation'] !== undefined && {\n SkipZonalShiftValidation: properties['SkipZonalShiftValidation'] as boolean,\n }),\n ...(properties['CapacityReservationSpecification'] !== undefined && {\n CapacityReservationSpecification: properties[\n 'CapacityReservationSpecification'\n ] as never,\n }),\n ...(properties['InstanceMaintenancePolicy'] !== undefined && {\n InstanceMaintenancePolicy: properties['InstanceMaintenancePolicy'] as never,\n }),\n ...(properties['DeletionProtection'] !== undefined && {\n DeletionProtection: properties['DeletionProtection'] as never,\n }),\n })\n );\n\n this.logger.debug(`Successfully updated AutoScalingGroup ${logicalId}`);\n\n const arn = await this.fetchArn(physicalId);\n const attributes: Record<string, unknown> = {};\n if (arn) attributes['Arn'] = arn;\n if (launchTemplate?.LaunchTemplateId) {\n attributes['LaunchTemplateID'] = launchTemplate.LaunchTemplateId;\n }\n return { physicalId, wasReplaced: false, attributes };\n } catch (error) {\n if (error instanceof ResourceUpdateNotSupportedError) throw error;\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to update AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n async delete(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n _properties?: Record<string, unknown>,\n context?: DeleteContext\n ): Promise<void> {\n this.logger.debug(`Deleting AutoScalingGroup ${logicalId}: ${physicalId}`);\n\n // `--remove-protection`: clear DeletionProtection in-place before the\n // actual delete, then set ForceDelete=true so AWS terminates running\n // instances as part of the delete (matches the \"I know what I'm doing\"\n // intent of the flag). Without `removeProtection`, ForceDelete stays\n // false and AWS rejects the delete on a group with running instances\n // or DeletionProtection set, surfacing as ProvisioningError. The\n // flip-off is idempotent — AWS accepts UpdateAutoScalingGroup\n // (DeletionProtection: 'none') even when protection is already\n // disabled, so we always issue it under the flag.\n if (context?.removeProtection === true) {\n try {\n await this.getClient().send(\n new UpdateAutoScalingGroupCommand({\n AutoScalingGroupName: physicalId,\n DeletionProtection: 'none' as never,\n })\n );\n this.logger.debug(\n `Disabled DeletionProtection on AutoScalingGroup ${logicalId} before delete`\n );\n } catch (flipError) {\n // Non-fatal: log and proceed. The actual delete below surfaces\n // any real error.\n this.logger.debug(\n `Could not disable DeletionProtection on ${physicalId}: ${flipError instanceof Error ? flipError.message : String(flipError)}`\n );\n }\n\n // ASG-level DeletionProtection + ForceDelete only governs the GROUP and\n // its scale-in protection. If the group's launch template sets\n // EC2-level termination protection (DisableApiTermination), the\n // ForceDelete below still cannot terminate those instances and they\n // ORPHAN after the group is gone (issue #796). Enumerate the group's\n // current instances and flip each one's DisableApiTermination off first,\n // mirroring the EC2Provider `--remove-protection` path.\n await this.removeInstanceTerminationProtection(physicalId, logicalId);\n }\n\n try {\n await this.getClient().send(\n new DeleteAutoScalingGroupCommand({\n AutoScalingGroupName: physicalId,\n ForceDelete: context?.removeProtection === true,\n })\n );\n\n this.logger.debug(`Successfully initiated deletion of AutoScalingGroup ${logicalId}`);\n\n // Wait for the group to be fully gone. ASG delete is asynchronous —\n // returning immediately would leave dependent EC2 / IAM / SG\n // resources blocked on the lingering group.\n await this.waitForGroupDeleted(physicalId);\n } catch (error) {\n if (this.isNotFoundError(error)) {\n const clientRegion = await this.getClient().config.region();\n assertRegionMatch(\n clientRegion,\n context?.expectedRegion,\n resourceType,\n logicalId,\n physicalId\n );\n this.logger.debug(`AutoScalingGroup ${physicalId} does not exist, skipping deletion`);\n return;\n }\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to delete AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n async getAttribute(\n physicalId: string,\n _resourceType: string,\n attributeName: string\n ): Promise<unknown> {\n const group = await this.describeGroup(physicalId);\n if (!group) {\n throw new ProvisioningError(\n `AutoScalingGroup ${physicalId} not found while resolving attribute ${attributeName}`,\n 'AWS::AutoScaling::AutoScalingGroup',\n physicalId,\n physicalId\n );\n }\n switch (attributeName) {\n case 'Arn':\n case 'AutoScalingGroupARN':\n return group.AutoScalingGroupARN ?? '';\n case 'LaunchConfigurationName':\n return group.LaunchConfigurationName ?? '';\n case 'LaunchTemplateID':\n case 'LaunchTemplateId':\n return group.LaunchTemplate?.LaunchTemplateId ?? '';\n default:\n return '';\n }\n }\n\n /**\n * Read the AWS-current AutoScalingGroup configuration in CFn-property shape.\n *\n * Surfaces the user-controllable subset of `DescribeAutoScalingGroups`,\n * with always-emit placeholders on user-controllable top-level keys per\n * the cdkd PR #145 always-emit convention so that v3 `observedProperties`\n * baseline catches console-side ADDs to fields a clean deploy did not\n * template (e.g. a console-set `DeletionProtection: 'prevent-force-deletion'`\n * on a group originally created without it).\n *\n * Sub-shapes (LifecycleHookSpecificationList / TrafficSources /\n * NotificationConfigurations) are surfaced via three parallel Describe\n * calls fired alongside the primary `DescribeAutoScalingGroups`. Each is\n * best-effort: a per-call failure (e.g. permissions gap on\n * `autoscaling:DescribeLifecycleHooks`) is logged at debug and the\n * matching key falls back to its always-emit `[]` placeholder rather\n * than aborting the whole drift read.\n *\n * `MetricsCollection` is reverse-mapped from `EnabledMetrics` (already\n * present on the primary `DescribeAutoScalingGroups` response, so no\n * extra call is needed).\n *\n * Returns `undefined` when the group is gone.\n */\n async readCurrentState(\n physicalId: string,\n _logicalId: string,\n _resourceType: string\n ): Promise<Record<string, unknown> | undefined> {\n // Fire the four reads in parallel. Sub-shape failures are best-effort\n // so a single permission gap does not break the whole drift read.\n const groupPromise = (async () => {\n try {\n return await this.describeGroup(physicalId);\n } catch (err) {\n if (this.isNotFoundError(err)) return undefined;\n throw err;\n }\n })();\n\n const lifecycleHooksPromise = this.getClient()\n .send(new DescribeLifecycleHooksCommand({ AutoScalingGroupName: physicalId }))\n .then((r) => r.LifecycleHooks ?? [])\n .catch((err) => {\n this.logger.debug(\n `DescribeLifecycleHooks(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return [];\n });\n\n const trafficSourcesPromise = this.getClient()\n .send(new DescribeTrafficSourcesCommand({ AutoScalingGroupName: physicalId }))\n .then((r) => r.TrafficSources ?? [])\n .catch((err) => {\n this.logger.debug(\n `DescribeTrafficSources(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return [];\n });\n\n const notificationsPromise = this.getClient()\n .send(new DescribeNotificationConfigurationsCommand({ AutoScalingGroupNames: [physicalId] }))\n .then((r) => r.NotificationConfigurations ?? [])\n .catch((err) => {\n this.logger.debug(\n `DescribeNotificationConfigurations(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return [];\n });\n\n const [group, lifecycleHooks, trafficSources, notifications] = await Promise.all([\n groupPromise,\n lifecycleHooksPromise,\n trafficSourcesPromise,\n notificationsPromise,\n ]);\n\n if (!group) return undefined;\n\n const result: Record<string, unknown> = {};\n if (group.AutoScalingGroupName !== undefined) {\n result['AutoScalingGroupName'] = group.AutoScalingGroupName;\n }\n if (group.LaunchTemplate) {\n const lt: Record<string, unknown> = {};\n if (group.LaunchTemplate.LaunchTemplateId !== undefined) {\n lt['LaunchTemplateId'] = group.LaunchTemplate.LaunchTemplateId;\n }\n if (group.LaunchTemplate.LaunchTemplateName !== undefined) {\n lt['LaunchTemplateName'] = group.LaunchTemplate.LaunchTemplateName;\n }\n if (group.LaunchTemplate.Version !== undefined) {\n lt['Version'] = group.LaunchTemplate.Version;\n }\n result['LaunchTemplate'] = lt;\n }\n result['MinSize'] = group.MinSize ?? 0;\n result['MaxSize'] = group.MaxSize ?? 0;\n if (group.DesiredCapacity !== undefined) result['DesiredCapacity'] = group.DesiredCapacity;\n // VPCZoneIdentifier round-trips back to the CFn list shape so the\n // comparator sees the same array the template emitted, not the\n // SDK-side comma-joined string.\n if (group.VPCZoneIdentifier !== undefined && group.VPCZoneIdentifier !== '') {\n result['VPCZoneIdentifier'] = group.VPCZoneIdentifier.split(',').map((s) => s.trim());\n } else {\n result['VPCZoneIdentifier'] = [];\n }\n result['AvailabilityZones'] = group.AvailabilityZones ?? [];\n if (group.HealthCheckType !== undefined) result['HealthCheckType'] = group.HealthCheckType;\n if (group.HealthCheckGracePeriod !== undefined) {\n result['HealthCheckGracePeriod'] = group.HealthCheckGracePeriod;\n }\n if (group.DefaultCooldown !== undefined) {\n // CFn template field is `Cooldown`; SDK / Describe response calls it\n // `DefaultCooldown`. Surface under the CFn name so the comparator\n // matches state directly.\n result['Cooldown'] = group.DefaultCooldown;\n }\n result['NewInstancesProtectedFromScaleIn'] = group.NewInstancesProtectedFromScaleIn ?? false;\n result['TerminationPolicies'] = group.TerminationPolicies ?? [];\n result['CapacityRebalance'] = group.CapacityRebalance ?? false;\n if (group.ServiceLinkedRoleARN !== undefined) {\n result['ServiceLinkedRoleARN'] = group.ServiceLinkedRoleARN;\n }\n if (group.MaxInstanceLifetime !== undefined) {\n result['MaxInstanceLifetime'] = group.MaxInstanceLifetime;\n }\n result['LoadBalancerNames'] = group.LoadBalancerNames ?? [];\n result['TargetGroupARNs'] = group.TargetGroupARNs ?? [];\n if (group.Context !== undefined) result['Context'] = group.Context;\n if (group.DesiredCapacityType !== undefined) {\n result['DesiredCapacityType'] = group.DesiredCapacityType;\n }\n if (group.DefaultInstanceWarmup !== undefined) {\n result['DefaultInstanceWarmup'] = group.DefaultInstanceWarmup;\n }\n if (group.MixedInstancesPolicy !== undefined) {\n result['MixedInstancesPolicy'] = group.MixedInstancesPolicy;\n }\n if (group.AvailabilityZoneDistribution !== undefined) {\n result['AvailabilityZoneDistribution'] = group.AvailabilityZoneDistribution;\n }\n if (group.AvailabilityZoneImpairmentPolicy !== undefined) {\n result['AvailabilityZoneImpairmentPolicy'] = group.AvailabilityZoneImpairmentPolicy;\n }\n if (group.CapacityReservationSpecification !== undefined) {\n result['CapacityReservationSpecification'] = group.CapacityReservationSpecification;\n }\n if (group.InstanceMaintenancePolicy !== undefined) {\n result['InstanceMaintenancePolicy'] = group.InstanceMaintenancePolicy;\n }\n if (group.DeletionProtection !== undefined) {\n result['DeletionProtection'] = group.DeletionProtection;\n } else {\n // AWS reports `undefined` when the group has the AWS-side default\n // (`'none'`). Always-emit placeholder so the v3 `observedProperties`\n // baseline catches a console-side flip to `prevent-force-deletion`\n // / `prevent-all-deletion`.\n result['DeletionProtection'] = 'none';\n }\n // Tags: filter aws:* prefix and normalize to CFn shape sorted by Key.\n // ASG returns Tags inside the AutoScalingGroup record (already populated\n // by DescribeAutoScalingGroups — no separate ListTagsForResource call).\n result['Tags'] = normalizeAwsTagsToCfn(group.Tags);\n\n // Sub-shapes — reverse-map AWS responses to CFn template shape and\n // always-emit `[]` placeholders so the v3 `observedProperties` baseline\n // catches console-side ADDs to a previously-empty list.\n result['MetricsCollection'] = mapEnabledMetricsToCfn(group.EnabledMetrics);\n result['LifecycleHookSpecificationList'] = mapLifecycleHooksToCfn(lifecycleHooks);\n // Strip ALL elbv2 / elb entries from TrafficSources — the canonical\n // attachment state for these types lives in TargetGroupARNs /\n // LoadBalancerNames. TrafficSources is meant for attachment types\n // without a dedicated CFn property (VPC Lattice, VPC Endpoint\n // Service). Filtering unconditionally avoids two failure modes\n // surfaced by tests/integration/drift-revert-vpc (PR #547):\n // double-attach/detach on revert, and stale TS entries from AWS's\n // eventual-consistency window after Attach/Detach surfacing as\n // false drift on the next read.\n const dedupedTrafficSources = trafficSources.filter((t) => {\n if (t.Identifier === undefined) return false;\n if (t.Type === 'elbv2' || t.Type === 'elb') return false;\n return true;\n });\n result['TrafficSources'] = mapTrafficSourcesToCfn(dedupedTrafficSources);\n result['NotificationConfigurations'] = mapNotificationsToCfn(notifications);\n\n return result;\n }\n\n // ─── Helpers ──────────────────────────────────────────────────────\n\n private buildLaunchTemplate(\n properties: Record<string, unknown>\n ): LaunchTemplateSpecification | undefined {\n const lt = properties['LaunchTemplate'] as\n | { LaunchTemplateId?: string; LaunchTemplateName?: string; Version?: string | number }\n | undefined;\n if (!lt) return undefined;\n const out: LaunchTemplateSpecification = {};\n // AWS UpdateAutoScalingGroup rejects when both LaunchTemplateId and\n // LaunchTemplateName are present in the same LaunchTemplate object\n // (\"Valid requests must contain either launchTemplateId or\n // LaunchTemplateName\"). DescribeAutoScalingGroups returns both, so\n // a straight readCurrentState → update round-trip on `drift --revert`\n // would hit this. Prefer the ID (canonical, doesn't change on LT\n // rename) and only fall back to Name when ID is absent.\n if (lt.LaunchTemplateId !== undefined) {\n out.LaunchTemplateId = lt.LaunchTemplateId;\n if (lt.LaunchTemplateName !== undefined) {\n // User templated BOTH — AWS would reject the resulting Create /\n // Update otherwise; we silently prefer the ID. Surface the\n // choice in --verbose so a user wondering why their Name didn't\n // take effect has an auditable signal.\n this.logger.debug(\n `buildLaunchTemplate: both LaunchTemplateId (${lt.LaunchTemplateId}) and LaunchTemplateName (${lt.LaunchTemplateName}) templated; dropping Name (#551)`\n );\n }\n } else if (lt.LaunchTemplateName !== undefined) {\n out.LaunchTemplateName = lt.LaunchTemplateName;\n }\n if (lt.Version !== undefined) {\n // Defensive coercion: AWS SDK `LaunchTemplateSpecification.Version`\n // is `string` and AWS rejects non-string forms with `Invalid\n // launch template version: either '$Default', '$Latest', or a\n // numeric version are allowed.`. cdkd's `IntrinsicResolver`\n // resolves `Fn::GetAtt <LaunchTemplate>.LatestVersionNumber`\n // through a per-type lookup; intermediate cases could surface\n // numeric values, so we coerce defensively.\n out.Version = String(lt.Version);\n }\n if (out.LaunchTemplateId === undefined && out.LaunchTemplateName === undefined) {\n return undefined;\n }\n return out;\n }\n\n /**\n * CFn `Tags` is `[{Key, Value, PropagateAtLaunch?}]`. AWS expects each\n * tag to also carry `ResourceId: <groupName>` and `ResourceType:\n * 'auto-scaling-group'`. We tack those on at create time so the SDK\n * input shape matches without forcing the user to template them.\n */\n private buildTags(groupName: string, properties: Record<string, unknown>): ASGTag[] {\n const raw = properties['Tags'] as\n | Array<{ Key?: string; Value?: string; PropagateAtLaunch?: boolean }>\n | undefined;\n if (!raw) return [];\n return raw\n .filter((t) => t.Key !== undefined)\n .map((t) => ({\n ResourceId: groupName,\n ResourceType: 'auto-scaling-group',\n Key: t.Key as string,\n Value: t.Value ?? '',\n PropagateAtLaunch: t.PropagateAtLaunch ?? false,\n }));\n }\n\n /**\n * CFn `VPCZoneIdentifier` is a list of subnet ids; the AWS SDK input\n * field is a comma-joined string.\n */\n private joinVpcZoneIdentifier(value: unknown): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (Array.isArray(value)) {\n const cleaned = value.map((v) => String(v).trim()).filter((v) => v.length > 0);\n if (cleaned.length === 0) return undefined;\n return cleaned.join(',');\n }\n if (typeof value === 'string') return value;\n return undefined;\n }\n\n private async describeGroup(groupName: string) {\n const response = await this.getClient().send(\n new DescribeAutoScalingGroupsCommand({\n AutoScalingGroupNames: [groupName],\n })\n );\n return response.AutoScalingGroups?.[0];\n }\n\n /**\n * Flip EC2-level termination protection (`DisableApiTermination`) off on\n * every instance currently launched by the group, so the subsequent\n * `DeleteAutoScalingGroup(ForceDelete: true)` can actually terminate them\n * instead of orphaning the protected instances (issue #796). Best-effort:\n * a Describe failure or a per-instance flip failure is logged at debug and\n * does not block the delete (the modify WRITE lags the terminate READ, so\n * the shared helper swallows propagation errors the same way the EC2 path\n * does — the orphan, if any, surfaces as a leftover instance the caller\n * can clean up rather than a hard delete failure).\n */\n private async removeInstanceTerminationProtection(\n groupName: string,\n logicalId: string\n ): Promise<void> {\n let instanceIds: string[];\n try {\n const group = await this.describeGroup(groupName);\n instanceIds = (group?.Instances ?? [])\n .map((i) => i.InstanceId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0);\n } catch (describeError) {\n this.logger.debug(\n `Could not enumerate instances of AutoScalingGroup ${logicalId} for termination-protection removal: ${describeError instanceof Error ? describeError.message : String(describeError)}`\n );\n return;\n }\n\n if (instanceIds.length === 0) return;\n\n this.logger.debug(\n `Disabling EC2 termination protection on ${instanceIds.length} instance(s) of AutoScalingGroup ${logicalId} before force delete`\n );\n for (const instanceId of instanceIds) {\n await disableInstanceApiTermination(this.getEc2Client(), instanceId, this.logger);\n }\n }\n\n private async fetchArn(groupName: string): Promise<string | undefined> {\n try {\n const group = await this.describeGroup(groupName);\n return group?.AutoScalingGroupARN;\n } catch (err) {\n this.logger.debug(\n `DescribeAutoScalingGroups(${groupName}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return undefined;\n }\n }\n\n private isNotFoundError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const name = (error as { name?: string }).name ?? '';\n const message = error.message.toLowerCase();\n // ASG returns ValidationError with message \"AutoScalingGroup name not\n // found\" rather than a typed NotFound exception; cover both shapes.\n return (\n name === 'ValidationError' &&\n (message.includes('autoscalinggroup name not found') ||\n message.includes('not found') ||\n message.includes('does not exist'))\n );\n }\n\n private async waitForGroupDeleted(groupName: string, maxWaitMs = 900_000): Promise<void> {\n const startTime = Date.now();\n let delay = 5_000;\n\n while (Date.now() - startTime < maxWaitMs) {\n try {\n const group = await this.describeGroup(groupName);\n if (!group) return;\n } catch (error) {\n if (this.isNotFoundError(error)) return;\n throw error;\n }\n\n await this.sleep(delay);\n delay = Math.min(delay * 2, 10_000);\n }\n\n throw new Error(\n `Timed out waiting for AutoScalingGroup ${groupName} to be deleted (15 minute cap)`\n );\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // ─── Sub-shape diff helpers ───────────────────────────────────────\n // Each helper is a no-op when before/after JSON is identical (the cheap\n // structural-equality check happens first; we only build SDK calls for\n // genuine diffs). Identity is positional within the array per CFn shape:\n // `MetricsCollection` keyed on `Granularity`, `LifecycleHookSpecification\n // List` on `LifecycleHookName`, `TrafficSources` on `Identifier`,\n // `NotificationConfigurations` on `TopicARN`.\n\n /**\n * Diff and apply changes to the ASG's `Tags` property via the\n * `CreateOrUpdateTags` / `DeleteTags` AWS APIs (#475). CFn Tags shape is\n * `[{Key, Value, PropagateAtLaunch}]`; AWS Tag input adds `ResourceId`\n * (= the ASG name) and `ResourceType: 'auto-scaling-group'`.\n *\n * Diff semantics:\n * - Removed keys → `DeleteTags`.\n * - Added keys → `CreateOrUpdateTags`.\n * - Modified value or `PropagateAtLaunch` flag → `CreateOrUpdateTags`\n * (the AWS API upserts by `(ResourceId, ResourceType, Key)` tuple, so\n * a single upsert call replaces the old value).\n *\n * No-op when before/after JSON is identical.\n */\n private async applyTagsDiff(physicalId: string, next: unknown, prev: unknown): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n type CfnTag = { Key?: string; Value?: string; PropagateAtLaunch?: boolean };\n const nextEntries = (Array.isArray(next) ? next : []) as CfnTag[];\n const prevEntries = (Array.isArray(prev) ? prev : []) as CfnTag[];\n const nextByKey = new Map<string, CfnTag>();\n for (const t of nextEntries) {\n if (t.Key) nextByKey.set(t.Key, t);\n }\n const prevByKey = new Map<string, CfnTag>();\n for (const t of prevEntries) {\n if (t.Key) prevByKey.set(t.Key, t);\n }\n // Delete keys removed from `next`.\n const toDelete: CfnTag[] = [];\n for (const [key, tag] of prevByKey) {\n if (!nextByKey.has(key)) toDelete.push(tag);\n }\n if (toDelete.length > 0) {\n await this.getClient().send(\n new DeleteTagsCommand({\n // DeleteTags is keyed only by (ResourceId, ResourceType, Key).\n // Intentionally omit `Value` / `PropagateAtLaunch`: AWS treats\n // those as additional match constraints, so passing the\n // cdkd-recorded values would silently no-op when a console-side\n // edit drifted them between deploys. cdkd owns the tag, so\n // delete-by-key matches the \"we own the resource\" intent.\n Tags: toDelete.map((t) => ({\n ResourceId: physicalId,\n ResourceType: 'auto-scaling-group',\n Key: t.Key as string,\n })),\n })\n );\n }\n // Upsert keys whose value / propagate-flag differs.\n const toUpsert: CfnTag[] = [];\n for (const [key, tag] of nextByKey) {\n const before = prevByKey.get(key);\n if (JSON.stringify(before) === JSON.stringify(tag)) continue;\n toUpsert.push(tag);\n }\n if (toUpsert.length > 0) {\n await this.getClient().send(\n new CreateOrUpdateTagsCommand({\n Tags: toUpsert.map((t) => ({\n ResourceId: physicalId,\n ResourceType: 'auto-scaling-group',\n Key: t.Key as string,\n ...(t.Value !== undefined && { Value: t.Value }),\n ...(t.PropagateAtLaunch !== undefined && {\n PropagateAtLaunch: t.PropagateAtLaunch,\n }),\n })),\n })\n );\n }\n }\n\n /**\n * Diff `LoadBalancerNames` (Classic Load Balancers) and issue\n * `AttachLoadBalancers` / `DetachLoadBalancers` for the delta (#476).\n * Names are opaque strings; AWS allows N attached LBs per ASG so this\n * helper batches every add into one Attach call and every remove into\n * one Detach call. No-op when before/after JSON is identical.\n */\n private async applyLoadBalancerNamesDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextNames = (Array.isArray(next) ? next : []).filter(\n (n): n is string => typeof n === 'string'\n );\n const prevNames = (Array.isArray(prev) ? prev : []).filter(\n (n): n is string => typeof n === 'string'\n );\n const nextSet = new Set(nextNames);\n const prevSet = new Set(prevNames);\n const toAttach = nextNames.filter((n) => !prevSet.has(n));\n const toDetach = prevNames.filter((n) => !nextSet.has(n));\n if (toDetach.length > 0) {\n await this.getClient().send(\n new DetachLoadBalancersCommand({\n AutoScalingGroupName: physicalId,\n LoadBalancerNames: toDetach,\n })\n );\n }\n if (toAttach.length > 0) {\n await this.getClient().send(\n new AttachLoadBalancersCommand({\n AutoScalingGroupName: physicalId,\n LoadBalancerNames: toAttach,\n })\n );\n }\n }\n\n /**\n * Diff `TargetGroupARNs` (ALB / NLB target groups) and issue\n * `AttachLoadBalancerTargetGroups` /\n * `DetachLoadBalancerTargetGroups` for the delta (#476). Target-group\n * ARNs are opaque strings; same per-call batching pattern as\n * `applyLoadBalancerNamesDiff`. No-op when before/after JSON is\n * identical.\n */\n private async applyTargetGroupArnsDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextArns = (Array.isArray(next) ? next : []).filter(\n (a): a is string => typeof a === 'string'\n );\n const prevArns = (Array.isArray(prev) ? prev : []).filter(\n (a): a is string => typeof a === 'string'\n );\n const nextSet = new Set(nextArns);\n const prevSet = new Set(prevArns);\n const toAttach = nextArns.filter((a) => !prevSet.has(a));\n const toDetach = prevArns.filter((a) => !nextSet.has(a));\n if (toDetach.length > 0) {\n await this.getClient().send(\n new DetachLoadBalancerTargetGroupsCommand({\n AutoScalingGroupName: physicalId,\n TargetGroupARNs: toDetach,\n })\n );\n }\n if (toAttach.length > 0) {\n await this.getClient().send(\n new AttachLoadBalancerTargetGroupsCommand({\n AutoScalingGroupName: physicalId,\n TargetGroupARNs: toAttach,\n })\n );\n }\n // AttachLoadBalancerTargetGroups is async — the target group starts in\n // 'Adding' state and only becomes visible in\n // DescribeAutoScalingGroups.TargetGroupARNs after AWS internal\n // propagation. A subsequent `cdkd drift` read right after the call\n // returns can otherwise see a stale snapshot and report drift\n // against the AWS-side empty list (surfaced by tests/integration/\n // drift-revert-vpc's step-6 \"drift again\" check). Bounded poll to\n // confirm the post-state matches the intent before returning so the\n // caller's next read is consistent.\n if (toDetach.length > 0 || toAttach.length > 0) {\n await this.waitForTargetGroupArnsConvergence(physicalId, new Set(nextArns));\n }\n }\n\n private static readonly TG_CONVERGENCE_TIMEOUT_MS = 30_000;\n private static readonly TG_CONVERGENCE_POLL_INTERVAL_MS = 1_000;\n\n private async waitForTargetGroupArnsConvergence(\n physicalId: string,\n expected: Set<string>\n ): Promise<void> {\n const deadlineMs = Date.now() + ASGProvider.TG_CONVERGENCE_TIMEOUT_MS;\n let lastObserved: Set<string> = new Set();\n while (Date.now() < deadlineMs) {\n let resp: DescribeAutoScalingGroupsCommandOutput | undefined;\n try {\n resp = await this.getClient().send(\n new DescribeAutoScalingGroupsCommand({ AutoScalingGroupNames: [physicalId] })\n );\n } catch (err) {\n // Transient throttle / network blip during the 30s window must\n // not throw out of applyTargetGroupArnsDiff — the Attach/Detach\n // already succeeded, and propagating would fail the whole\n // update path. Log + retry; the loop will fall through to the\n // timeout-warn path if the API is genuinely down.\n this.logger.debug(\n `applyTargetGroupArnsDiff convergence poll: transient error, retrying — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n await new Promise((r) => setTimeout(r, ASGProvider.TG_CONVERGENCE_POLL_INTERVAL_MS));\n continue;\n }\n lastObserved = new Set(resp.AutoScalingGroups?.[0]?.TargetGroupARNs ?? []);\n if (lastObserved.size === expected.size && [...expected].every((a) => lastObserved.has(a))) {\n return;\n }\n await new Promise((r) => setTimeout(r, ASGProvider.TG_CONVERGENCE_POLL_INTERVAL_MS));\n }\n // Timeout — surface as a warning rather than failure so the caller\n // still sees the SDK-side success; drift can re-report if the\n // propagation is still stuck. Includes observed vs expected so\n // post-mortem doesn't need a re-deploy.\n // Sort both sides before stringify for visual symmetry — expected\n // comes from the caller's insertion order, observed from AWS-side\n // order; eyeballing the diff in logs is easier when both are sorted.\n const expectedSorted = [...expected].sort();\n const observedSorted = [...lastObserved].sort();\n this.logger.warn(\n `applyTargetGroupArnsDiff: TG set did not converge within ${ASGProvider.TG_CONVERGENCE_TIMEOUT_MS}ms for ASG ${physicalId}. expected=${JSON.stringify(expectedSorted)} observed=${JSON.stringify(observedSorted)}`\n );\n }\n\n private async applyMetricsCollectionDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextEntries = (Array.isArray(next) ? next : []) as Array<{\n Granularity?: string;\n Metrics?: string[];\n }>;\n const prevEntries = (Array.isArray(prev) ? prev : []) as Array<{\n Granularity?: string;\n Metrics?: string[];\n }>;\n const prevByGranularity = new Map<string, string[] | undefined>();\n for (const e of prevEntries) {\n if (e.Granularity) prevByGranularity.set(e.Granularity, e.Metrics);\n }\n const nextByGranularity = new Map<string, string[] | undefined>();\n for (const e of nextEntries) {\n if (e.Granularity) nextByGranularity.set(e.Granularity, e.Metrics);\n }\n // Disable removed granularities first, then issue Enable for the\n // intended state of every Granularity in `next`. AWS treats Enable as\n // additive within a Granularity, so a remove-then-add pattern works\n // even when the Metrics list shrinks.\n for (const [granularity, metrics] of prevByGranularity) {\n if (!nextByGranularity.has(granularity)) {\n await this.getClient().send(\n new DisableMetricsCollectionCommand({\n AutoScalingGroupName: physicalId,\n ...(metrics && metrics.length > 0 ? { Metrics: metrics } : {}),\n })\n );\n }\n }\n for (const [granularity, metrics] of nextByGranularity) {\n const before = prevByGranularity.get(granularity);\n if (JSON.stringify(before ?? null) === JSON.stringify(metrics ?? null)) continue;\n // If the Metrics list shrunk, disable the removed metrics first\n // (AWS Enable is additive). When `metrics` is undefined or empty,\n // AWS treats that as \"all metrics\" — disable any prior subset\n // before re-enabling the full set.\n if (before && before.length > 0) {\n const removed = metrics ? before.filter((m) => !metrics.includes(m)) : [];\n if (removed.length > 0) {\n await this.getClient().send(\n new DisableMetricsCollectionCommand({\n AutoScalingGroupName: physicalId,\n Metrics: removed,\n })\n );\n }\n }\n await this.getClient().send(\n new EnableMetricsCollectionCommand({\n AutoScalingGroupName: physicalId,\n Granularity: granularity,\n ...(metrics && metrics.length > 0 ? { Metrics: metrics } : {}),\n })\n );\n }\n }\n\n private async applyLifecycleHooksDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextEntries = (Array.isArray(next) ? next : []) as Array<{\n LifecycleHookName?: string;\n LifecycleTransition?: string;\n RoleARN?: string;\n NotificationTargetARN?: string;\n NotificationMetadata?: string;\n HeartbeatTimeout?: number;\n DefaultResult?: string;\n }>;\n const prevEntries = (Array.isArray(prev) ? prev : []) as Array<{\n LifecycleHookName?: string;\n }>;\n const nextNames = new Set(\n nextEntries.map((e) => e.LifecycleHookName).filter((n): n is string => !!n)\n );\n // Delete hooks no longer in `next`.\n for (const e of prevEntries) {\n if (e.LifecycleHookName && !nextNames.has(e.LifecycleHookName)) {\n await this.getClient().send(\n new DeleteLifecycleHookCommand({\n AutoScalingGroupName: physicalId,\n LifecycleHookName: e.LifecycleHookName,\n })\n );\n }\n }\n // PutLifecycleHook is upsert — issue for every hook in `next` whose\n // shape differs from the matching `prev` entry.\n const prevByName = new Map<string, unknown>();\n for (const e of prevEntries) {\n if (e.LifecycleHookName) prevByName.set(e.LifecycleHookName, e);\n }\n for (const e of nextEntries) {\n if (!e.LifecycleHookName) continue;\n const prevHook = prevByName.get(e.LifecycleHookName);\n if (JSON.stringify(prevHook) === JSON.stringify(e)) continue;\n await this.getClient().send(\n new PutLifecycleHookCommand({\n AutoScalingGroupName: physicalId,\n LifecycleHookName: e.LifecycleHookName,\n ...(e.LifecycleTransition !== undefined && {\n LifecycleTransition: e.LifecycleTransition,\n }),\n ...(e.RoleARN !== undefined && { RoleARN: e.RoleARN }),\n ...(e.NotificationTargetARN !== undefined && {\n NotificationTargetARN: e.NotificationTargetARN,\n }),\n ...(e.NotificationMetadata !== undefined && {\n NotificationMetadata: e.NotificationMetadata,\n }),\n ...(e.HeartbeatTimeout !== undefined && { HeartbeatTimeout: e.HeartbeatTimeout }),\n ...(e.DefaultResult !== undefined && { DefaultResult: e.DefaultResult }),\n })\n );\n }\n }\n\n private async applyTrafficSourcesDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextEntries = (Array.isArray(next) ? next : []) as Array<{\n Identifier?: string;\n Type?: string;\n }>;\n const prevEntries = (Array.isArray(prev) ? prev : []) as Array<{\n Identifier?: string;\n Type?: string;\n }>;\n const nextIds = new Set(nextEntries.map((e) => e.Identifier).filter((i): i is string => !!i));\n const prevIds = new Set(prevEntries.map((e) => e.Identifier).filter((i): i is string => !!i));\n const toDetach = prevEntries.filter((e) => e.Identifier && !nextIds.has(e.Identifier));\n const toAttach = nextEntries.filter((e) => e.Identifier && !prevIds.has(e.Identifier));\n if (toDetach.length > 0) {\n await this.getClient().send(\n new DetachTrafficSourcesCommand({\n AutoScalingGroupName: physicalId,\n TrafficSources: toDetach.map((e) => ({\n Identifier: e.Identifier as string,\n ...(e.Type !== undefined && { Type: e.Type }),\n })),\n })\n );\n }\n if (toAttach.length > 0) {\n await this.getClient().send(\n new AttachTrafficSourcesCommand({\n AutoScalingGroupName: physicalId,\n TrafficSources: toAttach.map((e) => ({\n Identifier: e.Identifier as string,\n ...(e.Type !== undefined && { Type: e.Type }),\n })),\n })\n );\n }\n }\n\n private async applyNotificationConfigurationsDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n // CFn `NotificationConfigurations` is an array of `{TopicARN,\n // NotificationTypes[]}`; AWS `PutNotificationConfiguration` is keyed\n // by TopicARN — one call per topic. AWS reports each notification\n // type as a separate response entry (one row per `(asgName, topicArn,\n // notificationType)` triple), but cdkd state stores the CFn shape, so\n // both sides of the diff share the per-topic key.\n const nextEntries = (Array.isArray(next) ? next : []) as Array<{\n TopicARN?: string;\n NotificationTypes?: string[];\n }>;\n const prevEntries = (Array.isArray(prev) ? prev : []) as Array<{\n TopicARN?: string;\n NotificationTypes?: string[];\n }>;\n const nextByTopic = new Map<string, string[] | undefined>();\n for (const e of nextEntries) {\n if (e.TopicARN) nextByTopic.set(e.TopicARN, e.NotificationTypes);\n }\n const prevByTopic = new Map<string, string[] | undefined>();\n for (const e of prevEntries) {\n if (e.TopicARN) prevByTopic.set(e.TopicARN, e.NotificationTypes);\n }\n for (const topic of prevByTopic.keys()) {\n if (!nextByTopic.has(topic)) {\n await this.getClient().send(\n new DeleteNotificationConfigurationCommand({\n AutoScalingGroupName: physicalId,\n TopicARN: topic,\n })\n );\n }\n }\n for (const [topic, types] of nextByTopic) {\n const before = prevByTopic.get(topic);\n if (JSON.stringify(before ?? null) === JSON.stringify(types ?? null)) continue;\n await this.getClient().send(\n new PutNotificationConfigurationCommand({\n AutoScalingGroupName: physicalId,\n TopicARN: topic,\n NotificationTypes: types ?? [],\n })\n );\n }\n }\n}\n\n// ─── File-level reverse-mappers (CFn template shape) ────────────────\n\n/**\n * Reverse-map AWS `EnabledMetrics: [{Metric, Granularity}]` (flat list,\n * one row per enabled metric) back to the CFn array shape\n * `[{Granularity, Metrics?[]}]`. Metrics with the same Granularity are\n * grouped together; the resulting Metrics list is sorted alphabetically\n * for stable positional compare in the drift comparator.\n *\n * Always returns a placeholder `[]` per the cdkd PR #145 always-emit\n * convention so a console-side EnableMetricsCollection on a previously-\n * empty group surfaces as drift on the v3 `observedProperties` baseline.\n */\nfunction mapEnabledMetricsToCfn(\n enabledMetrics:\n | Array<{ Metric?: string | undefined; Granularity?: string | undefined }>\n | undefined\n): Array<{ Granularity: string; Metrics?: string[] }> {\n if (!enabledMetrics || enabledMetrics.length === 0) return [];\n const byGranularity = new Map<string, Set<string>>();\n for (const e of enabledMetrics) {\n const g = e.Granularity;\n if (!g) continue;\n let set = byGranularity.get(g);\n if (!set) {\n set = new Set();\n byGranularity.set(g, set);\n }\n if (e.Metric) set.add(e.Metric);\n }\n const result: Array<{ Granularity: string; Metrics?: string[] }> = [];\n // Sort by Granularity for stable positional compare.\n for (const granularity of Array.from(byGranularity.keys()).sort()) {\n const metrics = Array.from(byGranularity.get(granularity) ?? []).sort();\n result.push(\n metrics.length > 0\n ? { Granularity: granularity, Metrics: metrics }\n : { Granularity: granularity }\n );\n }\n return result;\n}\n\n/**\n * Reverse-map AWS `DescribeLifecycleHooks` response to the CFn\n * `LifecycleHookSpecificationList` shape. Each hook is surfaced under the\n * exact CFn property name. AWS-side fields cdkd state never carried\n * (`AutoScalingGroupName` — duplicated on every hook by AWS,\n * `GlobalTimeout` — AWS-derived) are filtered out. Sorted by\n * LifecycleHookName for stable positional compare.\n */\nfunction mapLifecycleHooksToCfn(\n hooks: Array<{\n LifecycleHookName?: string | undefined;\n LifecycleTransition?: string | undefined;\n NotificationTargetARN?: string | undefined;\n RoleARN?: string | undefined;\n NotificationMetadata?: string | undefined;\n HeartbeatTimeout?: number | undefined;\n DefaultResult?: string | undefined;\n }>\n): Array<Record<string, unknown>> {\n if (!hooks || hooks.length === 0) return [];\n const result: Array<Record<string, unknown>> = [];\n for (const h of hooks) {\n if (!h.LifecycleHookName) continue;\n const entry: Record<string, unknown> = { LifecycleHookName: h.LifecycleHookName };\n if (h.LifecycleTransition !== undefined) entry['LifecycleTransition'] = h.LifecycleTransition;\n if (h.RoleARN !== undefined) entry['RoleARN'] = h.RoleARN;\n if (h.NotificationTargetARN !== undefined) {\n entry['NotificationTargetARN'] = h.NotificationTargetARN;\n }\n if (h.NotificationMetadata !== undefined) {\n entry['NotificationMetadata'] = h.NotificationMetadata;\n }\n if (h.HeartbeatTimeout !== undefined) entry['HeartbeatTimeout'] = h.HeartbeatTimeout;\n if (h.DefaultResult !== undefined) entry['DefaultResult'] = h.DefaultResult;\n result.push(entry);\n }\n result.sort((a, b) =>\n String(a['LifecycleHookName']).localeCompare(String(b['LifecycleHookName']))\n );\n return result;\n}\n\n/**\n * Reverse-map AWS `DescribeTrafficSources` response to the CFn\n * `TrafficSources` shape `[{Identifier, Type?}]`. AWS-side runtime fields\n * (`State`, the deprecated `TrafficSource` alias) are filtered out.\n * Sorted by Identifier for stable positional compare.\n */\nfunction mapTrafficSourcesToCfn(\n trafficSources: Array<{ Identifier?: string | undefined; Type?: string | undefined }>\n): Array<Record<string, unknown>> {\n if (!trafficSources || trafficSources.length === 0) return [];\n const result: Array<Record<string, unknown>> = [];\n for (const t of trafficSources) {\n if (!t.Identifier) continue;\n const entry: Record<string, unknown> = { Identifier: t.Identifier };\n if (t.Type !== undefined) entry['Type'] = t.Type;\n result.push(entry);\n }\n result.sort((a, b) => String(a['Identifier']).localeCompare(String(b['Identifier'])));\n return result;\n}\n\n/**\n * Reverse-map AWS `DescribeNotificationConfigurations` (a flat list, one\n * row per `(topicArn, notificationType)`) into the CFn shape\n * `[{TopicARN, NotificationTypes[]}]`. NotificationTypes are grouped per\n * TopicARN and sorted alphabetically for stable positional compare.\n */\nfunction mapNotificationsToCfn(\n configurations: Array<{ TopicARN?: string | undefined; NotificationType?: string | undefined }>\n): Array<Record<string, unknown>> {\n if (!configurations || configurations.length === 0) return [];\n const byTopic = new Map<string, Set<string>>();\n for (const c of configurations) {\n if (!c.TopicARN) continue;\n let set = byTopic.get(c.TopicARN);\n if (!set) {\n set = new Set();\n byTopic.set(c.TopicARN, set);\n }\n if (c.NotificationType) set.add(c.NotificationType);\n }\n const result: Array<Record<string, unknown>> = [];\n for (const topic of Array.from(byTopic.keys()).sort()) {\n const types = Array.from(byTopic.get(topic) ?? []).sort();\n result.push({ TopicARN: topic, NotificationTypes: types });\n }\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,IAAa,cAAb,MAAa,YAAwC;CACnD,AAAQ;CACR,AAAQ;CACR,AAAiB,iBAAiB,QAAQ,IAAI;CAC9C,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,aAAa;CAEhD,oCAAoB,IAAI,IAAiC,CACvD,CACE,sDACA,IAAI,IAAI;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CACH,CACF,CAAC;CAED,oCAAoB,IAAI,IAAyC,CAC/D,CACE,sDACA,IAAI,IAAoB,CACtB,CACE,2BACA,2EACF,GACA,CACE,6BACA,wFACF,CACF,CAAC,CACH,CACF,CAAC;CAED,AAAQ,YAA+B;EACrC,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,kBACnB,KAAK,iBAAiB,EAAE,QAAQ,KAAK,eAAe,IAAI,CAAC,CAC3D;EAEF,OAAO,KAAK;CACd;CAEA,AAAQ,eAA0B;EAChC,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU,KAAK,iBAAiB,EAAE,QAAQ,KAAK,eAAe,IAAI,CAAC,CAAC;EAE3F,OAAO,KAAK;CACd;CAIA,MAAM,OACJ,WACA,cACA,YAC+B;EAC/B,IAAI,iBAAiB,sCACnB,MAAM,IAAI,kBACR,8BAA8B,gBAC9B,cACA,SACF;EAGF,MAAM,YACH,WAAW,2BACZ,qBAAqB,WAAW,EAAE,WAAW,IAAI,CAAC;EAEpD,KAAK,OAAO,MAAM,6BAA6B,UAAU,IAAI,WAAW;EAExE,IAAI;GACF,MAAM,iBAAiB,KAAK,oBAAoB,UAAU;GAC1D,MAAM,OAAO,KAAK,UAAU,WAAW,UAAU;GACjD,MAAM,oBAAoB,KAAK,sBAAsB,WAAW,oBAAoB;GAEpF,MAAM,UAAU,WAAW,cAAc,OAAO,OAAO,WAAW,UAAU,IAAI;GAChF,MAAM,UAAU,WAAW,cAAc,OAAO,OAAO,WAAW,UAAU,IAAI;GAEhF,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,8BAA8B;IAChC,sBAAsB;IACtB,SAAS;IACT,SAAS;IACT,GAAI,WAAW,sBAAsB,QAAQ,EAC3C,iBAAiB,OAAO,WAAW,kBAAkB,EACvD;IACA,GAAI,kBAAkB,EAAE,gBAAgB,eAAe;IACvD,GAAI,WAAW,4BAA4B,UAAa,EACtD,sBAAsB,WAAW,wBACnC;IACA,GAAI,sBAAsB,UAAa,EAAE,mBAAmB,kBAAkB;IAC9E,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,uBAAuB,UAAa,EACjD,iBAAiB,WAAW,mBAC9B;IACA,GAAI,WAAW,6BAA6B,QAAQ,EAClD,wBAAwB,OAAO,WAAW,yBAAyB,EACrE;IACA,GAAI,WAAW,eAAe,QAAQ,EACpC,iBAAiB,OAAO,WAAW,WAAW,EAChD;IACA,GAAI,WAAW,sBAAsB,QAAQ,EAC3C,iBAAiB,OAAO,WAAW,kBAAkB,EACvD;IACA,GAAI,WAAW,2BAA2B,UAAa,EACrD,qBAAqB,WAAW,uBAClC;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,4BAA4B,UAAa,EACtD,sBAAsB,WAAW,wBACnC;IACA,GAAI,WAAW,0BAA0B,QAAQ,EAC/C,qBAAqB,OAAO,WAAW,sBAAsB,EAC/D;IACA,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,uBAAuB,UAAa,EACjD,iBAAiB,WAAW,mBAC9B;IACA,GAAI,WAAW,eAAe,UAAa,EACzC,SAAS,WAAW,WACtB;IACA,GAAI,WAAW,2BAA2B,UAAa,EACrD,qBAAqB,WAAW,uBAClC;IACA,GAAI,WAAW,4BAA4B,QAAQ,EACjD,uBAAuB,OAAO,WAAW,wBAAwB,EACnE;IACA,GAAI,WAAW,sCAAsC,UAAa,EAChE,gCAAgC,WAAW,kCAC7C;IACA,GAAI,WAAW,sBAAsB,UAAa,EAChD,gBAAgB,WAAW,kBAC7B;IACA,GAAI,WAAW,oCAAoC,UAAa,EAC9D,8BAA8B,WAAW,gCAC3C;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,gCAAgC,UAAa,EAC1D,0BAA0B,WAAW,4BACvC;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,iCAAiC,UAAa,EAC3D,2BAA2B,WAAW,6BACxC;IACA,GAAI,WAAW,0BAA0B,UAAa,EACpD,oBAAoB,WAAW,sBACjC;IACA,GAAI,KAAK,SAAS,KAAK,EAAE,MAAM,KAAK;GACtC,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,yCAAyC,UAAU,IAAI,WAAW;GAEpF,MAAM,MAAM,MAAM,KAAK,SAAS,SAAS;GACzC,MAAM,aAAsC,CAAC;GAC7C,IAAI,KAAK,WAAW,SAAS;GAC7B,IAAI,gBAAgB,kBAClB,WAAW,sBAAsB,eAAe;GAElD,OAAO;IAAE,YAAY;IAAW;GAAW;EAC7C,SAAS,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,qCAAqC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACxG,cACA,WACA,WACA,KACF;EACF;CACF;CAEA,MAAM,OACJ,WACA,YACA,cACA,YACA,oBAC+B;EAC/B,IAAI,iBAAiB,sCACnB,MAAM,IAAI,kBACR,8BAA8B,gBAC9B,cACA,WACA,UACF;EAEF,KAAK,OAAO,MAAM,6BAA6B,UAAU,IAAI,YAAY;EAOzE,MAAM,YAAY,GAAY,MAAwB,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;EAC5F,IAAI,CAAC,SAAS,WAAW,yBAAyB,mBAAmB,uBAAuB,GAC1F,MAAM,IAAI,gCACR,cACA,WACA,8KACF;EAEF,IAAI;GAMF,MAAM,KAAK,cAAc,YAAY,WAAW,SAAS,mBAAmB,OAAO;GACnF,MAAM,KAAK,2BACT,YACA,WAAW,sBACX,mBAAmB,oBACrB;GACA,MAAM,KAAK,yBACT,YACA,WAAW,oBACX,mBAAmB,kBACrB;GACA,MAAM,KAAK,2BACT,YACA,WAAW,sBACX,mBAAmB,oBACrB;GACA,MAAM,KAAK,wBACT,YACA,WAAW,mCACX,mBAAmB,iCACrB;GACA,MAAM,KAAK,wBACT,YACA,WAAW,mBACX,mBAAmB,iBACrB;GACA,MAAM,KAAK,oCACT,YACA,WAAW,+BACX,mBAAmB,6BACrB;GAEA,MAAM,iBAAiB,KAAK,oBAAoB,UAAU;GAC1D,MAAM,oBAAoB,KAAK,sBAAsB,WAAW,oBAAoB;GAEpF,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,8BAA8B;IAChC,sBAAsB;IACtB,GAAI,WAAW,cAAc,QAAQ,EAAE,SAAS,OAAO,WAAW,UAAU,EAAE;IAC9E,GAAI,WAAW,cAAc,QAAQ,EAAE,SAAS,OAAO,WAAW,UAAU,EAAE;IAC9E,GAAI,WAAW,sBAAsB,QAAQ,EAC3C,iBAAiB,OAAO,WAAW,kBAAkB,EACvD;IACA,GAAI,kBAAkB,EAAE,gBAAgB,eAAe;IACvD,GAAI,WAAW,4BAA4B,UAAa,EACtD,sBAAsB,WAAW,wBACnC;IACA,GAAI,sBAAsB,UAAa,EAAE,mBAAmB,kBAAkB;IAC9E,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,uBAAuB,UAAa,EACjD,iBAAiB,WAAW,mBAC9B;IACA,GAAI,WAAW,6BAA6B,QAAQ,EAClD,wBAAwB,OAAO,WAAW,yBAAyB,EACrE;IACA,GAAI,WAAW,eAAe,QAAQ,EACpC,iBAAiB,OAAO,WAAW,WAAW,EAChD;IACA,GAAI,WAAW,sBAAsB,QAAQ,EAC3C,iBAAiB,OAAO,WAAW,kBAAkB,EACvD;IACA,GAAI,WAAW,2BAA2B,UAAa,EACrD,qBAAqB,WAAW,uBAClC;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,4BAA4B,UAAa,EACtD,sBAAsB,WAAW,wBACnC;IACA,GAAI,WAAW,0BAA0B,QAAQ,EAC/C,qBAAqB,OAAO,WAAW,sBAAsB,EAC/D;IACA,GAAI,WAAW,eAAe,UAAa,EACzC,SAAS,WAAW,WACtB;IACA,GAAI,WAAW,2BAA2B,UAAa,EACrD,qBAAqB,WAAW,uBAClC;IACA,GAAI,WAAW,4BAA4B,QAAQ,EACjD,uBAAuB,OAAO,WAAW,wBAAwB,EACnE;IACA,GAAI,WAAW,oCAAoC,UAAa,EAC9D,8BAA8B,WAAW,gCAC3C;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,gCAAgC,UAAa,EAC1D,0BAA0B,WAAW,4BACvC;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,iCAAiC,UAAa,EAC3D,2BAA2B,WAAW,6BACxC;IACA,GAAI,WAAW,0BAA0B,UAAa,EACpD,oBAAoB,WAAW,sBACjC;GACF,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,yCAAyC,WAAW;GAEtE,MAAM,MAAM,MAAM,KAAK,SAAS,UAAU;GAC1C,MAAM,aAAsC,CAAC;GAC7C,IAAI,KAAK,WAAW,SAAS;GAC7B,IAAI,gBAAgB,kBAClB,WAAW,sBAAsB,eAAe;GAElD,OAAO;IAAE;IAAY,aAAa;IAAO;GAAW;EACtD,SAAS,OAAO;GACd,IAAI,iBAAiB,iCAAiC,MAAM;GAC5D,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,qCAAqC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACxG,cACA,WACA,YACA,KACF;EACF;CACF;CAEA,MAAM,OACJ,WACA,YACA,cACA,aACA,SACe;EACf,KAAK,OAAO,MAAM,6BAA6B,UAAU,IAAI,YAAY;EAWzE,IAAI,SAAS,qBAAqB,MAAM;GACtC,IAAI;IACF,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,8BAA8B;KAChC,sBAAsB;KACtB,oBAAoB;IACtB,CAAC,CACH;IACA,KAAK,OAAO,MACV,mDAAmD,UAAU,eAC/D;GACF,SAAS,WAAW;IAGlB,KAAK,OAAO,MACV,2CAA2C,WAAW,IAAI,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,GAC7H;GACF;GASA,MAAM,KAAK,oCAAoC,YAAY,SAAS;EACtE;EAEA,IAAI;GACF,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,8BAA8B;IAChC,sBAAsB;IACtB,aAAa,SAAS,qBAAqB;GAC7C,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,uDAAuD,WAAW;GAKpF,MAAM,KAAK,oBAAoB,UAAU;EAC3C,SAAS,OAAO;GACd,IAAI,KAAK,gBAAgB,KAAK,GAAG;IAE/B,kBACE,MAFyB,KAAK,UAAU,CAAC,CAAC,OAAO,OAAO,GAGxD,SAAS,gBACT,cACA,WACA,UACF;IACA,KAAK,OAAO,MAAM,oBAAoB,WAAW,mCAAmC;IACpF;GACF;GACA,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,qCAAqC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACxG,cACA,WACA,YACA,KACF;EACF;CACF;CAEA,MAAM,aACJ,YACA,eACA,eACkB;EAClB,MAAM,QAAQ,MAAM,KAAK,cAAc,UAAU;EACjD,IAAI,CAAC,OACH,MAAM,IAAI,kBACR,oBAAoB,WAAW,uCAAuC,iBACtE,sCACA,YACA,UACF;EAEF,QAAQ,eAAR;GACE,KAAK;GACL,KAAK,uBACH,OAAO,MAAM,uBAAuB;GACtC,KAAK,2BACH,OAAO,MAAM,2BAA2B;GAC1C,KAAK;GACL,KAAK,oBACH,OAAO,MAAM,gBAAgB,oBAAoB;GACnD,SACE,OAAO;EACX;CACF;;;;;;;;;;;;;;;;;;;;;;;;;CA0BA,MAAM,iBACJ,YACA,YACA,eAC8C;EAG9C,MAAM,gBAAgB,YAAY;GAChC,IAAI;IACF,OAAO,MAAM,KAAK,cAAc,UAAU;GAC5C,SAAS,KAAK;IACZ,IAAI,KAAK,gBAAgB,GAAG,GAAG,OAAO;IACtC,MAAM;GACR;EACF,EAAC,CAAE;EAEH,MAAM,wBAAwB,KAAK,UAAU,CAAC,CAC3C,KAAK,IAAI,8BAA8B,EAAE,sBAAsB,WAAW,CAAC,CAAC,CAAC,CAC7E,MAAM,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CACnC,OAAO,QAAQ;GACd,KAAK,OAAO,MACV,0BAA0B,WAAW,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAClG;GACA,OAAO,CAAC;EACV,CAAC;EAEH,MAAM,wBAAwB,KAAK,UAAU,CAAC,CAC3C,KAAK,IAAI,8BAA8B,EAAE,sBAAsB,WAAW,CAAC,CAAC,CAAC,CAC7E,MAAM,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CACnC,OAAO,QAAQ;GACd,KAAK,OAAO,MACV,0BAA0B,WAAW,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAClG;GACA,OAAO,CAAC;EACV,CAAC;EAEH,MAAM,uBAAuB,KAAK,UAAU,CAAC,CAC1C,KAAK,IAAI,0CAA0C,EAAE,uBAAuB,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAC5F,MAAM,MAAM,EAAE,8BAA8B,CAAC,CAAC,CAAC,CAC/C,OAAO,QAAQ;GACd,KAAK,OAAO,MACV,sCAAsC,WAAW,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAC9G;GACA,OAAO,CAAC;EACV,CAAC;EAEH,MAAM,CAAC,OAAO,gBAAgB,gBAAgB,iBAAiB,MAAM,QAAQ,IAAI;GAC/E;GACA;GACA;GACA;EACF,CAAC;EAED,IAAI,CAAC,OAAO,OAAO;EAEnB,MAAM,SAAkC,CAAC;EACzC,IAAI,MAAM,yBAAyB,QACjC,OAAO,0BAA0B,MAAM;EAEzC,IAAI,MAAM,gBAAgB;GACxB,MAAM,KAA8B,CAAC;GACrC,IAAI,MAAM,eAAe,qBAAqB,QAC5C,GAAG,sBAAsB,MAAM,eAAe;GAEhD,IAAI,MAAM,eAAe,uBAAuB,QAC9C,GAAG,wBAAwB,MAAM,eAAe;GAElD,IAAI,MAAM,eAAe,YAAY,QACnC,GAAG,aAAa,MAAM,eAAe;GAEvC,OAAO,oBAAoB;EAC7B;EACA,OAAO,aAAa,MAAM,WAAW;EACrC,OAAO,aAAa,MAAM,WAAW;EACrC,IAAI,MAAM,oBAAoB,QAAW,OAAO,qBAAqB,MAAM;EAI3E,IAAI,MAAM,sBAAsB,UAAa,MAAM,sBAAsB,IACvE,OAAO,uBAAuB,MAAM,kBAAkB,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC;OAEpF,OAAO,uBAAuB,CAAC;EAEjC,OAAO,uBAAuB,MAAM,qBAAqB,CAAC;EAC1D,IAAI,MAAM,oBAAoB,QAAW,OAAO,qBAAqB,MAAM;EAC3E,IAAI,MAAM,2BAA2B,QACnC,OAAO,4BAA4B,MAAM;EAE3C,IAAI,MAAM,oBAAoB,QAI5B,OAAO,cAAc,MAAM;EAE7B,OAAO,sCAAsC,MAAM,oCAAoC;EACvF,OAAO,yBAAyB,MAAM,uBAAuB,CAAC;EAC9D,OAAO,uBAAuB,MAAM,qBAAqB;EACzD,IAAI,MAAM,yBAAyB,QACjC,OAAO,0BAA0B,MAAM;EAEzC,IAAI,MAAM,wBAAwB,QAChC,OAAO,yBAAyB,MAAM;EAExC,OAAO,uBAAuB,MAAM,qBAAqB,CAAC;EAC1D,OAAO,qBAAqB,MAAM,mBAAmB,CAAC;EACtD,IAAI,MAAM,YAAY,QAAW,OAAO,aAAa,MAAM;EAC3D,IAAI,MAAM,wBAAwB,QAChC,OAAO,yBAAyB,MAAM;EAExC,IAAI,MAAM,0BAA0B,QAClC,OAAO,2BAA2B,MAAM;EAE1C,IAAI,MAAM,yBAAyB,QACjC,OAAO,0BAA0B,MAAM;EAEzC,IAAI,MAAM,iCAAiC,QACzC,OAAO,kCAAkC,MAAM;EAEjD,IAAI,MAAM,qCAAqC,QAC7C,OAAO,sCAAsC,MAAM;EAErD,IAAI,MAAM,qCAAqC,QAC7C,OAAO,sCAAsC,MAAM;EAErD,IAAI,MAAM,8BAA8B,QACtC,OAAO,+BAA+B,MAAM;EAE9C,IAAI,MAAM,uBAAuB,QAC/B,OAAO,wBAAwB,MAAM;OAMrC,OAAO,wBAAwB;EAKjC,OAAO,UAAU,sBAAsB,MAAM,IAAI;EAKjD,OAAO,uBAAuB,uBAAuB,MAAM,cAAc;EACzE,OAAO,oCAAoC,uBAAuB,cAAc;EAehF,OAAO,oBAAoB,uBALG,eAAe,QAAQ,MAAM;GACzD,IAAI,EAAE,eAAe,QAAW,OAAO;GACvC,IAAI,EAAE,SAAS,WAAW,EAAE,SAAS,OAAO,OAAO;GACnD,OAAO;EACT,CACsE,CAAC;EACvE,OAAO,gCAAgC,sBAAsB,aAAa;EAE1E,OAAO;CACT;CAIA,AAAQ,oBACN,YACyC;EACzC,MAAM,KAAK,WAAW;EAGtB,IAAI,CAAC,IAAI,OAAO;EAChB,MAAM,MAAmC,CAAC;EAQ1C,IAAI,GAAG,qBAAqB,QAAW;GACrC,IAAI,mBAAmB,GAAG;GAC1B,IAAI,GAAG,uBAAuB,QAK5B,KAAK,OAAO,MACV,+CAA+C,GAAG,iBAAiB,4BAA4B,GAAG,mBAAmB,kCACvH;EAEJ,OAAO,IAAI,GAAG,uBAAuB,QACnC,IAAI,qBAAqB,GAAG;EAE9B,IAAI,GAAG,YAAY,QAQjB,IAAI,UAAU,OAAO,GAAG,OAAO;EAEjC,IAAI,IAAI,qBAAqB,UAAa,IAAI,uBAAuB,QACnE;EAEF,OAAO;CACT;;;;;;;CAQA,AAAQ,UAAU,WAAmB,YAA+C;EAClF,MAAM,MAAM,WAAW;EAGvB,IAAI,CAAC,KAAK,OAAO,CAAC;EAClB,OAAO,IACJ,QAAQ,MAAM,EAAE,QAAQ,MAAS,CAAC,CAClC,KAAK,OAAO;GACX,YAAY;GACZ,cAAc;GACd,KAAK,EAAE;GACP,OAAO,EAAE,SAAS;GAClB,mBAAmB,EAAE,qBAAqB;EAC5C,EAAE;CACN;;;;;CAMA,AAAQ,sBAAsB,OAAoC;EAChE,IAAI,UAAU,UAAa,UAAU,MAAM,OAAO;EAClD,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,MAAM,UAAU,MAAM,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,MAAM,EAAE,SAAS,CAAC;GAC7E,IAAI,QAAQ,WAAW,GAAG,OAAO;GACjC,OAAO,QAAQ,KAAK,GAAG;EACzB;EACA,IAAI,OAAO,UAAU,UAAU,OAAO;CAExC;CAEA,MAAc,cAAc,WAAmB;EAM7C,QAAO,MALgB,KAAK,UAAU,CAAC,CAAC,KACtC,IAAI,iCAAiC,EACnC,uBAAuB,CAAC,SAAS,EACnC,CAAC,CACH,EACe,CAAC,oBAAoB;CACtC;;;;;;;;;;;;CAaA,MAAc,oCACZ,WACA,WACe;EACf,IAAI;EACJ,IAAI;GAEF,gBAAe,MADK,KAAK,cAAc,SAAS,EAC5B,EAAE,aAAa,CAAC,EAAC,CAClC,KAAK,MAAM,EAAE,UAAU,CAAC,CACxB,QAAQ,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;EACzE,SAAS,eAAe;GACtB,KAAK,OAAO,MACV,qDAAqD,UAAU,uCAAuC,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa,GACrL;GACA;EACF;EAEA,IAAI,YAAY,WAAW,GAAG;EAE9B,KAAK,OAAO,MACV,2CAA2C,YAAY,OAAO,mCAAmC,UAAU,qBAC7G;EACA,KAAK,MAAM,cAAc,aACvB,MAAM,8BAA8B,KAAK,aAAa,GAAG,YAAY,KAAK,MAAM;CAEpF;CAEA,MAAc,SAAS,WAAgD;EACrE,IAAI;GAEF,QAAO,MADa,KAAK,cAAc,SAAS,EACpC,EAAE;EAChB,SAAS,KAAK;GACZ,KAAK,OAAO,MACV,6BAA6B,UAAU,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GACpG;GACA;EACF;CACF;CAEA,AAAQ,gBAAgB,OAAyB;EAC/C,IAAI,EAAE,iBAAiB,QAAQ,OAAO;EACtC,MAAM,OAAQ,MAA4B,QAAQ;EAClD,MAAM,UAAU,MAAM,QAAQ,YAAY;EAG1C,OACE,SAAS,sBACR,QAAQ,SAAS,iCAAiC,KACjD,QAAQ,SAAS,WAAW,KAC5B,QAAQ,SAAS,gBAAgB;CAEvC;CAEA,MAAc,oBAAoB,WAAmB,YAAY,KAAwB;EACvF,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI,QAAQ;EAEZ,OAAO,KAAK,IAAI,IAAI,YAAY,WAAW;GACzC,IAAI;IAEF,IAAI,CAAC,MADe,KAAK,cAAc,SAAS,GACpC;GACd,SAAS,OAAO;IACd,IAAI,KAAK,gBAAgB,KAAK,GAAG;IACjC,MAAM;GACR;GAEA,MAAM,KAAK,MAAM,KAAK;GACtB,QAAQ,KAAK,IAAI,QAAQ,GAAG,GAAM;EACpC;EAEA,MAAM,IAAI,MACR,0CAA0C,UAAU,+BACtD;CACF;CAEA,AAAQ,MAAM,IAA2B;EACvC,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;CACzD;;;;;;;;;;;;;;;;CAyBA,MAAc,cAAc,YAAoB,MAAe,MAA8B;EAC3F,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAE/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EACnD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EACnD,MAAM,4BAAY,IAAI,IAAoB;EAC1C,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,KAAK,UAAU,IAAI,EAAE,KAAK,CAAC;EAEnC,MAAM,4BAAY,IAAI,IAAoB;EAC1C,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,KAAK,UAAU,IAAI,EAAE,KAAK,CAAC;EAGnC,MAAM,WAAqB,CAAC;EAC5B,KAAK,MAAM,CAAC,KAAK,QAAQ,WACvB,IAAI,CAAC,UAAU,IAAI,GAAG,GAAG,SAAS,KAAK,GAAG;EAE5C,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAIA,oBAAkB,EAOpB,MAAM,SAAS,KAAK,OAAO;GACzB,YAAY;GACZ,cAAc;GACd,KAAK,EAAE;EACT,EAAE,EACJ,CAAC,CACH;EAGF,MAAM,WAAqB,CAAC;EAC5B,KAAK,MAAM,CAAC,KAAK,QAAQ,WAAW;GAClC,MAAM,SAAS,UAAU,IAAI,GAAG;GAChC,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,UAAU,GAAG,GAAG;GACpD,SAAS,KAAK,GAAG;EACnB;EACA,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,0BAA0B,EAC5B,MAAM,SAAS,KAAK,OAAO;GACzB,YAAY;GACZ,cAAc;GACd,KAAK,EAAE;GACP,GAAI,EAAE,UAAU,UAAa,EAAE,OAAO,EAAE,MAAM;GAC9C,GAAI,EAAE,sBAAsB,UAAa,EACvC,mBAAmB,EAAE,kBACvB;EACF,EAAE,EACJ,CAAC,CACH;CAEJ;;;;;;;;CASA,MAAc,2BACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,aAAa,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,EAAC,CAAE,QACjD,MAAmB,OAAO,MAAM,QACnC;EACA,MAAM,aAAa,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,EAAC,CAAE,QACjD,MAAmB,OAAO,MAAM,QACnC;EACA,MAAM,UAAU,IAAI,IAAI,SAAS;EACjC,MAAM,UAAU,IAAI,IAAI,SAAS;EACjC,MAAM,WAAW,UAAU,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACxD,MAAM,WAAW,UAAU,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACxD,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,2BAA2B;GAC7B,sBAAsB;GACtB,mBAAmB;EACrB,CAAC,CACH;EAEF,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,2BAA2B;GAC7B,sBAAsB;GACtB,mBAAmB;EACrB,CAAC,CACH;CAEJ;;;;;;;;;CAUA,MAAc,yBACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,EAAC,CAAE,QAChD,MAAmB,OAAO,MAAM,QACnC;EACA,MAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,EAAC,CAAE,QAChD,MAAmB,OAAO,MAAM,QACnC;EACA,MAAM,UAAU,IAAI,IAAI,QAAQ;EAChC,MAAM,UAAU,IAAI,IAAI,QAAQ;EAChC,MAAM,WAAW,SAAS,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACvD,MAAM,WAAW,SAAS,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACvD,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,sCAAsC;GACxC,sBAAsB;GACtB,iBAAiB;EACnB,CAAC,CACH;EAEF,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,sCAAsC;GACxC,sBAAsB;GACtB,iBAAiB;EACnB,CAAC,CACH;EAWF,IAAI,SAAS,SAAS,KAAK,SAAS,SAAS,GAC3C,MAAM,KAAK,kCAAkC,YAAY,IAAI,IAAI,QAAQ,CAAC;CAE9E;CAEA,OAAwB,4BAA4B;CACpD,OAAwB,kCAAkC;CAE1D,MAAc,kCACZ,YACA,UACe;EACf,MAAM,aAAa,KAAK,IAAI,IAAI,YAAY;EAC5C,IAAI,+BAA4B,IAAI,IAAI;EACxC,OAAO,KAAK,IAAI,IAAI,YAAY;GAC9B,IAAI;GACJ,IAAI;IACF,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,KAC5B,IAAI,iCAAiC,EAAE,uBAAuB,CAAC,UAAU,EAAE,CAAC,CAC9E;GACF,SAAS,KAAK;IAMZ,KAAK,OAAO,MACV,0EACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAEnD;IACA,MAAM,IAAI,SAAS,MAAM,WAAW,GAAG,YAAY,+BAA+B,CAAC;IACnF;GACF;GACA,eAAe,IAAI,IAAI,KAAK,oBAAoB,EAAE,EAAE,mBAAmB,CAAC,CAAC;GACzE,IAAI,aAAa,SAAS,SAAS,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,OAAO,MAAM,aAAa,IAAI,CAAC,CAAC,GACvF;GAEF,MAAM,IAAI,SAAS,MAAM,WAAW,GAAG,YAAY,+BAA+B,CAAC;EACrF;EAQA,MAAM,iBAAiB,CAAC,GAAG,QAAQ,CAAC,CAAC,KAAK;EAC1C,MAAM,iBAAiB,CAAC,GAAG,YAAY,CAAC,CAAC,KAAK;EAC9C,KAAK,OAAO,KACV,4DAA4D,YAAY,0BAA0B,aAAa,WAAW,aAAa,KAAK,UAAU,cAAc,EAAE,YAAY,KAAK,UAAU,cAAc,GACjN;CACF;CAEA,MAAc,2BACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,oCAAoB,IAAI,IAAkC;EAChE,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,aAAa,kBAAkB,IAAI,EAAE,aAAa,EAAE,OAAO;EAEnE,MAAM,oCAAoB,IAAI,IAAkC;EAChE,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,aAAa,kBAAkB,IAAI,EAAE,aAAa,EAAE,OAAO;EAMnE,KAAK,MAAM,CAAC,aAAa,YAAY,mBACnC,IAAI,CAAC,kBAAkB,IAAI,WAAW,GACpC,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,gCAAgC;GAClC,sBAAsB;GACtB,GAAI,WAAW,QAAQ,SAAS,IAAI,EAAE,SAAS,QAAQ,IAAI,CAAC;EAC9D,CAAC,CACH;EAGJ,KAAK,MAAM,CAAC,aAAa,YAAY,mBAAmB;GACtD,MAAM,SAAS,kBAAkB,IAAI,WAAW;GAChD,IAAI,KAAK,UAAU,UAAU,IAAI,MAAM,KAAK,UAAU,WAAW,IAAI,GAAG;GAKxE,IAAI,UAAU,OAAO,SAAS,GAAG;IAC/B,MAAM,UAAU,UAAU,OAAO,QAAQ,MAAM,CAAC,QAAQ,SAAS,CAAC,CAAC,IAAI,CAAC;IACxE,IAAI,QAAQ,SAAS,GACnB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,gCAAgC;KAClC,sBAAsB;KACtB,SAAS;IACX,CAAC,CACH;GAEJ;GACA,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,+BAA+B;IACjC,sBAAsB;IACtB,aAAa;IACb,GAAI,WAAW,QAAQ,SAAS,IAAI,EAAE,SAAS,QAAQ,IAAI,CAAC;GAC9D,CAAC,CACH;EACF;CACF;CAEA,MAAc,wBACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EASnD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAGnD,MAAM,YAAY,IAAI,IACpB,YAAY,KAAK,MAAM,EAAE,iBAAiB,CAAC,CAAC,QAAQ,MAAmB,CAAC,CAAC,CAAC,CAC5E;EAEA,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,qBAAqB,CAAC,UAAU,IAAI,EAAE,iBAAiB,GAC3D,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,2BAA2B;GAC7B,sBAAsB;GACtB,mBAAmB,EAAE;EACvB,CAAC,CACH;EAKJ,MAAM,6BAAa,IAAI,IAAqB;EAC5C,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,mBAAmB,WAAW,IAAI,EAAE,mBAAmB,CAAC;EAEhE,KAAK,MAAM,KAAK,aAAa;GAC3B,IAAI,CAAC,EAAE,mBAAmB;GAC1B,MAAM,WAAW,WAAW,IAAI,EAAE,iBAAiB;GACnD,IAAI,KAAK,UAAU,QAAQ,MAAM,KAAK,UAAU,CAAC,GAAG;GACpD,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,wBAAwB;IAC1B,sBAAsB;IACtB,mBAAmB,EAAE;IACrB,GAAI,EAAE,wBAAwB,UAAa,EACzC,qBAAqB,EAAE,oBACzB;IACA,GAAI,EAAE,YAAY,UAAa,EAAE,SAAS,EAAE,QAAQ;IACpD,GAAI,EAAE,0BAA0B,UAAa,EAC3C,uBAAuB,EAAE,sBAC3B;IACA,GAAI,EAAE,yBAAyB,UAAa,EAC1C,sBAAsB,EAAE,qBAC1B;IACA,GAAI,EAAE,qBAAqB,UAAa,EAAE,kBAAkB,EAAE,iBAAiB;IAC/E,GAAI,EAAE,kBAAkB,UAAa,EAAE,eAAe,EAAE,cAAc;GACxE,CAAC,CACH;EACF;CACF;CAEA,MAAc,wBACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,UAAU,IAAI,IAAI,YAAY,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC,QAAQ,MAAmB,CAAC,CAAC,CAAC,CAAC;EAC5F,MAAM,UAAU,IAAI,IAAI,YAAY,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC,QAAQ,MAAmB,CAAC,CAAC,CAAC,CAAC;EAC5F,MAAM,WAAW,YAAY,QAAQ,MAAM,EAAE,cAAc,CAAC,QAAQ,IAAI,EAAE,UAAU,CAAC;EACrF,MAAM,WAAW,YAAY,QAAQ,MAAM,EAAE,cAAc,CAAC,QAAQ,IAAI,EAAE,UAAU,CAAC;EACrF,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,4BAA4B;GAC9B,sBAAsB;GACtB,gBAAgB,SAAS,KAAK,OAAO;IACnC,YAAY,EAAE;IACd,GAAI,EAAE,SAAS,UAAa,EAAE,MAAM,EAAE,KAAK;GAC7C,EAAE;EACJ,CAAC,CACH;EAEF,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,4BAA4B;GAC9B,sBAAsB;GACtB,gBAAgB,SAAS,KAAK,OAAO;IACnC,YAAY,EAAE;IACd,GAAI,EAAE,SAAS,UAAa,EAAE,MAAM,EAAE,KAAK;GAC7C,EAAE;EACJ,CAAC,CACH;CAEJ;CAEA,MAAc,oCACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAO/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,8BAAc,IAAI,IAAkC;EAC1D,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,UAAU,YAAY,IAAI,EAAE,UAAU,EAAE,iBAAiB;EAEjE,MAAM,8BAAc,IAAI,IAAkC;EAC1D,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,UAAU,YAAY,IAAI,EAAE,UAAU,EAAE,iBAAiB;EAEjE,KAAK,MAAM,SAAS,YAAY,KAAK,GACnC,IAAI,CAAC,YAAY,IAAI,KAAK,GACxB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,uCAAuC;GACzC,sBAAsB;GACtB,UAAU;EACZ,CAAC,CACH;EAGJ,KAAK,MAAM,CAAC,OAAO,UAAU,aAAa;GACxC,MAAM,SAAS,YAAY,IAAI,KAAK;GACpC,IAAI,KAAK,UAAU,UAAU,IAAI,MAAM,KAAK,UAAU,SAAS,IAAI,GAAG;GACtE,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,oCAAoC;IACtC,sBAAsB;IACtB,UAAU;IACV,mBAAmB,SAAS,CAAC;GAC/B,CAAC,CACH;EACF;CACF;AACF;;;;;;;;;;;;AAeA,SAAS,uBACP,gBAGoD;CACpD,IAAI,CAAC,kBAAkB,eAAe,WAAW,GAAG,OAAO,CAAC;CAC5D,MAAM,gCAAgB,IAAI,IAAyB;CACnD,KAAK,MAAM,KAAK,gBAAgB;EAC9B,MAAM,IAAI,EAAE;EACZ,IAAI,CAAC,GAAG;EACR,IAAI,MAAM,cAAc,IAAI,CAAC;EAC7B,IAAI,CAAC,KAAK;GACR,sBAAM,IAAI,IAAI;GACd,cAAc,IAAI,GAAG,GAAG;EAC1B;EACA,IAAI,EAAE,QAAQ,IAAI,IAAI,EAAE,MAAM;CAChC;CACA,MAAM,SAA6D,CAAC;CAEpE,KAAK,MAAM,eAAe,MAAM,KAAK,cAAc,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;EACjE,MAAM,UAAU,MAAM,KAAK,cAAc,IAAI,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK;EACtE,OAAO,KACL,QAAQ,SAAS,IACb;GAAE,aAAa;GAAa,SAAS;EAAQ,IAC7C,EAAE,aAAa,YAAY,CACjC;CACF;CACA,OAAO;AACT;;;;;;;;;AAUA,SAAS,uBACP,OASgC;CAChC,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO,CAAC;CAC1C,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,KAAK,OAAO;EACrB,IAAI,CAAC,EAAE,mBAAmB;EAC1B,MAAM,QAAiC,EAAE,mBAAmB,EAAE,kBAAkB;EAChF,IAAI,EAAE,wBAAwB,QAAW,MAAM,yBAAyB,EAAE;EAC1E,IAAI,EAAE,YAAY,QAAW,MAAM,aAAa,EAAE;EAClD,IAAI,EAAE,0BAA0B,QAC9B,MAAM,2BAA2B,EAAE;EAErC,IAAI,EAAE,yBAAyB,QAC7B,MAAM,0BAA0B,EAAE;EAEpC,IAAI,EAAE,qBAAqB,QAAW,MAAM,sBAAsB,EAAE;EACpE,IAAI,EAAE,kBAAkB,QAAW,MAAM,mBAAmB,EAAE;EAC9D,OAAO,KAAK,KAAK;CACnB;CACA,OAAO,MAAM,GAAG,MACd,OAAO,EAAE,oBAAoB,CAAC,CAAC,cAAc,OAAO,EAAE,oBAAoB,CAAC,CAC7E;CACA,OAAO;AACT;;;;;;;AAQA,SAAS,uBACP,gBACgC;CAChC,IAAI,CAAC,kBAAkB,eAAe,WAAW,GAAG,OAAO,CAAC;CAC5D,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,KAAK,gBAAgB;EAC9B,IAAI,CAAC,EAAE,YAAY;EACnB,MAAM,QAAiC,EAAE,YAAY,EAAE,WAAW;EAClE,IAAI,EAAE,SAAS,QAAW,MAAM,UAAU,EAAE;EAC5C,OAAO,KAAK,KAAK;CACnB;CACA,OAAO,MAAM,GAAG,MAAM,OAAO,EAAE,aAAa,CAAC,CAAC,cAAc,OAAO,EAAE,aAAa,CAAC,CAAC;CACpF,OAAO;AACT;;;;;;;AAQA,SAAS,sBACP,gBACgC;CAChC,IAAI,CAAC,kBAAkB,eAAe,WAAW,GAAG,OAAO,CAAC;CAC5D,MAAM,0BAAU,IAAI,IAAyB;CAC7C,KAAK,MAAM,KAAK,gBAAgB;EAC9B,IAAI,CAAC,EAAE,UAAU;EACjB,IAAI,MAAM,QAAQ,IAAI,EAAE,QAAQ;EAChC,IAAI,CAAC,KAAK;GACR,sBAAM,IAAI,IAAI;GACd,QAAQ,IAAI,EAAE,UAAU,GAAG;EAC7B;EACA,IAAI,EAAE,kBAAkB,IAAI,IAAI,EAAE,gBAAgB;CACpD;CACA,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,SAAS,MAAM,KAAK,QAAQ,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;EACrD,MAAM,QAAQ,MAAM,KAAK,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK;EACxD,OAAO,KAAK;GAAE,UAAU;GAAO,mBAAmB;EAAM,CAAC;CAC3D;CACA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"asg-provider-Cvnv7x_Q.js","names":["DeleteTagsCommand"],"sources":["../src/provisioning/providers/asg-provider.ts"],"sourcesContent":["import {\n AutoScalingClient,\n CreateAutoScalingGroupCommand,\n UpdateAutoScalingGroupCommand,\n DeleteAutoScalingGroupCommand,\n DescribeAutoScalingGroupsCommand,\n type DescribeAutoScalingGroupsCommandOutput,\n DescribeLifecycleHooksCommand,\n DescribeTrafficSourcesCommand,\n DescribeNotificationConfigurationsCommand,\n EnableMetricsCollectionCommand,\n DisableMetricsCollectionCommand,\n PutLifecycleHookCommand,\n DeleteLifecycleHookCommand,\n AttachTrafficSourcesCommand,\n DetachTrafficSourcesCommand,\n PutNotificationConfigurationCommand,\n DeleteNotificationConfigurationCommand,\n CreateOrUpdateTagsCommand,\n DeleteTagsCommand,\n AttachLoadBalancersCommand,\n DetachLoadBalancersCommand,\n AttachLoadBalancerTargetGroupsCommand,\n DetachLoadBalancerTargetGroupsCommand,\n type Tag as ASGTag,\n type LaunchTemplateSpecification,\n} from '@aws-sdk/client-auto-scaling';\nimport { EC2Client } from '@aws-sdk/client-ec2';\nimport { getLogger } from '../../utils/logger.js';\nimport { ProvisioningError, ResourceUpdateNotSupportedError } from '../../utils/error-handler.js';\nimport { assertRegionMatch, type DeleteContext } from '../region-check.js';\nimport { disableInstanceApiTermination } from '../ec2-termination-protection.js';\nimport { generateResourceName } from '../resource-name.js';\nimport { normalizeAwsTagsToCfn } from '../import-helpers.js';\nimport type {\n ResourceProvider,\n ResourceCreateResult,\n ResourceUpdateResult,\n} from '../../types/resource.js';\n\n/**\n * AWS Auto Scaling Provider\n *\n * Implements resource provisioning for `AWS::AutoScaling::AutoScalingGroup`.\n *\n * WHY a dedicated SDK provider (instead of CC API fallback):\n * 1. Owns the `--remove-protection` flip-off: ASG protection has three\n * levels (`none` / `prevent-force-deletion` / `prevent-all-deletion`)\n * and the destroy path needs to (a) clear it via `UpdateAutoScalingGroup\n * ({DeletionProtection: 'none'})` before the actual delete and (b) set\n * `ForceDelete: true` on `DeleteAutoScalingGroup` so AWS terminates any\n * running instances as part of the delete (matches the user's \"I know\n * what I'm doing\" intent).\n * 2. Faster than CC API for the common case — direct Create/Update calls\n * with no eventual-consistency polling beyond what `DescribeAutoScaling\n * Groups` already provides.\n *\n * Update has narrower coverage than create: AWS does not support modifying\n * `AutoScalingGroupName` (immutable) — that diff still surfaces\n * `ResourceUpdateNotSupportedError` so the caller can `cdkd deploy\n * --replace`. The mutable fields handled in-place via\n * `UpdateAutoScalingGroup` include MinSize / MaxSize / DesiredCapacity /\n * VPCZoneIdentifier / HealthCheckType / HealthCheckGracePeriod /\n * DefaultCooldown / Cooldown / NewInstancesProtectedFromScaleIn /\n * MaxInstanceLifetime / TerminationPolicies / CapacityRebalance /\n * ServiceLinkedRoleARN / Context / DesiredCapacityType /\n * DefaultInstanceWarmup / AvailabilityZones / AvailabilityZoneDistribution\n * / AvailabilityZoneImpairmentPolicy / SkipZonalShiftValidation /\n * CapacityReservationSpecification / InstanceMaintenancePolicy /\n * DeletionProtection / MixedInstancesPolicy / LaunchTemplate.\n *\n * Sub-shape diffs are applied via dedicated AWS APIs before the main\n * `UpdateAutoScalingGroup` call:\n * - `Tags` → `CreateOrUpdateTags` / `DeleteTags` (#475)\n * - `LoadBalancerNames` → `AttachLoadBalancers` /\n * `DetachLoadBalancers` (#476)\n * - `TargetGroupARNs` → `AttachLoadBalancerTargetGroups` /\n * `DetachLoadBalancerTargetGroups` (#476)\n * - `MetricsCollection` → `EnableMetricsCollection` /\n * `DisableMetricsCollection`\n * - `LifecycleHookSpecificationList` → per-entry `PutLifecycleHook` /\n * `DeleteLifecycleHook`\n * - `TrafficSources` → `AttachTrafficSources` /\n * `DetachTrafficSources`\n * - `NotificationConfigurations` → per-topic\n * `PutNotificationConfiguration` /\n * `DeleteNotificationConfiguration`\n *\n * Each helper is a no-op when the before/after JSON is identical.\n */\nexport class ASGProvider implements ResourceProvider {\n private asgClient?: AutoScalingClient;\n private ec2Client?: EC2Client;\n private readonly providerRegion = process.env['AWS_REGION'];\n private logger = getLogger().child('ASGProvider');\n\n handledProperties = new Map<string, ReadonlySet<string>>([\n [\n 'AWS::AutoScaling::AutoScalingGroup',\n new Set([\n 'AutoScalingGroupName',\n 'LaunchTemplate',\n 'MinSize',\n 'MaxSize',\n 'DesiredCapacity',\n 'VPCZoneIdentifier',\n 'AvailabilityZones',\n 'HealthCheckType',\n 'HealthCheckGracePeriod',\n 'Cooldown',\n 'DefaultCooldown',\n 'Tags',\n 'TerminationPolicies',\n 'NewInstancesProtectedFromScaleIn',\n 'CapacityRebalance',\n 'ServiceLinkedRoleARN',\n 'MaxInstanceLifetime',\n 'LoadBalancerNames',\n 'TargetGroupARNs',\n 'MetricsCollection',\n 'LifecycleHookSpecificationList',\n 'MixedInstancesPolicy',\n 'Context',\n 'DesiredCapacityType',\n 'DefaultInstanceWarmup',\n 'TrafficSources',\n 'NotificationConfigurations',\n 'AvailabilityZoneDistribution',\n 'AvailabilityZoneImpairmentPolicy',\n 'SkipZonalShiftValidation',\n 'CapacityReservationSpecification',\n 'InstanceMaintenancePolicy',\n 'DeletionProtection',\n ]),\n ],\n ]);\n\n unhandledByDesign = new Map<string, ReadonlyMap<string, string>>([\n [\n 'AWS::AutoScaling::AutoScalingGroup',\n new Map<string, string>([\n [\n 'LaunchConfigurationName',\n 'AWS Launch Configurations end-of-life 2024-10; use LaunchTemplate instead',\n ],\n [\n 'NotificationConfiguration',\n 'Legacy singular form; use NotificationConfigurations (plural) which cdkd already wires',\n ],\n ]),\n ],\n ]);\n\n private getClient(): AutoScalingClient {\n if (!this.asgClient) {\n this.asgClient = new AutoScalingClient(\n this.providerRegion ? { region: this.providerRegion } : {}\n );\n }\n return this.asgClient;\n }\n\n private getEc2Client(): EC2Client {\n if (!this.ec2Client) {\n this.ec2Client = new EC2Client(this.providerRegion ? { region: this.providerRegion } : {});\n }\n return this.ec2Client;\n }\n\n // ─── Dispatch ─────────────────────────────────────────────────────\n\n async create(\n logicalId: string,\n resourceType: string,\n properties: Record<string, unknown>\n ): Promise<ResourceCreateResult> {\n if (resourceType !== 'AWS::AutoScaling::AutoScalingGroup') {\n throw new ProvisioningError(\n `Unsupported resource type: ${resourceType}`,\n resourceType,\n logicalId\n );\n }\n\n const groupName =\n (properties['AutoScalingGroupName'] as string | undefined) ||\n generateResourceName(logicalId, { maxLength: 255 });\n\n this.logger.debug(`Creating AutoScalingGroup ${logicalId}: ${groupName}`);\n\n try {\n const launchTemplate = this.buildLaunchTemplate(properties);\n const tags = this.buildTags(groupName, properties);\n const vpcZoneIdentifier = this.joinVpcZoneIdentifier(properties['VPCZoneIdentifier']);\n\n const minSize = properties['MinSize'] != null ? Number(properties['MinSize']) : 0;\n const maxSize = properties['MaxSize'] != null ? Number(properties['MaxSize']) : minSize;\n\n await this.getClient().send(\n new CreateAutoScalingGroupCommand({\n AutoScalingGroupName: groupName,\n MinSize: minSize,\n MaxSize: maxSize,\n ...(properties['DesiredCapacity'] != null && {\n DesiredCapacity: Number(properties['DesiredCapacity']),\n }),\n ...(launchTemplate && { LaunchTemplate: launchTemplate }),\n ...(properties['MixedInstancesPolicy'] !== undefined && {\n MixedInstancesPolicy: properties['MixedInstancesPolicy'] as never,\n }),\n ...(vpcZoneIdentifier !== undefined && { VPCZoneIdentifier: vpcZoneIdentifier }),\n ...(properties['AvailabilityZones'] !== undefined && {\n AvailabilityZones: properties['AvailabilityZones'] as string[],\n }),\n ...(properties['HealthCheckType'] !== undefined && {\n HealthCheckType: properties['HealthCheckType'] as string,\n }),\n ...(properties['HealthCheckGracePeriod'] != null && {\n HealthCheckGracePeriod: Number(properties['HealthCheckGracePeriod']),\n }),\n ...(properties['Cooldown'] != null && {\n DefaultCooldown: Number(properties['Cooldown']),\n }),\n ...(properties['DefaultCooldown'] != null && {\n DefaultCooldown: Number(properties['DefaultCooldown']),\n }),\n ...(properties['TerminationPolicies'] !== undefined && {\n TerminationPolicies: properties['TerminationPolicies'] as string[],\n }),\n ...(properties['NewInstancesProtectedFromScaleIn'] !== undefined && {\n NewInstancesProtectedFromScaleIn: properties[\n 'NewInstancesProtectedFromScaleIn'\n ] as boolean,\n }),\n ...(properties['CapacityRebalance'] !== undefined && {\n CapacityRebalance: properties['CapacityRebalance'] as boolean,\n }),\n ...(properties['ServiceLinkedRoleARN'] !== undefined && {\n ServiceLinkedRoleARN: properties['ServiceLinkedRoleARN'] as string,\n }),\n ...(properties['MaxInstanceLifetime'] != null && {\n MaxInstanceLifetime: Number(properties['MaxInstanceLifetime']),\n }),\n ...(properties['LoadBalancerNames'] !== undefined && {\n LoadBalancerNames: properties['LoadBalancerNames'] as string[],\n }),\n ...(properties['TargetGroupARNs'] !== undefined && {\n TargetGroupARNs: properties['TargetGroupARNs'] as string[],\n }),\n ...(properties['Context'] !== undefined && {\n Context: properties['Context'] as string,\n }),\n ...(properties['DesiredCapacityType'] !== undefined && {\n DesiredCapacityType: properties['DesiredCapacityType'] as string,\n }),\n ...(properties['DefaultInstanceWarmup'] != null && {\n DefaultInstanceWarmup: Number(properties['DefaultInstanceWarmup']),\n }),\n ...(properties['LifecycleHookSpecificationList'] !== undefined && {\n LifecycleHookSpecificationList: properties['LifecycleHookSpecificationList'] as never,\n }),\n ...(properties['TrafficSources'] !== undefined && {\n TrafficSources: properties['TrafficSources'] as never,\n }),\n ...(properties['AvailabilityZoneDistribution'] !== undefined && {\n AvailabilityZoneDistribution: properties['AvailabilityZoneDistribution'] as never,\n }),\n ...(properties['AvailabilityZoneImpairmentPolicy'] !== undefined && {\n AvailabilityZoneImpairmentPolicy: properties[\n 'AvailabilityZoneImpairmentPolicy'\n ] as never,\n }),\n ...(properties['SkipZonalShiftValidation'] !== undefined && {\n SkipZonalShiftValidation: properties['SkipZonalShiftValidation'] as boolean,\n }),\n ...(properties['CapacityReservationSpecification'] !== undefined && {\n CapacityReservationSpecification: properties[\n 'CapacityReservationSpecification'\n ] as never,\n }),\n ...(properties['InstanceMaintenancePolicy'] !== undefined && {\n InstanceMaintenancePolicy: properties['InstanceMaintenancePolicy'] as never,\n }),\n ...(properties['DeletionProtection'] !== undefined && {\n DeletionProtection: properties['DeletionProtection'] as never,\n }),\n ...(tags.length > 0 && { Tags: tags }),\n })\n );\n\n this.logger.debug(`Successfully created AutoScalingGroup ${logicalId}: ${groupName}`);\n\n const arn = await this.fetchArn(groupName);\n const attributes: Record<string, unknown> = {};\n if (arn) attributes['Arn'] = arn;\n if (launchTemplate?.LaunchTemplateId) {\n attributes['LaunchTemplateID'] = launchTemplate.LaunchTemplateId;\n }\n return { physicalId: groupName, attributes };\n } catch (error) {\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to create AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n groupName,\n cause\n );\n }\n }\n\n async update(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n properties: Record<string, unknown>,\n previousProperties: Record<string, unknown>\n ): Promise<ResourceUpdateResult> {\n if (resourceType !== 'AWS::AutoScaling::AutoScalingGroup') {\n throw new ProvisioningError(\n `Unsupported resource type: ${resourceType}`,\n resourceType,\n logicalId,\n physicalId\n );\n }\n this.logger.debug(`Updating AutoScalingGroup ${logicalId}: ${physicalId}`);\n\n // Reject diffs on fields AWS does not support modifying via\n // UpdateAutoScalingGroup. The replacement-detection layer typically\n // catches AutoScalingGroupName changes earlier; this is defense-in-\n // depth + the only place to surface the equivalent error for\n // sub-resource fields the caller may reasonably expect to round-trip.\n const stringEq = (a: unknown, b: unknown): boolean => JSON.stringify(a) === JSON.stringify(b);\n if (!stringEq(properties['AutoScalingGroupName'], previousProperties['AutoScalingGroupName'])) {\n throw new ResourceUpdateNotSupportedError(\n resourceType,\n logicalId,\n 'AutoScalingGroupName is immutable on AWS — UpdateAutoScalingGroup does not accept a new name; the name is fixed at creation. Use cdkd deploy --replace to replace the group.'\n );\n }\n try {\n // Sub-shape diffs are applied via separate per-shape SDK calls\n // BEFORE the main UpdateAutoScalingGroup. AWS does not expose these\n // fields on UpdateAutoScalingGroup, so each one rides its own\n // dedicated API. Each per-shape helper is a no-op when the\n // before/after JSON is identical.\n await this.applyTagsDiff(physicalId, properties['Tags'], previousProperties['Tags']);\n await this.applyLoadBalancerNamesDiff(\n physicalId,\n properties['LoadBalancerNames'],\n previousProperties['LoadBalancerNames']\n );\n await this.applyTargetGroupArnsDiff(\n physicalId,\n properties['TargetGroupARNs'],\n previousProperties['TargetGroupARNs']\n );\n await this.applyMetricsCollectionDiff(\n physicalId,\n properties['MetricsCollection'],\n previousProperties['MetricsCollection']\n );\n await this.applyLifecycleHooksDiff(\n physicalId,\n properties['LifecycleHookSpecificationList'],\n previousProperties['LifecycleHookSpecificationList']\n );\n await this.applyTrafficSourcesDiff(\n physicalId,\n properties['TrafficSources'],\n previousProperties['TrafficSources']\n );\n await this.applyNotificationConfigurationsDiff(\n physicalId,\n properties['NotificationConfigurations'],\n previousProperties['NotificationConfigurations']\n );\n\n const launchTemplate = this.buildLaunchTemplate(properties);\n const vpcZoneIdentifier = this.joinVpcZoneIdentifier(properties['VPCZoneIdentifier']);\n\n await this.getClient().send(\n new UpdateAutoScalingGroupCommand({\n AutoScalingGroupName: physicalId,\n ...(properties['MinSize'] != null && { MinSize: Number(properties['MinSize']) }),\n ...(properties['MaxSize'] != null && { MaxSize: Number(properties['MaxSize']) }),\n ...(properties['DesiredCapacity'] != null && {\n DesiredCapacity: Number(properties['DesiredCapacity']),\n }),\n ...(launchTemplate && { LaunchTemplate: launchTemplate }),\n ...(properties['MixedInstancesPolicy'] !== undefined && {\n MixedInstancesPolicy: properties['MixedInstancesPolicy'] as never,\n }),\n ...(vpcZoneIdentifier !== undefined && { VPCZoneIdentifier: vpcZoneIdentifier }),\n ...(properties['AvailabilityZones'] !== undefined && {\n AvailabilityZones: properties['AvailabilityZones'] as string[],\n }),\n ...(properties['HealthCheckType'] !== undefined && {\n HealthCheckType: properties['HealthCheckType'] as string,\n }),\n ...(properties['HealthCheckGracePeriod'] != null && {\n HealthCheckGracePeriod: Number(properties['HealthCheckGracePeriod']),\n }),\n ...(properties['Cooldown'] != null && {\n DefaultCooldown: Number(properties['Cooldown']),\n }),\n ...(properties['DefaultCooldown'] != null && {\n DefaultCooldown: Number(properties['DefaultCooldown']),\n }),\n ...(properties['TerminationPolicies'] !== undefined && {\n TerminationPolicies: properties['TerminationPolicies'] as string[],\n }),\n ...(properties['NewInstancesProtectedFromScaleIn'] !== undefined && {\n NewInstancesProtectedFromScaleIn: properties[\n 'NewInstancesProtectedFromScaleIn'\n ] as boolean,\n }),\n ...(properties['CapacityRebalance'] !== undefined && {\n CapacityRebalance: properties['CapacityRebalance'] as boolean,\n }),\n ...(properties['ServiceLinkedRoleARN'] !== undefined && {\n ServiceLinkedRoleARN: properties['ServiceLinkedRoleARN'] as string,\n }),\n ...(properties['MaxInstanceLifetime'] != null && {\n MaxInstanceLifetime: Number(properties['MaxInstanceLifetime']),\n }),\n ...(properties['Context'] !== undefined && {\n Context: properties['Context'] as string,\n }),\n ...(properties['DesiredCapacityType'] !== undefined && {\n DesiredCapacityType: properties['DesiredCapacityType'] as string,\n }),\n ...(properties['DefaultInstanceWarmup'] != null && {\n DefaultInstanceWarmup: Number(properties['DefaultInstanceWarmup']),\n }),\n ...(properties['AvailabilityZoneDistribution'] !== undefined && {\n AvailabilityZoneDistribution: properties['AvailabilityZoneDistribution'] as never,\n }),\n ...(properties['AvailabilityZoneImpairmentPolicy'] !== undefined && {\n AvailabilityZoneImpairmentPolicy: properties[\n 'AvailabilityZoneImpairmentPolicy'\n ] as never,\n }),\n ...(properties['SkipZonalShiftValidation'] !== undefined && {\n SkipZonalShiftValidation: properties['SkipZonalShiftValidation'] as boolean,\n }),\n ...(properties['CapacityReservationSpecification'] !== undefined && {\n CapacityReservationSpecification: properties[\n 'CapacityReservationSpecification'\n ] as never,\n }),\n ...(properties['InstanceMaintenancePolicy'] !== undefined && {\n InstanceMaintenancePolicy: properties['InstanceMaintenancePolicy'] as never,\n }),\n ...(properties['DeletionProtection'] !== undefined && {\n DeletionProtection: properties['DeletionProtection'] as never,\n }),\n })\n );\n\n this.logger.debug(`Successfully updated AutoScalingGroup ${logicalId}`);\n\n const arn = await this.fetchArn(physicalId);\n const attributes: Record<string, unknown> = {};\n if (arn) attributes['Arn'] = arn;\n if (launchTemplate?.LaunchTemplateId) {\n attributes['LaunchTemplateID'] = launchTemplate.LaunchTemplateId;\n }\n return { physicalId, wasReplaced: false, attributes };\n } catch (error) {\n if (error instanceof ResourceUpdateNotSupportedError) throw error;\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to update AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n async delete(\n logicalId: string,\n physicalId: string,\n resourceType: string,\n _properties?: Record<string, unknown>,\n context?: DeleteContext\n ): Promise<void> {\n this.logger.debug(`Deleting AutoScalingGroup ${logicalId}: ${physicalId}`);\n\n // `--remove-protection`: clear DeletionProtection in-place before the\n // actual delete, then set ForceDelete=true so AWS terminates running\n // instances as part of the delete (matches the \"I know what I'm doing\"\n // intent of the flag). Without `removeProtection`, ForceDelete stays\n // false and AWS rejects the delete on a group with running instances\n // or DeletionProtection set, surfacing as ProvisioningError. The\n // flip-off is idempotent — AWS accepts UpdateAutoScalingGroup\n // (DeletionProtection: 'none') even when protection is already\n // disabled, so we always issue it under the flag.\n if (context?.removeProtection === true) {\n try {\n await this.getClient().send(\n new UpdateAutoScalingGroupCommand({\n AutoScalingGroupName: physicalId,\n DeletionProtection: 'none' as never,\n })\n );\n this.logger.debug(\n `Disabled DeletionProtection on AutoScalingGroup ${logicalId} before delete`\n );\n } catch (flipError) {\n // Non-fatal: log and proceed. The actual delete below surfaces\n // any real error.\n this.logger.debug(\n `Could not disable DeletionProtection on ${physicalId}: ${flipError instanceof Error ? flipError.message : String(flipError)}`\n );\n }\n\n // ASG-level DeletionProtection + ForceDelete only governs the GROUP and\n // its scale-in protection. If the group's launch template sets\n // EC2-level termination protection (DisableApiTermination), the\n // ForceDelete below still cannot terminate those instances and they\n // ORPHAN after the group is gone (issue #796). Enumerate the group's\n // current instances and flip each one's DisableApiTermination off first,\n // mirroring the EC2Provider `--remove-protection` path.\n await this.removeInstanceTerminationProtection(physicalId, logicalId);\n }\n\n try {\n await this.getClient().send(\n new DeleteAutoScalingGroupCommand({\n AutoScalingGroupName: physicalId,\n ForceDelete: context?.removeProtection === true,\n })\n );\n\n this.logger.debug(`Successfully initiated deletion of AutoScalingGroup ${logicalId}`);\n\n // Wait for the group to be fully gone. ASG delete is asynchronous —\n // returning immediately would leave dependent EC2 / IAM / SG\n // resources blocked on the lingering group.\n await this.waitForGroupDeleted(physicalId);\n } catch (error) {\n if (this.isNotFoundError(error)) {\n const clientRegion = await this.getClient().config.region();\n assertRegionMatch(\n clientRegion,\n context?.expectedRegion,\n resourceType,\n logicalId,\n physicalId\n );\n this.logger.debug(`AutoScalingGroup ${physicalId} does not exist, skipping deletion`);\n return;\n }\n const cause = error instanceof Error ? error : undefined;\n throw new ProvisioningError(\n `Failed to delete AutoScalingGroup ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,\n resourceType,\n logicalId,\n physicalId,\n cause\n );\n }\n }\n\n async getAttribute(\n physicalId: string,\n _resourceType: string,\n attributeName: string\n ): Promise<unknown> {\n const group = await this.describeGroup(physicalId);\n if (!group) {\n throw new ProvisioningError(\n `AutoScalingGroup ${physicalId} not found while resolving attribute ${attributeName}`,\n 'AWS::AutoScaling::AutoScalingGroup',\n physicalId,\n physicalId\n );\n }\n switch (attributeName) {\n case 'Arn':\n case 'AutoScalingGroupARN':\n return group.AutoScalingGroupARN ?? '';\n case 'LaunchConfigurationName':\n return group.LaunchConfigurationName ?? '';\n case 'LaunchTemplateID':\n case 'LaunchTemplateId':\n return group.LaunchTemplate?.LaunchTemplateId ?? '';\n default:\n return '';\n }\n }\n\n /**\n * Read the AWS-current AutoScalingGroup configuration in CFn-property shape.\n *\n * Surfaces the user-controllable subset of `DescribeAutoScalingGroups`,\n * with always-emit placeholders on user-controllable top-level keys per\n * the cdkd PR #145 always-emit convention so that v3 `observedProperties`\n * baseline catches console-side ADDs to fields a clean deploy did not\n * template (e.g. a console-set `DeletionProtection: 'prevent-force-deletion'`\n * on a group originally created without it).\n *\n * Sub-shapes (LifecycleHookSpecificationList / TrafficSources /\n * NotificationConfigurations) are surfaced via three parallel Describe\n * calls fired alongside the primary `DescribeAutoScalingGroups`. Each is\n * best-effort: a per-call failure (e.g. permissions gap on\n * `autoscaling:DescribeLifecycleHooks`) is logged at debug and the\n * matching key falls back to its always-emit `[]` placeholder rather\n * than aborting the whole drift read.\n *\n * `MetricsCollection` is reverse-mapped from `EnabledMetrics` (already\n * present on the primary `DescribeAutoScalingGroups` response, so no\n * extra call is needed).\n *\n * Returns `undefined` when the group is gone.\n */\n async readCurrentState(\n physicalId: string,\n _logicalId: string,\n _resourceType: string\n ): Promise<Record<string, unknown> | undefined> {\n // Fire the four reads in parallel. Sub-shape failures are best-effort\n // so a single permission gap does not break the whole drift read.\n const groupPromise = (async () => {\n try {\n return await this.describeGroup(physicalId);\n } catch (err) {\n if (this.isNotFoundError(err)) return undefined;\n throw err;\n }\n })();\n\n const lifecycleHooksPromise = this.getClient()\n .send(new DescribeLifecycleHooksCommand({ AutoScalingGroupName: physicalId }))\n .then((r) => r.LifecycleHooks ?? [])\n .catch((err) => {\n this.logger.debug(\n `DescribeLifecycleHooks(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return [];\n });\n\n const trafficSourcesPromise = this.getClient()\n .send(new DescribeTrafficSourcesCommand({ AutoScalingGroupName: physicalId }))\n .then((r) => r.TrafficSources ?? [])\n .catch((err) => {\n this.logger.debug(\n `DescribeTrafficSources(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return [];\n });\n\n const notificationsPromise = this.getClient()\n .send(new DescribeNotificationConfigurationsCommand({ AutoScalingGroupNames: [physicalId] }))\n .then((r) => r.NotificationConfigurations ?? [])\n .catch((err) => {\n this.logger.debug(\n `DescribeNotificationConfigurations(${physicalId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return [];\n });\n\n const [group, lifecycleHooks, trafficSources, notifications] = await Promise.all([\n groupPromise,\n lifecycleHooksPromise,\n trafficSourcesPromise,\n notificationsPromise,\n ]);\n\n if (!group) return undefined;\n\n const result: Record<string, unknown> = {};\n if (group.AutoScalingGroupName !== undefined) {\n result['AutoScalingGroupName'] = group.AutoScalingGroupName;\n }\n if (group.LaunchTemplate) {\n const lt: Record<string, unknown> = {};\n if (group.LaunchTemplate.LaunchTemplateId !== undefined) {\n lt['LaunchTemplateId'] = group.LaunchTemplate.LaunchTemplateId;\n }\n if (group.LaunchTemplate.LaunchTemplateName !== undefined) {\n lt['LaunchTemplateName'] = group.LaunchTemplate.LaunchTemplateName;\n }\n if (group.LaunchTemplate.Version !== undefined) {\n lt['Version'] = group.LaunchTemplate.Version;\n }\n result['LaunchTemplate'] = lt;\n }\n result['MinSize'] = group.MinSize ?? 0;\n result['MaxSize'] = group.MaxSize ?? 0;\n if (group.DesiredCapacity !== undefined) result['DesiredCapacity'] = group.DesiredCapacity;\n // VPCZoneIdentifier round-trips back to the CFn list shape so the\n // comparator sees the same array the template emitted, not the\n // SDK-side comma-joined string.\n if (group.VPCZoneIdentifier !== undefined && group.VPCZoneIdentifier !== '') {\n result['VPCZoneIdentifier'] = group.VPCZoneIdentifier.split(',').map((s) => s.trim());\n } else {\n result['VPCZoneIdentifier'] = [];\n }\n result['AvailabilityZones'] = group.AvailabilityZones ?? [];\n if (group.HealthCheckType !== undefined) result['HealthCheckType'] = group.HealthCheckType;\n if (group.HealthCheckGracePeriod !== undefined) {\n result['HealthCheckGracePeriod'] = group.HealthCheckGracePeriod;\n }\n if (group.DefaultCooldown !== undefined) {\n // CFn template field is `Cooldown`; SDK / Describe response calls it\n // `DefaultCooldown`. Surface under the CFn name so the comparator\n // matches state directly.\n result['Cooldown'] = group.DefaultCooldown;\n }\n result['NewInstancesProtectedFromScaleIn'] = group.NewInstancesProtectedFromScaleIn ?? false;\n result['TerminationPolicies'] = group.TerminationPolicies ?? [];\n result['CapacityRebalance'] = group.CapacityRebalance ?? false;\n if (group.ServiceLinkedRoleARN !== undefined) {\n result['ServiceLinkedRoleARN'] = group.ServiceLinkedRoleARN;\n }\n if (group.MaxInstanceLifetime !== undefined) {\n result['MaxInstanceLifetime'] = group.MaxInstanceLifetime;\n }\n result['LoadBalancerNames'] = group.LoadBalancerNames ?? [];\n result['TargetGroupARNs'] = group.TargetGroupARNs ?? [];\n if (group.Context !== undefined) result['Context'] = group.Context;\n if (group.DesiredCapacityType !== undefined) {\n result['DesiredCapacityType'] = group.DesiredCapacityType;\n }\n if (group.DefaultInstanceWarmup !== undefined) {\n result['DefaultInstanceWarmup'] = group.DefaultInstanceWarmup;\n }\n if (group.MixedInstancesPolicy !== undefined) {\n result['MixedInstancesPolicy'] = group.MixedInstancesPolicy;\n }\n if (group.AvailabilityZoneDistribution !== undefined) {\n result['AvailabilityZoneDistribution'] = group.AvailabilityZoneDistribution;\n }\n if (group.AvailabilityZoneImpairmentPolicy !== undefined) {\n result['AvailabilityZoneImpairmentPolicy'] = group.AvailabilityZoneImpairmentPolicy;\n }\n if (group.CapacityReservationSpecification !== undefined) {\n result['CapacityReservationSpecification'] = group.CapacityReservationSpecification;\n }\n if (group.InstanceMaintenancePolicy !== undefined) {\n result['InstanceMaintenancePolicy'] = group.InstanceMaintenancePolicy;\n }\n if (group.DeletionProtection !== undefined) {\n result['DeletionProtection'] = group.DeletionProtection;\n } else {\n // AWS reports `undefined` when the group has the AWS-side default\n // (`'none'`). Always-emit placeholder so the v3 `observedProperties`\n // baseline catches a console-side flip to `prevent-force-deletion`\n // / `prevent-all-deletion`.\n result['DeletionProtection'] = 'none';\n }\n // Tags: filter aws:* prefix and normalize to CFn shape sorted by Key.\n // ASG returns Tags inside the AutoScalingGroup record (already populated\n // by DescribeAutoScalingGroups — no separate ListTagsForResource call).\n result['Tags'] = normalizeAwsTagsToCfn(group.Tags);\n\n // Sub-shapes — reverse-map AWS responses to CFn template shape and\n // always-emit `[]` placeholders so the v3 `observedProperties` baseline\n // catches console-side ADDs to a previously-empty list.\n result['MetricsCollection'] = mapEnabledMetricsToCfn(group.EnabledMetrics);\n result['LifecycleHookSpecificationList'] = mapLifecycleHooksToCfn(lifecycleHooks);\n // Strip ALL elbv2 / elb entries from TrafficSources — the canonical\n // attachment state for these types lives in TargetGroupARNs /\n // LoadBalancerNames. TrafficSources is meant for attachment types\n // without a dedicated CFn property (VPC Lattice, VPC Endpoint\n // Service). Filtering unconditionally avoids two failure modes\n // surfaced by tests/integration/drift-revert-vpc (PR #547):\n // double-attach/detach on revert, and stale TS entries from AWS's\n // eventual-consistency window after Attach/Detach surfacing as\n // false drift on the next read.\n const dedupedTrafficSources = trafficSources.filter((t) => {\n if (t.Identifier === undefined) return false;\n if (t.Type === 'elbv2' || t.Type === 'elb') return false;\n return true;\n });\n result['TrafficSources'] = mapTrafficSourcesToCfn(dedupedTrafficSources);\n result['NotificationConfigurations'] = mapNotificationsToCfn(notifications);\n\n return result;\n }\n\n // ─── Helpers ──────────────────────────────────────────────────────\n\n private buildLaunchTemplate(\n properties: Record<string, unknown>\n ): LaunchTemplateSpecification | undefined {\n const lt = properties['LaunchTemplate'] as\n | { LaunchTemplateId?: string; LaunchTemplateName?: string; Version?: string | number }\n | undefined;\n if (!lt) return undefined;\n const out: LaunchTemplateSpecification = {};\n // AWS UpdateAutoScalingGroup rejects when both LaunchTemplateId and\n // LaunchTemplateName are present in the same LaunchTemplate object\n // (\"Valid requests must contain either launchTemplateId or\n // LaunchTemplateName\"). DescribeAutoScalingGroups returns both, so\n // a straight readCurrentState → update round-trip on `drift --revert`\n // would hit this. Prefer the ID (canonical, doesn't change on LT\n // rename) and only fall back to Name when ID is absent.\n if (lt.LaunchTemplateId !== undefined) {\n out.LaunchTemplateId = lt.LaunchTemplateId;\n if (lt.LaunchTemplateName !== undefined) {\n // User templated BOTH — AWS would reject the resulting Create /\n // Update otherwise; we silently prefer the ID. Surface the\n // choice in --verbose so a user wondering why their Name didn't\n // take effect has an auditable signal.\n this.logger.debug(\n `buildLaunchTemplate: both LaunchTemplateId (${lt.LaunchTemplateId}) and LaunchTemplateName (${lt.LaunchTemplateName}) templated; dropping Name (#551)`\n );\n }\n } else if (lt.LaunchTemplateName !== undefined) {\n out.LaunchTemplateName = lt.LaunchTemplateName;\n }\n if (lt.Version !== undefined) {\n // Defensive coercion: AWS SDK `LaunchTemplateSpecification.Version`\n // is `string` and AWS rejects non-string forms with `Invalid\n // launch template version: either '$Default', '$Latest', or a\n // numeric version are allowed.`. cdkd's `IntrinsicResolver`\n // resolves `Fn::GetAtt <LaunchTemplate>.LatestVersionNumber`\n // through a per-type lookup; intermediate cases could surface\n // numeric values, so we coerce defensively.\n out.Version = String(lt.Version);\n }\n if (out.LaunchTemplateId === undefined && out.LaunchTemplateName === undefined) {\n return undefined;\n }\n return out;\n }\n\n /**\n * CFn `Tags` is `[{Key, Value, PropagateAtLaunch?}]`. AWS expects each\n * tag to also carry `ResourceId: <groupName>` and `ResourceType:\n * 'auto-scaling-group'`. We tack those on at create time so the SDK\n * input shape matches without forcing the user to template them.\n */\n private buildTags(groupName: string, properties: Record<string, unknown>): ASGTag[] {\n const raw = properties['Tags'] as\n | Array<{ Key?: string; Value?: string; PropagateAtLaunch?: boolean }>\n | undefined;\n if (!raw) return [];\n return raw\n .filter((t) => t.Key !== undefined)\n .map((t) => ({\n ResourceId: groupName,\n ResourceType: 'auto-scaling-group',\n Key: t.Key as string,\n Value: t.Value ?? '',\n PropagateAtLaunch: t.PropagateAtLaunch ?? false,\n }));\n }\n\n /**\n * CFn `VPCZoneIdentifier` is a list of subnet ids; the AWS SDK input\n * field is a comma-joined string.\n */\n private joinVpcZoneIdentifier(value: unknown): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (Array.isArray(value)) {\n const cleaned = value.map((v) => String(v).trim()).filter((v) => v.length > 0);\n if (cleaned.length === 0) return undefined;\n return cleaned.join(',');\n }\n if (typeof value === 'string') return value;\n return undefined;\n }\n\n private async describeGroup(groupName: string) {\n const response = await this.getClient().send(\n new DescribeAutoScalingGroupsCommand({\n AutoScalingGroupNames: [groupName],\n })\n );\n return response.AutoScalingGroups?.[0];\n }\n\n /**\n * Flip EC2-level termination protection (`DisableApiTermination`) off on\n * every instance currently launched by the group, so the subsequent\n * `DeleteAutoScalingGroup(ForceDelete: true)` can actually terminate them\n * instead of orphaning the protected instances (issue #796). Best-effort:\n * a Describe failure or a per-instance flip failure is logged at debug and\n * does not block the delete (the modify WRITE lags the terminate READ, so\n * the shared helper swallows propagation errors the same way the EC2 path\n * does — the orphan, if any, surfaces as a leftover instance the caller\n * can clean up rather than a hard delete failure).\n */\n private async removeInstanceTerminationProtection(\n groupName: string,\n logicalId: string\n ): Promise<void> {\n let instanceIds: string[];\n try {\n const group = await this.describeGroup(groupName);\n instanceIds = (group?.Instances ?? [])\n .map((i) => i.InstanceId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0);\n } catch (describeError) {\n this.logger.debug(\n `Could not enumerate instances of AutoScalingGroup ${logicalId} for termination-protection removal: ${describeError instanceof Error ? describeError.message : String(describeError)}`\n );\n return;\n }\n\n if (instanceIds.length === 0) return;\n\n this.logger.debug(\n `Disabling EC2 termination protection on ${instanceIds.length} instance(s) of AutoScalingGroup ${logicalId} before force delete`\n );\n for (const instanceId of instanceIds) {\n await disableInstanceApiTermination(this.getEc2Client(), instanceId, this.logger);\n }\n }\n\n private async fetchArn(groupName: string): Promise<string | undefined> {\n try {\n const group = await this.describeGroup(groupName);\n return group?.AutoScalingGroupARN;\n } catch (err) {\n this.logger.debug(\n `DescribeAutoScalingGroups(${groupName}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n return undefined;\n }\n }\n\n private isNotFoundError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const name = (error as { name?: string }).name ?? '';\n const message = error.message.toLowerCase();\n // ASG returns ValidationError with message \"AutoScalingGroup name not\n // found\" rather than a typed NotFound exception; cover both shapes.\n return (\n name === 'ValidationError' &&\n (message.includes('autoscalinggroup name not found') ||\n message.includes('not found') ||\n message.includes('does not exist'))\n );\n }\n\n private async waitForGroupDeleted(groupName: string, maxWaitMs = 900_000): Promise<void> {\n const startTime = Date.now();\n let delay = 5_000;\n\n while (Date.now() - startTime < maxWaitMs) {\n try {\n const group = await this.describeGroup(groupName);\n if (!group) return;\n } catch (error) {\n if (this.isNotFoundError(error)) return;\n throw error;\n }\n\n await this.sleep(delay);\n delay = Math.min(delay * 2, 10_000);\n }\n\n throw new Error(\n `Timed out waiting for AutoScalingGroup ${groupName} to be deleted (15 minute cap)`\n );\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // ─── Sub-shape diff helpers ───────────────────────────────────────\n // Each helper is a no-op when before/after JSON is identical (the cheap\n // structural-equality check happens first; we only build SDK calls for\n // genuine diffs). Identity is positional within the array per CFn shape:\n // `MetricsCollection` keyed on `Granularity`, `LifecycleHookSpecification\n // List` on `LifecycleHookName`, `TrafficSources` on `Identifier`,\n // `NotificationConfigurations` on `TopicARN`.\n\n /**\n * Diff and apply changes to the ASG's `Tags` property via the\n * `CreateOrUpdateTags` / `DeleteTags` AWS APIs (#475). CFn Tags shape is\n * `[{Key, Value, PropagateAtLaunch}]`; AWS Tag input adds `ResourceId`\n * (= the ASG name) and `ResourceType: 'auto-scaling-group'`.\n *\n * Diff semantics:\n * - Removed keys → `DeleteTags`.\n * - Added keys → `CreateOrUpdateTags`.\n * - Modified value or `PropagateAtLaunch` flag → `CreateOrUpdateTags`\n * (the AWS API upserts by `(ResourceId, ResourceType, Key)` tuple, so\n * a single upsert call replaces the old value).\n *\n * No-op when before/after JSON is identical.\n */\n private async applyTagsDiff(physicalId: string, next: unknown, prev: unknown): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n type CfnTag = { Key?: string; Value?: string; PropagateAtLaunch?: boolean };\n const nextEntries = (Array.isArray(next) ? next : []) as CfnTag[];\n const prevEntries = (Array.isArray(prev) ? prev : []) as CfnTag[];\n const nextByKey = new Map<string, CfnTag>();\n for (const t of nextEntries) {\n if (t.Key) nextByKey.set(t.Key, t);\n }\n const prevByKey = new Map<string, CfnTag>();\n for (const t of prevEntries) {\n if (t.Key) prevByKey.set(t.Key, t);\n }\n // Delete keys removed from `next`.\n const toDelete: CfnTag[] = [];\n for (const [key, tag] of prevByKey) {\n if (!nextByKey.has(key)) toDelete.push(tag);\n }\n if (toDelete.length > 0) {\n await this.getClient().send(\n new DeleteTagsCommand({\n // DeleteTags is keyed only by (ResourceId, ResourceType, Key).\n // Intentionally omit `Value` / `PropagateAtLaunch`: AWS treats\n // those as additional match constraints, so passing the\n // cdkd-recorded values would silently no-op when a console-side\n // edit drifted them between deploys. cdkd owns the tag, so\n // delete-by-key matches the \"we own the resource\" intent.\n Tags: toDelete.map((t) => ({\n ResourceId: physicalId,\n ResourceType: 'auto-scaling-group',\n Key: t.Key as string,\n })),\n })\n );\n }\n // Upsert keys whose value / propagate-flag differs.\n const toUpsert: CfnTag[] = [];\n for (const [key, tag] of nextByKey) {\n const before = prevByKey.get(key);\n if (JSON.stringify(before) === JSON.stringify(tag)) continue;\n toUpsert.push(tag);\n }\n if (toUpsert.length > 0) {\n await this.getClient().send(\n new CreateOrUpdateTagsCommand({\n Tags: toUpsert.map((t) => ({\n ResourceId: physicalId,\n ResourceType: 'auto-scaling-group',\n Key: t.Key as string,\n ...(t.Value !== undefined && { Value: t.Value }),\n ...(t.PropagateAtLaunch !== undefined && {\n PropagateAtLaunch: t.PropagateAtLaunch,\n }),\n })),\n })\n );\n }\n }\n\n /**\n * Diff `LoadBalancerNames` (Classic Load Balancers) and issue\n * `AttachLoadBalancers` / `DetachLoadBalancers` for the delta (#476).\n * Names are opaque strings; AWS allows N attached LBs per ASG so this\n * helper batches every add into one Attach call and every remove into\n * one Detach call. No-op when before/after JSON is identical.\n */\n private async applyLoadBalancerNamesDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextNames = (Array.isArray(next) ? next : []).filter(\n (n): n is string => typeof n === 'string'\n );\n const prevNames = (Array.isArray(prev) ? prev : []).filter(\n (n): n is string => typeof n === 'string'\n );\n const nextSet = new Set(nextNames);\n const prevSet = new Set(prevNames);\n const toAttach = nextNames.filter((n) => !prevSet.has(n));\n const toDetach = prevNames.filter((n) => !nextSet.has(n));\n if (toDetach.length > 0) {\n await this.getClient().send(\n new DetachLoadBalancersCommand({\n AutoScalingGroupName: physicalId,\n LoadBalancerNames: toDetach,\n })\n );\n }\n if (toAttach.length > 0) {\n await this.getClient().send(\n new AttachLoadBalancersCommand({\n AutoScalingGroupName: physicalId,\n LoadBalancerNames: toAttach,\n })\n );\n }\n }\n\n /**\n * Diff `TargetGroupARNs` (ALB / NLB target groups) and issue\n * `AttachLoadBalancerTargetGroups` /\n * `DetachLoadBalancerTargetGroups` for the delta (#476). Target-group\n * ARNs are opaque strings; same per-call batching pattern as\n * `applyLoadBalancerNamesDiff`. No-op when before/after JSON is\n * identical.\n */\n private async applyTargetGroupArnsDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextArns = (Array.isArray(next) ? next : []).filter(\n (a): a is string => typeof a === 'string'\n );\n const prevArns = (Array.isArray(prev) ? prev : []).filter(\n (a): a is string => typeof a === 'string'\n );\n const nextSet = new Set(nextArns);\n const prevSet = new Set(prevArns);\n const toAttach = nextArns.filter((a) => !prevSet.has(a));\n const toDetach = prevArns.filter((a) => !nextSet.has(a));\n if (toDetach.length > 0) {\n await this.getClient().send(\n new DetachLoadBalancerTargetGroupsCommand({\n AutoScalingGroupName: physicalId,\n TargetGroupARNs: toDetach,\n })\n );\n }\n if (toAttach.length > 0) {\n await this.getClient().send(\n new AttachLoadBalancerTargetGroupsCommand({\n AutoScalingGroupName: physicalId,\n TargetGroupARNs: toAttach,\n })\n );\n }\n // AttachLoadBalancerTargetGroups is async — the target group starts in\n // 'Adding' state and only becomes visible in\n // DescribeAutoScalingGroups.TargetGroupARNs after AWS internal\n // propagation. A subsequent `cdkd drift` read right after the call\n // returns can otherwise see a stale snapshot and report drift\n // against the AWS-side empty list (surfaced by tests/integration/\n // drift-revert-vpc's step-6 \"drift again\" check). Bounded poll to\n // confirm the post-state matches the intent before returning so the\n // caller's next read is consistent.\n if (toDetach.length > 0 || toAttach.length > 0) {\n await this.waitForTargetGroupArnsConvergence(physicalId, new Set(nextArns));\n }\n }\n\n private static readonly TG_CONVERGENCE_TIMEOUT_MS = 30_000;\n private static readonly TG_CONVERGENCE_POLL_INTERVAL_MS = 1_000;\n\n private async waitForTargetGroupArnsConvergence(\n physicalId: string,\n expected: Set<string>\n ): Promise<void> {\n const deadlineMs = Date.now() + ASGProvider.TG_CONVERGENCE_TIMEOUT_MS;\n let lastObserved: Set<string> = new Set();\n while (Date.now() < deadlineMs) {\n let resp: DescribeAutoScalingGroupsCommandOutput | undefined;\n try {\n resp = await this.getClient().send(\n new DescribeAutoScalingGroupsCommand({ AutoScalingGroupNames: [physicalId] })\n );\n } catch (err) {\n // Transient throttle / network blip during the 30s window must\n // not throw out of applyTargetGroupArnsDiff — the Attach/Detach\n // already succeeded, and propagating would fail the whole\n // update path. Log + retry; the loop will fall through to the\n // timeout-warn path if the API is genuinely down.\n this.logger.debug(\n `applyTargetGroupArnsDiff convergence poll: transient error, retrying — ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n await new Promise((r) => setTimeout(r, ASGProvider.TG_CONVERGENCE_POLL_INTERVAL_MS));\n continue;\n }\n lastObserved = new Set(resp.AutoScalingGroups?.[0]?.TargetGroupARNs ?? []);\n if (lastObserved.size === expected.size && [...expected].every((a) => lastObserved.has(a))) {\n return;\n }\n await new Promise((r) => setTimeout(r, ASGProvider.TG_CONVERGENCE_POLL_INTERVAL_MS));\n }\n // Timeout — surface as a warning rather than failure so the caller\n // still sees the SDK-side success; drift can re-report if the\n // propagation is still stuck. Includes observed vs expected so\n // post-mortem doesn't need a re-deploy.\n // Sort both sides before stringify for visual symmetry — expected\n // comes from the caller's insertion order, observed from AWS-side\n // order; eyeballing the diff in logs is easier when both are sorted.\n const expectedSorted = [...expected].sort();\n const observedSorted = [...lastObserved].sort();\n this.logger.warn(\n `applyTargetGroupArnsDiff: TG set did not converge within ${ASGProvider.TG_CONVERGENCE_TIMEOUT_MS}ms for ASG ${physicalId}. expected=${JSON.stringify(expectedSorted)} observed=${JSON.stringify(observedSorted)}`\n );\n }\n\n private async applyMetricsCollectionDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextEntries = (Array.isArray(next) ? next : []) as Array<{\n Granularity?: string;\n Metrics?: string[];\n }>;\n const prevEntries = (Array.isArray(prev) ? prev : []) as Array<{\n Granularity?: string;\n Metrics?: string[];\n }>;\n const prevByGranularity = new Map<string, string[] | undefined>();\n for (const e of prevEntries) {\n if (e.Granularity) prevByGranularity.set(e.Granularity, e.Metrics);\n }\n const nextByGranularity = new Map<string, string[] | undefined>();\n for (const e of nextEntries) {\n if (e.Granularity) nextByGranularity.set(e.Granularity, e.Metrics);\n }\n // Disable removed granularities first, then issue Enable for the\n // intended state of every Granularity in `next`. AWS treats Enable as\n // additive within a Granularity, so a remove-then-add pattern works\n // even when the Metrics list shrinks.\n for (const [granularity, metrics] of prevByGranularity) {\n if (!nextByGranularity.has(granularity)) {\n await this.getClient().send(\n new DisableMetricsCollectionCommand({\n AutoScalingGroupName: physicalId,\n ...(metrics && metrics.length > 0 ? { Metrics: metrics } : {}),\n })\n );\n }\n }\n for (const [granularity, metrics] of nextByGranularity) {\n const before = prevByGranularity.get(granularity);\n if (JSON.stringify(before ?? null) === JSON.stringify(metrics ?? null)) continue;\n // If the Metrics list shrunk, disable the removed metrics first\n // (AWS Enable is additive). When `metrics` is undefined or empty,\n // AWS treats that as \"all metrics\" — disable any prior subset\n // before re-enabling the full set.\n if (before && before.length > 0) {\n const removed = metrics ? before.filter((m) => !metrics.includes(m)) : [];\n if (removed.length > 0) {\n await this.getClient().send(\n new DisableMetricsCollectionCommand({\n AutoScalingGroupName: physicalId,\n Metrics: removed,\n })\n );\n }\n }\n await this.getClient().send(\n new EnableMetricsCollectionCommand({\n AutoScalingGroupName: physicalId,\n Granularity: granularity,\n ...(metrics && metrics.length > 0 ? { Metrics: metrics } : {}),\n })\n );\n }\n }\n\n private async applyLifecycleHooksDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextEntries = (Array.isArray(next) ? next : []) as Array<{\n LifecycleHookName?: string;\n LifecycleTransition?: string;\n RoleARN?: string;\n NotificationTargetARN?: string;\n NotificationMetadata?: string;\n HeartbeatTimeout?: number;\n DefaultResult?: string;\n }>;\n const prevEntries = (Array.isArray(prev) ? prev : []) as Array<{\n LifecycleHookName?: string;\n }>;\n const nextNames = new Set(\n nextEntries.map((e) => e.LifecycleHookName).filter((n): n is string => !!n)\n );\n // Delete hooks no longer in `next`.\n for (const e of prevEntries) {\n if (e.LifecycleHookName && !nextNames.has(e.LifecycleHookName)) {\n await this.getClient().send(\n new DeleteLifecycleHookCommand({\n AutoScalingGroupName: physicalId,\n LifecycleHookName: e.LifecycleHookName,\n })\n );\n }\n }\n // PutLifecycleHook is upsert — issue for every hook in `next` whose\n // shape differs from the matching `prev` entry.\n const prevByName = new Map<string, unknown>();\n for (const e of prevEntries) {\n if (e.LifecycleHookName) prevByName.set(e.LifecycleHookName, e);\n }\n for (const e of nextEntries) {\n if (!e.LifecycleHookName) continue;\n const prevHook = prevByName.get(e.LifecycleHookName);\n if (JSON.stringify(prevHook) === JSON.stringify(e)) continue;\n await this.getClient().send(\n new PutLifecycleHookCommand({\n AutoScalingGroupName: physicalId,\n LifecycleHookName: e.LifecycleHookName,\n ...(e.LifecycleTransition !== undefined && {\n LifecycleTransition: e.LifecycleTransition,\n }),\n ...(e.RoleARN !== undefined && { RoleARN: e.RoleARN }),\n ...(e.NotificationTargetARN !== undefined && {\n NotificationTargetARN: e.NotificationTargetARN,\n }),\n ...(e.NotificationMetadata !== undefined && {\n NotificationMetadata: e.NotificationMetadata,\n }),\n ...(e.HeartbeatTimeout !== undefined && { HeartbeatTimeout: e.HeartbeatTimeout }),\n ...(e.DefaultResult !== undefined && { DefaultResult: e.DefaultResult }),\n })\n );\n }\n }\n\n private async applyTrafficSourcesDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n const nextEntries = (Array.isArray(next) ? next : []) as Array<{\n Identifier?: string;\n Type?: string;\n }>;\n const prevEntries = (Array.isArray(prev) ? prev : []) as Array<{\n Identifier?: string;\n Type?: string;\n }>;\n const nextIds = new Set(nextEntries.map((e) => e.Identifier).filter((i): i is string => !!i));\n const prevIds = new Set(prevEntries.map((e) => e.Identifier).filter((i): i is string => !!i));\n const toDetach = prevEntries.filter((e) => e.Identifier && !nextIds.has(e.Identifier));\n const toAttach = nextEntries.filter((e) => e.Identifier && !prevIds.has(e.Identifier));\n if (toDetach.length > 0) {\n await this.getClient().send(\n new DetachTrafficSourcesCommand({\n AutoScalingGroupName: physicalId,\n TrafficSources: toDetach.map((e) => ({\n Identifier: e.Identifier as string,\n ...(e.Type !== undefined && { Type: e.Type }),\n })),\n })\n );\n }\n if (toAttach.length > 0) {\n await this.getClient().send(\n new AttachTrafficSourcesCommand({\n AutoScalingGroupName: physicalId,\n TrafficSources: toAttach.map((e) => ({\n Identifier: e.Identifier as string,\n ...(e.Type !== undefined && { Type: e.Type }),\n })),\n })\n );\n }\n }\n\n private async applyNotificationConfigurationsDiff(\n physicalId: string,\n next: unknown,\n prev: unknown\n ): Promise<void> {\n if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;\n // CFn `NotificationConfigurations` is an array of `{TopicARN,\n // NotificationTypes[]}`; AWS `PutNotificationConfiguration` is keyed\n // by TopicARN — one call per topic. AWS reports each notification\n // type as a separate response entry (one row per `(asgName, topicArn,\n // notificationType)` triple), but cdkd state stores the CFn shape, so\n // both sides of the diff share the per-topic key.\n const nextEntries = (Array.isArray(next) ? next : []) as Array<{\n TopicARN?: string;\n NotificationTypes?: string[];\n }>;\n const prevEntries = (Array.isArray(prev) ? prev : []) as Array<{\n TopicARN?: string;\n NotificationTypes?: string[];\n }>;\n const nextByTopic = new Map<string, string[] | undefined>();\n for (const e of nextEntries) {\n if (e.TopicARN) nextByTopic.set(e.TopicARN, e.NotificationTypes);\n }\n const prevByTopic = new Map<string, string[] | undefined>();\n for (const e of prevEntries) {\n if (e.TopicARN) prevByTopic.set(e.TopicARN, e.NotificationTypes);\n }\n for (const topic of prevByTopic.keys()) {\n if (!nextByTopic.has(topic)) {\n await this.getClient().send(\n new DeleteNotificationConfigurationCommand({\n AutoScalingGroupName: physicalId,\n TopicARN: topic,\n })\n );\n }\n }\n for (const [topic, types] of nextByTopic) {\n const before = prevByTopic.get(topic);\n if (JSON.stringify(before ?? null) === JSON.stringify(types ?? null)) continue;\n await this.getClient().send(\n new PutNotificationConfigurationCommand({\n AutoScalingGroupName: physicalId,\n TopicARN: topic,\n NotificationTypes: types ?? [],\n })\n );\n }\n }\n}\n\n// ─── File-level reverse-mappers (CFn template shape) ────────────────\n\n/**\n * Reverse-map AWS `EnabledMetrics: [{Metric, Granularity}]` (flat list,\n * one row per enabled metric) back to the CFn array shape\n * `[{Granularity, Metrics?[]}]`. Metrics with the same Granularity are\n * grouped together; the resulting Metrics list is sorted alphabetically\n * for stable positional compare in the drift comparator.\n *\n * Always returns a placeholder `[]` per the cdkd PR #145 always-emit\n * convention so a console-side EnableMetricsCollection on a previously-\n * empty group surfaces as drift on the v3 `observedProperties` baseline.\n */\nfunction mapEnabledMetricsToCfn(\n enabledMetrics:\n | Array<{ Metric?: string | undefined; Granularity?: string | undefined }>\n | undefined\n): Array<{ Granularity: string; Metrics?: string[] }> {\n if (!enabledMetrics || enabledMetrics.length === 0) return [];\n const byGranularity = new Map<string, Set<string>>();\n for (const e of enabledMetrics) {\n const g = e.Granularity;\n if (!g) continue;\n let set = byGranularity.get(g);\n if (!set) {\n set = new Set();\n byGranularity.set(g, set);\n }\n if (e.Metric) set.add(e.Metric);\n }\n const result: Array<{ Granularity: string; Metrics?: string[] }> = [];\n // Sort by Granularity for stable positional compare.\n for (const granularity of Array.from(byGranularity.keys()).sort()) {\n const metrics = Array.from(byGranularity.get(granularity) ?? []).sort();\n result.push(\n metrics.length > 0\n ? { Granularity: granularity, Metrics: metrics }\n : { Granularity: granularity }\n );\n }\n return result;\n}\n\n/**\n * Reverse-map AWS `DescribeLifecycleHooks` response to the CFn\n * `LifecycleHookSpecificationList` shape. Each hook is surfaced under the\n * exact CFn property name. AWS-side fields cdkd state never carried\n * (`AutoScalingGroupName` — duplicated on every hook by AWS,\n * `GlobalTimeout` — AWS-derived) are filtered out. Sorted by\n * LifecycleHookName for stable positional compare.\n */\nfunction mapLifecycleHooksToCfn(\n hooks: Array<{\n LifecycleHookName?: string | undefined;\n LifecycleTransition?: string | undefined;\n NotificationTargetARN?: string | undefined;\n RoleARN?: string | undefined;\n NotificationMetadata?: string | undefined;\n HeartbeatTimeout?: number | undefined;\n DefaultResult?: string | undefined;\n }>\n): Array<Record<string, unknown>> {\n if (!hooks || hooks.length === 0) return [];\n const result: Array<Record<string, unknown>> = [];\n for (const h of hooks) {\n if (!h.LifecycleHookName) continue;\n const entry: Record<string, unknown> = { LifecycleHookName: h.LifecycleHookName };\n if (h.LifecycleTransition !== undefined) entry['LifecycleTransition'] = h.LifecycleTransition;\n if (h.RoleARN !== undefined) entry['RoleARN'] = h.RoleARN;\n if (h.NotificationTargetARN !== undefined) {\n entry['NotificationTargetARN'] = h.NotificationTargetARN;\n }\n if (h.NotificationMetadata !== undefined) {\n entry['NotificationMetadata'] = h.NotificationMetadata;\n }\n if (h.HeartbeatTimeout !== undefined) entry['HeartbeatTimeout'] = h.HeartbeatTimeout;\n if (h.DefaultResult !== undefined) entry['DefaultResult'] = h.DefaultResult;\n result.push(entry);\n }\n result.sort((a, b) =>\n String(a['LifecycleHookName']).localeCompare(String(b['LifecycleHookName']))\n );\n return result;\n}\n\n/**\n * Reverse-map AWS `DescribeTrafficSources` response to the CFn\n * `TrafficSources` shape `[{Identifier, Type?}]`. AWS-side runtime fields\n * (`State`, the deprecated `TrafficSource` alias) are filtered out.\n * Sorted by Identifier for stable positional compare.\n */\nfunction mapTrafficSourcesToCfn(\n trafficSources: Array<{ Identifier?: string | undefined; Type?: string | undefined }>\n): Array<Record<string, unknown>> {\n if (!trafficSources || trafficSources.length === 0) return [];\n const result: Array<Record<string, unknown>> = [];\n for (const t of trafficSources) {\n if (!t.Identifier) continue;\n const entry: Record<string, unknown> = { Identifier: t.Identifier };\n if (t.Type !== undefined) entry['Type'] = t.Type;\n result.push(entry);\n }\n result.sort((a, b) => String(a['Identifier']).localeCompare(String(b['Identifier'])));\n return result;\n}\n\n/**\n * Reverse-map AWS `DescribeNotificationConfigurations` (a flat list, one\n * row per `(topicArn, notificationType)`) into the CFn shape\n * `[{TopicARN, NotificationTypes[]}]`. NotificationTypes are grouped per\n * TopicARN and sorted alphabetically for stable positional compare.\n */\nfunction mapNotificationsToCfn(\n configurations: Array<{ TopicARN?: string | undefined; NotificationType?: string | undefined }>\n): Array<Record<string, unknown>> {\n if (!configurations || configurations.length === 0) return [];\n const byTopic = new Map<string, Set<string>>();\n for (const c of configurations) {\n if (!c.TopicARN) continue;\n let set = byTopic.get(c.TopicARN);\n if (!set) {\n set = new Set();\n byTopic.set(c.TopicARN, set);\n }\n if (c.NotificationType) set.add(c.NotificationType);\n }\n const result: Array<Record<string, unknown>> = [];\n for (const topic of Array.from(byTopic.keys()).sort()) {\n const types = Array.from(byTopic.get(topic) ?? []).sort();\n result.push({ TopicARN: topic, NotificationTypes: types });\n }\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0FA,IAAa,cAAb,MAAa,YAAwC;CACnD,AAAQ;CACR,AAAQ;CACR,AAAiB,iBAAiB,QAAQ,IAAI;CAC9C,AAAQ,SAAS,UAAU,CAAC,CAAC,MAAM,aAAa;CAEhD,oCAAoB,IAAI,IAAiC,CACvD,CACE,sDACA,IAAI,IAAI;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CACH,CACF,CAAC;CAED,oCAAoB,IAAI,IAAyC,CAC/D,CACE,sDACA,IAAI,IAAoB,CACtB,CACE,2BACA,2EACF,GACA,CACE,6BACA,wFACF,CACF,CAAC,CACH,CACF,CAAC;CAED,AAAQ,YAA+B;EACrC,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,kBACnB,KAAK,iBAAiB,EAAE,QAAQ,KAAK,eAAe,IAAI,CAAC,CAC3D;EAEF,OAAO,KAAK;CACd;CAEA,AAAQ,eAA0B;EAChC,IAAI,CAAC,KAAK,WACR,KAAK,YAAY,IAAI,UAAU,KAAK,iBAAiB,EAAE,QAAQ,KAAK,eAAe,IAAI,CAAC,CAAC;EAE3F,OAAO,KAAK;CACd;CAIA,MAAM,OACJ,WACA,cACA,YAC+B;EAC/B,IAAI,iBAAiB,sCACnB,MAAM,IAAI,kBACR,8BAA8B,gBAC9B,cACA,SACF;EAGF,MAAM,YACH,WAAW,2BACZ,qBAAqB,WAAW,EAAE,WAAW,IAAI,CAAC;EAEpD,KAAK,OAAO,MAAM,6BAA6B,UAAU,IAAI,WAAW;EAExE,IAAI;GACF,MAAM,iBAAiB,KAAK,oBAAoB,UAAU;GAC1D,MAAM,OAAO,KAAK,UAAU,WAAW,UAAU;GACjD,MAAM,oBAAoB,KAAK,sBAAsB,WAAW,oBAAoB;GAEpF,MAAM,UAAU,WAAW,cAAc,OAAO,OAAO,WAAW,UAAU,IAAI;GAChF,MAAM,UAAU,WAAW,cAAc,OAAO,OAAO,WAAW,UAAU,IAAI;GAEhF,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,8BAA8B;IAChC,sBAAsB;IACtB,SAAS;IACT,SAAS;IACT,GAAI,WAAW,sBAAsB,QAAQ,EAC3C,iBAAiB,OAAO,WAAW,kBAAkB,EACvD;IACA,GAAI,kBAAkB,EAAE,gBAAgB,eAAe;IACvD,GAAI,WAAW,4BAA4B,UAAa,EACtD,sBAAsB,WAAW,wBACnC;IACA,GAAI,sBAAsB,UAAa,EAAE,mBAAmB,kBAAkB;IAC9E,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,uBAAuB,UAAa,EACjD,iBAAiB,WAAW,mBAC9B;IACA,GAAI,WAAW,6BAA6B,QAAQ,EAClD,wBAAwB,OAAO,WAAW,yBAAyB,EACrE;IACA,GAAI,WAAW,eAAe,QAAQ,EACpC,iBAAiB,OAAO,WAAW,WAAW,EAChD;IACA,GAAI,WAAW,sBAAsB,QAAQ,EAC3C,iBAAiB,OAAO,WAAW,kBAAkB,EACvD;IACA,GAAI,WAAW,2BAA2B,UAAa,EACrD,qBAAqB,WAAW,uBAClC;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,4BAA4B,UAAa,EACtD,sBAAsB,WAAW,wBACnC;IACA,GAAI,WAAW,0BAA0B,QAAQ,EAC/C,qBAAqB,OAAO,WAAW,sBAAsB,EAC/D;IACA,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,uBAAuB,UAAa,EACjD,iBAAiB,WAAW,mBAC9B;IACA,GAAI,WAAW,eAAe,UAAa,EACzC,SAAS,WAAW,WACtB;IACA,GAAI,WAAW,2BAA2B,UAAa,EACrD,qBAAqB,WAAW,uBAClC;IACA,GAAI,WAAW,4BAA4B,QAAQ,EACjD,uBAAuB,OAAO,WAAW,wBAAwB,EACnE;IACA,GAAI,WAAW,sCAAsC,UAAa,EAChE,gCAAgC,WAAW,kCAC7C;IACA,GAAI,WAAW,sBAAsB,UAAa,EAChD,gBAAgB,WAAW,kBAC7B;IACA,GAAI,WAAW,oCAAoC,UAAa,EAC9D,8BAA8B,WAAW,gCAC3C;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,gCAAgC,UAAa,EAC1D,0BAA0B,WAAW,4BACvC;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,iCAAiC,UAAa,EAC3D,2BAA2B,WAAW,6BACxC;IACA,GAAI,WAAW,0BAA0B,UAAa,EACpD,oBAAoB,WAAW,sBACjC;IACA,GAAI,KAAK,SAAS,KAAK,EAAE,MAAM,KAAK;GACtC,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,yCAAyC,UAAU,IAAI,WAAW;GAEpF,MAAM,MAAM,MAAM,KAAK,SAAS,SAAS;GACzC,MAAM,aAAsC,CAAC;GAC7C,IAAI,KAAK,WAAW,SAAS;GAC7B,IAAI,gBAAgB,kBAClB,WAAW,sBAAsB,eAAe;GAElD,OAAO;IAAE,YAAY;IAAW;GAAW;EAC7C,SAAS,OAAO;GACd,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,qCAAqC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACxG,cACA,WACA,WACA,KACF;EACF;CACF;CAEA,MAAM,OACJ,WACA,YACA,cACA,YACA,oBAC+B;EAC/B,IAAI,iBAAiB,sCACnB,MAAM,IAAI,kBACR,8BAA8B,gBAC9B,cACA,WACA,UACF;EAEF,KAAK,OAAO,MAAM,6BAA6B,UAAU,IAAI,YAAY;EAOzE,MAAM,YAAY,GAAY,MAAwB,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;EAC5F,IAAI,CAAC,SAAS,WAAW,yBAAyB,mBAAmB,uBAAuB,GAC1F,MAAM,IAAI,gCACR,cACA,WACA,8KACF;EAEF,IAAI;GAMF,MAAM,KAAK,cAAc,YAAY,WAAW,SAAS,mBAAmB,OAAO;GACnF,MAAM,KAAK,2BACT,YACA,WAAW,sBACX,mBAAmB,oBACrB;GACA,MAAM,KAAK,yBACT,YACA,WAAW,oBACX,mBAAmB,kBACrB;GACA,MAAM,KAAK,2BACT,YACA,WAAW,sBACX,mBAAmB,oBACrB;GACA,MAAM,KAAK,wBACT,YACA,WAAW,mCACX,mBAAmB,iCACrB;GACA,MAAM,KAAK,wBACT,YACA,WAAW,mBACX,mBAAmB,iBACrB;GACA,MAAM,KAAK,oCACT,YACA,WAAW,+BACX,mBAAmB,6BACrB;GAEA,MAAM,iBAAiB,KAAK,oBAAoB,UAAU;GAC1D,MAAM,oBAAoB,KAAK,sBAAsB,WAAW,oBAAoB;GAEpF,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,8BAA8B;IAChC,sBAAsB;IACtB,GAAI,WAAW,cAAc,QAAQ,EAAE,SAAS,OAAO,WAAW,UAAU,EAAE;IAC9E,GAAI,WAAW,cAAc,QAAQ,EAAE,SAAS,OAAO,WAAW,UAAU,EAAE;IAC9E,GAAI,WAAW,sBAAsB,QAAQ,EAC3C,iBAAiB,OAAO,WAAW,kBAAkB,EACvD;IACA,GAAI,kBAAkB,EAAE,gBAAgB,eAAe;IACvD,GAAI,WAAW,4BAA4B,UAAa,EACtD,sBAAsB,WAAW,wBACnC;IACA,GAAI,sBAAsB,UAAa,EAAE,mBAAmB,kBAAkB;IAC9E,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,uBAAuB,UAAa,EACjD,iBAAiB,WAAW,mBAC9B;IACA,GAAI,WAAW,6BAA6B,QAAQ,EAClD,wBAAwB,OAAO,WAAW,yBAAyB,EACrE;IACA,GAAI,WAAW,eAAe,QAAQ,EACpC,iBAAiB,OAAO,WAAW,WAAW,EAChD;IACA,GAAI,WAAW,sBAAsB,QAAQ,EAC3C,iBAAiB,OAAO,WAAW,kBAAkB,EACvD;IACA,GAAI,WAAW,2BAA2B,UAAa,EACrD,qBAAqB,WAAW,uBAClC;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,yBAAyB,UAAa,EACnD,mBAAmB,WAAW,qBAChC;IACA,GAAI,WAAW,4BAA4B,UAAa,EACtD,sBAAsB,WAAW,wBACnC;IACA,GAAI,WAAW,0BAA0B,QAAQ,EAC/C,qBAAqB,OAAO,WAAW,sBAAsB,EAC/D;IACA,GAAI,WAAW,eAAe,UAAa,EACzC,SAAS,WAAW,WACtB;IACA,GAAI,WAAW,2BAA2B,UAAa,EACrD,qBAAqB,WAAW,uBAClC;IACA,GAAI,WAAW,4BAA4B,QAAQ,EACjD,uBAAuB,OAAO,WAAW,wBAAwB,EACnE;IACA,GAAI,WAAW,oCAAoC,UAAa,EAC9D,8BAA8B,WAAW,gCAC3C;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,gCAAgC,UAAa,EAC1D,0BAA0B,WAAW,4BACvC;IACA,GAAI,WAAW,wCAAwC,UAAa,EAClE,kCAAkC,WAChC,oCAEJ;IACA,GAAI,WAAW,iCAAiC,UAAa,EAC3D,2BAA2B,WAAW,6BACxC;IACA,GAAI,WAAW,0BAA0B,UAAa,EACpD,oBAAoB,WAAW,sBACjC;GACF,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,yCAAyC,WAAW;GAEtE,MAAM,MAAM,MAAM,KAAK,SAAS,UAAU;GAC1C,MAAM,aAAsC,CAAC;GAC7C,IAAI,KAAK,WAAW,SAAS;GAC7B,IAAI,gBAAgB,kBAClB,WAAW,sBAAsB,eAAe;GAElD,OAAO;IAAE;IAAY,aAAa;IAAO;GAAW;EACtD,SAAS,OAAO;GACd,IAAI,iBAAiB,iCAAiC,MAAM;GAC5D,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,qCAAqC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACxG,cACA,WACA,YACA,KACF;EACF;CACF;CAEA,MAAM,OACJ,WACA,YACA,cACA,aACA,SACe;EACf,KAAK,OAAO,MAAM,6BAA6B,UAAU,IAAI,YAAY;EAWzE,IAAI,SAAS,qBAAqB,MAAM;GACtC,IAAI;IACF,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,8BAA8B;KAChC,sBAAsB;KACtB,oBAAoB;IACtB,CAAC,CACH;IACA,KAAK,OAAO,MACV,mDAAmD,UAAU,eAC/D;GACF,SAAS,WAAW;IAGlB,KAAK,OAAO,MACV,2CAA2C,WAAW,IAAI,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,GAC7H;GACF;GASA,MAAM,KAAK,oCAAoC,YAAY,SAAS;EACtE;EAEA,IAAI;GACF,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,8BAA8B;IAChC,sBAAsB;IACtB,aAAa,SAAS,qBAAqB;GAC7C,CAAC,CACH;GAEA,KAAK,OAAO,MAAM,uDAAuD,WAAW;GAKpF,MAAM,KAAK,oBAAoB,UAAU;EAC3C,SAAS,OAAO;GACd,IAAI,KAAK,gBAAgB,KAAK,GAAG;IAE/B,kBACE,MAFyB,KAAK,UAAU,CAAC,CAAC,OAAO,OAAO,GAGxD,SAAS,gBACT,cACA,WACA,UACF;IACA,KAAK,OAAO,MAAM,oBAAoB,WAAW,mCAAmC;IACpF;GACF;GACA,MAAM,QAAQ,iBAAiB,QAAQ,QAAQ;GAC/C,MAAM,IAAI,kBACR,qCAAqC,UAAU,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACxG,cACA,WACA,YACA,KACF;EACF;CACF;CAEA,MAAM,aACJ,YACA,eACA,eACkB;EAClB,MAAM,QAAQ,MAAM,KAAK,cAAc,UAAU;EACjD,IAAI,CAAC,OACH,MAAM,IAAI,kBACR,oBAAoB,WAAW,uCAAuC,iBACtE,sCACA,YACA,UACF;EAEF,QAAQ,eAAR;GACE,KAAK;GACL,KAAK,uBACH,OAAO,MAAM,uBAAuB;GACtC,KAAK,2BACH,OAAO,MAAM,2BAA2B;GAC1C,KAAK;GACL,KAAK,oBACH,OAAO,MAAM,gBAAgB,oBAAoB;GACnD,SACE,OAAO;EACX;CACF;;;;;;;;;;;;;;;;;;;;;;;;;CA0BA,MAAM,iBACJ,YACA,YACA,eAC8C;EAG9C,MAAM,gBAAgB,YAAY;GAChC,IAAI;IACF,OAAO,MAAM,KAAK,cAAc,UAAU;GAC5C,SAAS,KAAK;IACZ,IAAI,KAAK,gBAAgB,GAAG,GAAG,OAAO;IACtC,MAAM;GACR;EACF,EAAC,CAAE;EAEH,MAAM,wBAAwB,KAAK,UAAU,CAAC,CAC3C,KAAK,IAAI,8BAA8B,EAAE,sBAAsB,WAAW,CAAC,CAAC,CAAC,CAC7E,MAAM,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CACnC,OAAO,QAAQ;GACd,KAAK,OAAO,MACV,0BAA0B,WAAW,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAClG;GACA,OAAO,CAAC;EACV,CAAC;EAEH,MAAM,wBAAwB,KAAK,UAAU,CAAC,CAC3C,KAAK,IAAI,8BAA8B,EAAE,sBAAsB,WAAW,CAAC,CAAC,CAAC,CAC7E,MAAM,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CACnC,OAAO,QAAQ;GACd,KAAK,OAAO,MACV,0BAA0B,WAAW,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAClG;GACA,OAAO,CAAC;EACV,CAAC;EAEH,MAAM,uBAAuB,KAAK,UAAU,CAAC,CAC1C,KAAK,IAAI,0CAA0C,EAAE,uBAAuB,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAC5F,MAAM,MAAM,EAAE,8BAA8B,CAAC,CAAC,CAAC,CAC/C,OAAO,QAAQ;GACd,KAAK,OAAO,MACV,sCAAsC,WAAW,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAC9G;GACA,OAAO,CAAC;EACV,CAAC;EAEH,MAAM,CAAC,OAAO,gBAAgB,gBAAgB,iBAAiB,MAAM,QAAQ,IAAI;GAC/E;GACA;GACA;GACA;EACF,CAAC;EAED,IAAI,CAAC,OAAO,OAAO;EAEnB,MAAM,SAAkC,CAAC;EACzC,IAAI,MAAM,yBAAyB,QACjC,OAAO,0BAA0B,MAAM;EAEzC,IAAI,MAAM,gBAAgB;GACxB,MAAM,KAA8B,CAAC;GACrC,IAAI,MAAM,eAAe,qBAAqB,QAC5C,GAAG,sBAAsB,MAAM,eAAe;GAEhD,IAAI,MAAM,eAAe,uBAAuB,QAC9C,GAAG,wBAAwB,MAAM,eAAe;GAElD,IAAI,MAAM,eAAe,YAAY,QACnC,GAAG,aAAa,MAAM,eAAe;GAEvC,OAAO,oBAAoB;EAC7B;EACA,OAAO,aAAa,MAAM,WAAW;EACrC,OAAO,aAAa,MAAM,WAAW;EACrC,IAAI,MAAM,oBAAoB,QAAW,OAAO,qBAAqB,MAAM;EAI3E,IAAI,MAAM,sBAAsB,UAAa,MAAM,sBAAsB,IACvE,OAAO,uBAAuB,MAAM,kBAAkB,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC;OAEpF,OAAO,uBAAuB,CAAC;EAEjC,OAAO,uBAAuB,MAAM,qBAAqB,CAAC;EAC1D,IAAI,MAAM,oBAAoB,QAAW,OAAO,qBAAqB,MAAM;EAC3E,IAAI,MAAM,2BAA2B,QACnC,OAAO,4BAA4B,MAAM;EAE3C,IAAI,MAAM,oBAAoB,QAI5B,OAAO,cAAc,MAAM;EAE7B,OAAO,sCAAsC,MAAM,oCAAoC;EACvF,OAAO,yBAAyB,MAAM,uBAAuB,CAAC;EAC9D,OAAO,uBAAuB,MAAM,qBAAqB;EACzD,IAAI,MAAM,yBAAyB,QACjC,OAAO,0BAA0B,MAAM;EAEzC,IAAI,MAAM,wBAAwB,QAChC,OAAO,yBAAyB,MAAM;EAExC,OAAO,uBAAuB,MAAM,qBAAqB,CAAC;EAC1D,OAAO,qBAAqB,MAAM,mBAAmB,CAAC;EACtD,IAAI,MAAM,YAAY,QAAW,OAAO,aAAa,MAAM;EAC3D,IAAI,MAAM,wBAAwB,QAChC,OAAO,yBAAyB,MAAM;EAExC,IAAI,MAAM,0BAA0B,QAClC,OAAO,2BAA2B,MAAM;EAE1C,IAAI,MAAM,yBAAyB,QACjC,OAAO,0BAA0B,MAAM;EAEzC,IAAI,MAAM,iCAAiC,QACzC,OAAO,kCAAkC,MAAM;EAEjD,IAAI,MAAM,qCAAqC,QAC7C,OAAO,sCAAsC,MAAM;EAErD,IAAI,MAAM,qCAAqC,QAC7C,OAAO,sCAAsC,MAAM;EAErD,IAAI,MAAM,8BAA8B,QACtC,OAAO,+BAA+B,MAAM;EAE9C,IAAI,MAAM,uBAAuB,QAC/B,OAAO,wBAAwB,MAAM;OAMrC,OAAO,wBAAwB;EAKjC,OAAO,UAAU,sBAAsB,MAAM,IAAI;EAKjD,OAAO,uBAAuB,uBAAuB,MAAM,cAAc;EACzE,OAAO,oCAAoC,uBAAuB,cAAc;EAehF,OAAO,oBAAoB,uBALG,eAAe,QAAQ,MAAM;GACzD,IAAI,EAAE,eAAe,QAAW,OAAO;GACvC,IAAI,EAAE,SAAS,WAAW,EAAE,SAAS,OAAO,OAAO;GACnD,OAAO;EACT,CACsE,CAAC;EACvE,OAAO,gCAAgC,sBAAsB,aAAa;EAE1E,OAAO;CACT;CAIA,AAAQ,oBACN,YACyC;EACzC,MAAM,KAAK,WAAW;EAGtB,IAAI,CAAC,IAAI,OAAO;EAChB,MAAM,MAAmC,CAAC;EAQ1C,IAAI,GAAG,qBAAqB,QAAW;GACrC,IAAI,mBAAmB,GAAG;GAC1B,IAAI,GAAG,uBAAuB,QAK5B,KAAK,OAAO,MACV,+CAA+C,GAAG,iBAAiB,4BAA4B,GAAG,mBAAmB,kCACvH;EAEJ,OAAO,IAAI,GAAG,uBAAuB,QACnC,IAAI,qBAAqB,GAAG;EAE9B,IAAI,GAAG,YAAY,QAQjB,IAAI,UAAU,OAAO,GAAG,OAAO;EAEjC,IAAI,IAAI,qBAAqB,UAAa,IAAI,uBAAuB,QACnE;EAEF,OAAO;CACT;;;;;;;CAQA,AAAQ,UAAU,WAAmB,YAA+C;EAClF,MAAM,MAAM,WAAW;EAGvB,IAAI,CAAC,KAAK,OAAO,CAAC;EAClB,OAAO,IACJ,QAAQ,MAAM,EAAE,QAAQ,MAAS,CAAC,CAClC,KAAK,OAAO;GACX,YAAY;GACZ,cAAc;GACd,KAAK,EAAE;GACP,OAAO,EAAE,SAAS;GAClB,mBAAmB,EAAE,qBAAqB;EAC5C,EAAE;CACN;;;;;CAMA,AAAQ,sBAAsB,OAAoC;EAChE,IAAI,UAAU,UAAa,UAAU,MAAM,OAAO;EAClD,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,MAAM,UAAU,MAAM,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,MAAM,EAAE,SAAS,CAAC;GAC7E,IAAI,QAAQ,WAAW,GAAG,OAAO;GACjC,OAAO,QAAQ,KAAK,GAAG;EACzB;EACA,IAAI,OAAO,UAAU,UAAU,OAAO;CAExC;CAEA,MAAc,cAAc,WAAmB;EAM7C,QAAO,MALgB,KAAK,UAAU,CAAC,CAAC,KACtC,IAAI,iCAAiC,EACnC,uBAAuB,CAAC,SAAS,EACnC,CAAC,CACH,EACe,CAAC,oBAAoB;CACtC;;;;;;;;;;;;CAaA,MAAc,oCACZ,WACA,WACe;EACf,IAAI;EACJ,IAAI;GAEF,gBAAe,MADK,KAAK,cAAc,SAAS,EAC5B,EAAE,aAAa,CAAC,EAAC,CAClC,KAAK,MAAM,EAAE,UAAU,CAAC,CACxB,QAAQ,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;EACzE,SAAS,eAAe;GACtB,KAAK,OAAO,MACV,qDAAqD,UAAU,uCAAuC,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa,GACrL;GACA;EACF;EAEA,IAAI,YAAY,WAAW,GAAG;EAE9B,KAAK,OAAO,MACV,2CAA2C,YAAY,OAAO,mCAAmC,UAAU,qBAC7G;EACA,KAAK,MAAM,cAAc,aACvB,MAAM,8BAA8B,KAAK,aAAa,GAAG,YAAY,KAAK,MAAM;CAEpF;CAEA,MAAc,SAAS,WAAgD;EACrE,IAAI;GAEF,QAAO,MADa,KAAK,cAAc,SAAS,EACpC,EAAE;EAChB,SAAS,KAAK;GACZ,KAAK,OAAO,MACV,6BAA6B,UAAU,YAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GACpG;GACA;EACF;CACF;CAEA,AAAQ,gBAAgB,OAAyB;EAC/C,IAAI,EAAE,iBAAiB,QAAQ,OAAO;EACtC,MAAM,OAAQ,MAA4B,QAAQ;EAClD,MAAM,UAAU,MAAM,QAAQ,YAAY;EAG1C,OACE,SAAS,sBACR,QAAQ,SAAS,iCAAiC,KACjD,QAAQ,SAAS,WAAW,KAC5B,QAAQ,SAAS,gBAAgB;CAEvC;CAEA,MAAc,oBAAoB,WAAmB,YAAY,KAAwB;EACvF,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI,QAAQ;EAEZ,OAAO,KAAK,IAAI,IAAI,YAAY,WAAW;GACzC,IAAI;IAEF,IAAI,CAAC,MADe,KAAK,cAAc,SAAS,GACpC;GACd,SAAS,OAAO;IACd,IAAI,KAAK,gBAAgB,KAAK,GAAG;IACjC,MAAM;GACR;GAEA,MAAM,KAAK,MAAM,KAAK;GACtB,QAAQ,KAAK,IAAI,QAAQ,GAAG,GAAM;EACpC;EAEA,MAAM,IAAI,MACR,0CAA0C,UAAU,+BACtD;CACF;CAEA,AAAQ,MAAM,IAA2B;EACvC,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;CACzD;;;;;;;;;;;;;;;;CAyBA,MAAc,cAAc,YAAoB,MAAe,MAA8B;EAC3F,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAE/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EACnD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EACnD,MAAM,4BAAY,IAAI,IAAoB;EAC1C,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,KAAK,UAAU,IAAI,EAAE,KAAK,CAAC;EAEnC,MAAM,4BAAY,IAAI,IAAoB;EAC1C,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,KAAK,UAAU,IAAI,EAAE,KAAK,CAAC;EAGnC,MAAM,WAAqB,CAAC;EAC5B,KAAK,MAAM,CAAC,KAAK,QAAQ,WACvB,IAAI,CAAC,UAAU,IAAI,GAAG,GAAG,SAAS,KAAK,GAAG;EAE5C,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAIA,oBAAkB,EAOpB,MAAM,SAAS,KAAK,OAAO;GACzB,YAAY;GACZ,cAAc;GACd,KAAK,EAAE;EACT,EAAE,EACJ,CAAC,CACH;EAGF,MAAM,WAAqB,CAAC;EAC5B,KAAK,MAAM,CAAC,KAAK,QAAQ,WAAW;GAClC,MAAM,SAAS,UAAU,IAAI,GAAG;GAChC,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,UAAU,GAAG,GAAG;GACpD,SAAS,KAAK,GAAG;EACnB;EACA,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,0BAA0B,EAC5B,MAAM,SAAS,KAAK,OAAO;GACzB,YAAY;GACZ,cAAc;GACd,KAAK,EAAE;GACP,GAAI,EAAE,UAAU,UAAa,EAAE,OAAO,EAAE,MAAM;GAC9C,GAAI,EAAE,sBAAsB,UAAa,EACvC,mBAAmB,EAAE,kBACvB;EACF,EAAE,EACJ,CAAC,CACH;CAEJ;;;;;;;;CASA,MAAc,2BACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,aAAa,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,EAAC,CAAE,QACjD,MAAmB,OAAO,MAAM,QACnC;EACA,MAAM,aAAa,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,EAAC,CAAE,QACjD,MAAmB,OAAO,MAAM,QACnC;EACA,MAAM,UAAU,IAAI,IAAI,SAAS;EACjC,MAAM,UAAU,IAAI,IAAI,SAAS;EACjC,MAAM,WAAW,UAAU,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACxD,MAAM,WAAW,UAAU,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACxD,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,2BAA2B;GAC7B,sBAAsB;GACtB,mBAAmB;EACrB,CAAC,CACH;EAEF,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,2BAA2B;GAC7B,sBAAsB;GACtB,mBAAmB;EACrB,CAAC,CACH;CAEJ;;;;;;;;;CAUA,MAAc,yBACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,EAAC,CAAE,QAChD,MAAmB,OAAO,MAAM,QACnC;EACA,MAAM,YAAY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,EAAC,CAAE,QAChD,MAAmB,OAAO,MAAM,QACnC;EACA,MAAM,UAAU,IAAI,IAAI,QAAQ;EAChC,MAAM,UAAU,IAAI,IAAI,QAAQ;EAChC,MAAM,WAAW,SAAS,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACvD,MAAM,WAAW,SAAS,QAAQ,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;EACvD,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,sCAAsC;GACxC,sBAAsB;GACtB,iBAAiB;EACnB,CAAC,CACH;EAEF,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,sCAAsC;GACxC,sBAAsB;GACtB,iBAAiB;EACnB,CAAC,CACH;EAWF,IAAI,SAAS,SAAS,KAAK,SAAS,SAAS,GAC3C,MAAM,KAAK,kCAAkC,YAAY,IAAI,IAAI,QAAQ,CAAC;CAE9E;CAEA,OAAwB,4BAA4B;CACpD,OAAwB,kCAAkC;CAE1D,MAAc,kCACZ,YACA,UACe;EACf,MAAM,aAAa,KAAK,IAAI,IAAI,YAAY;EAC5C,IAAI,+BAA4B,IAAI,IAAI;EACxC,OAAO,KAAK,IAAI,IAAI,YAAY;GAC9B,IAAI;GACJ,IAAI;IACF,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,KAC5B,IAAI,iCAAiC,EAAE,uBAAuB,CAAC,UAAU,EAAE,CAAC,CAC9E;GACF,SAAS,KAAK;IAMZ,KAAK,OAAO,MACV,0EACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAEnD;IACA,MAAM,IAAI,SAAS,MAAM,WAAW,GAAG,YAAY,+BAA+B,CAAC;IACnF;GACF;GACA,eAAe,IAAI,IAAI,KAAK,oBAAoB,EAAE,EAAE,mBAAmB,CAAC,CAAC;GACzE,IAAI,aAAa,SAAS,SAAS,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,OAAO,MAAM,aAAa,IAAI,CAAC,CAAC,GACvF;GAEF,MAAM,IAAI,SAAS,MAAM,WAAW,GAAG,YAAY,+BAA+B,CAAC;EACrF;EAQA,MAAM,iBAAiB,CAAC,GAAG,QAAQ,CAAC,CAAC,KAAK;EAC1C,MAAM,iBAAiB,CAAC,GAAG,YAAY,CAAC,CAAC,KAAK;EAC9C,KAAK,OAAO,KACV,4DAA4D,YAAY,0BAA0B,aAAa,WAAW,aAAa,KAAK,UAAU,cAAc,EAAE,YAAY,KAAK,UAAU,cAAc,GACjN;CACF;CAEA,MAAc,2BACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,oCAAoB,IAAI,IAAkC;EAChE,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,aAAa,kBAAkB,IAAI,EAAE,aAAa,EAAE,OAAO;EAEnE,MAAM,oCAAoB,IAAI,IAAkC;EAChE,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,aAAa,kBAAkB,IAAI,EAAE,aAAa,EAAE,OAAO;EAMnE,KAAK,MAAM,CAAC,aAAa,YAAY,mBACnC,IAAI,CAAC,kBAAkB,IAAI,WAAW,GACpC,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,gCAAgC;GAClC,sBAAsB;GACtB,GAAI,WAAW,QAAQ,SAAS,IAAI,EAAE,SAAS,QAAQ,IAAI,CAAC;EAC9D,CAAC,CACH;EAGJ,KAAK,MAAM,CAAC,aAAa,YAAY,mBAAmB;GACtD,MAAM,SAAS,kBAAkB,IAAI,WAAW;GAChD,IAAI,KAAK,UAAU,UAAU,IAAI,MAAM,KAAK,UAAU,WAAW,IAAI,GAAG;GAKxE,IAAI,UAAU,OAAO,SAAS,GAAG;IAC/B,MAAM,UAAU,UAAU,OAAO,QAAQ,MAAM,CAAC,QAAQ,SAAS,CAAC,CAAC,IAAI,CAAC;IACxE,IAAI,QAAQ,SAAS,GACnB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,gCAAgC;KAClC,sBAAsB;KACtB,SAAS;IACX,CAAC,CACH;GAEJ;GACA,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,+BAA+B;IACjC,sBAAsB;IACtB,aAAa;IACb,GAAI,WAAW,QAAQ,SAAS,IAAI,EAAE,SAAS,QAAQ,IAAI,CAAC;GAC9D,CAAC,CACH;EACF;CACF;CAEA,MAAc,wBACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EASnD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAGnD,MAAM,YAAY,IAAI,IACpB,YAAY,KAAK,MAAM,EAAE,iBAAiB,CAAC,CAAC,QAAQ,MAAmB,CAAC,CAAC,CAAC,CAC5E;EAEA,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,qBAAqB,CAAC,UAAU,IAAI,EAAE,iBAAiB,GAC3D,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,2BAA2B;GAC7B,sBAAsB;GACtB,mBAAmB,EAAE;EACvB,CAAC,CACH;EAKJ,MAAM,6BAAa,IAAI,IAAqB;EAC5C,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,mBAAmB,WAAW,IAAI,EAAE,mBAAmB,CAAC;EAEhE,KAAK,MAAM,KAAK,aAAa;GAC3B,IAAI,CAAC,EAAE,mBAAmB;GAC1B,MAAM,WAAW,WAAW,IAAI,EAAE,iBAAiB;GACnD,IAAI,KAAK,UAAU,QAAQ,MAAM,KAAK,UAAU,CAAC,GAAG;GACpD,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,wBAAwB;IAC1B,sBAAsB;IACtB,mBAAmB,EAAE;IACrB,GAAI,EAAE,wBAAwB,UAAa,EACzC,qBAAqB,EAAE,oBACzB;IACA,GAAI,EAAE,YAAY,UAAa,EAAE,SAAS,EAAE,QAAQ;IACpD,GAAI,EAAE,0BAA0B,UAAa,EAC3C,uBAAuB,EAAE,sBAC3B;IACA,GAAI,EAAE,yBAAyB,UAAa,EAC1C,sBAAsB,EAAE,qBAC1B;IACA,GAAI,EAAE,qBAAqB,UAAa,EAAE,kBAAkB,EAAE,iBAAiB;IAC/E,GAAI,EAAE,kBAAkB,UAAa,EAAE,eAAe,EAAE,cAAc;GACxE,CAAC,CACH;EACF;CACF;CAEA,MAAc,wBACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAC/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,UAAU,IAAI,IAAI,YAAY,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC,QAAQ,MAAmB,CAAC,CAAC,CAAC,CAAC;EAC5F,MAAM,UAAU,IAAI,IAAI,YAAY,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC,QAAQ,MAAmB,CAAC,CAAC,CAAC,CAAC;EAC5F,MAAM,WAAW,YAAY,QAAQ,MAAM,EAAE,cAAc,CAAC,QAAQ,IAAI,EAAE,UAAU,CAAC;EACrF,MAAM,WAAW,YAAY,QAAQ,MAAM,EAAE,cAAc,CAAC,QAAQ,IAAI,EAAE,UAAU,CAAC;EACrF,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,4BAA4B;GAC9B,sBAAsB;GACtB,gBAAgB,SAAS,KAAK,OAAO;IACnC,YAAY,EAAE;IACd,GAAI,EAAE,SAAS,UAAa,EAAE,MAAM,EAAE,KAAK;GAC7C,EAAE;EACJ,CAAC,CACH;EAEF,IAAI,SAAS,SAAS,GACpB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,4BAA4B;GAC9B,sBAAsB;GACtB,gBAAgB,SAAS,KAAK,OAAO;IACnC,YAAY,EAAE;IACd,GAAI,EAAE,SAAS,UAAa,EAAE,MAAM,EAAE,KAAK;GAC7C,EAAE;EACJ,CAAC,CACH;CAEJ;CAEA,MAAc,oCACZ,YACA,MACA,MACe;EACf,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC,GAAG;EAO/D,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,cAAe,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;EAInD,MAAM,8BAAc,IAAI,IAAkC;EAC1D,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,UAAU,YAAY,IAAI,EAAE,UAAU,EAAE,iBAAiB;EAEjE,MAAM,8BAAc,IAAI,IAAkC;EAC1D,KAAK,MAAM,KAAK,aACd,IAAI,EAAE,UAAU,YAAY,IAAI,EAAE,UAAU,EAAE,iBAAiB;EAEjE,KAAK,MAAM,SAAS,YAAY,KAAK,GACnC,IAAI,CAAC,YAAY,IAAI,KAAK,GACxB,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,uCAAuC;GACzC,sBAAsB;GACtB,UAAU;EACZ,CAAC,CACH;EAGJ,KAAK,MAAM,CAAC,OAAO,UAAU,aAAa;GACxC,MAAM,SAAS,YAAY,IAAI,KAAK;GACpC,IAAI,KAAK,UAAU,UAAU,IAAI,MAAM,KAAK,UAAU,SAAS,IAAI,GAAG;GACtE,MAAM,KAAK,UAAU,CAAC,CAAC,KACrB,IAAI,oCAAoC;IACtC,sBAAsB;IACtB,UAAU;IACV,mBAAmB,SAAS,CAAC;GAC/B,CAAC,CACH;EACF;CACF;AACF;;;;;;;;;;;;AAeA,SAAS,uBACP,gBAGoD;CACpD,IAAI,CAAC,kBAAkB,eAAe,WAAW,GAAG,OAAO,CAAC;CAC5D,MAAM,gCAAgB,IAAI,IAAyB;CACnD,KAAK,MAAM,KAAK,gBAAgB;EAC9B,MAAM,IAAI,EAAE;EACZ,IAAI,CAAC,GAAG;EACR,IAAI,MAAM,cAAc,IAAI,CAAC;EAC7B,IAAI,CAAC,KAAK;GACR,sBAAM,IAAI,IAAI;GACd,cAAc,IAAI,GAAG,GAAG;EAC1B;EACA,IAAI,EAAE,QAAQ,IAAI,IAAI,EAAE,MAAM;CAChC;CACA,MAAM,SAA6D,CAAC;CAEpE,KAAK,MAAM,eAAe,MAAM,KAAK,cAAc,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;EACjE,MAAM,UAAU,MAAM,KAAK,cAAc,IAAI,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK;EACtE,OAAO,KACL,QAAQ,SAAS,IACb;GAAE,aAAa;GAAa,SAAS;EAAQ,IAC7C,EAAE,aAAa,YAAY,CACjC;CACF;CACA,OAAO;AACT;;;;;;;;;AAUA,SAAS,uBACP,OASgC;CAChC,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO,CAAC;CAC1C,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,KAAK,OAAO;EACrB,IAAI,CAAC,EAAE,mBAAmB;EAC1B,MAAM,QAAiC,EAAE,mBAAmB,EAAE,kBAAkB;EAChF,IAAI,EAAE,wBAAwB,QAAW,MAAM,yBAAyB,EAAE;EAC1E,IAAI,EAAE,YAAY,QAAW,MAAM,aAAa,EAAE;EAClD,IAAI,EAAE,0BAA0B,QAC9B,MAAM,2BAA2B,EAAE;EAErC,IAAI,EAAE,yBAAyB,QAC7B,MAAM,0BAA0B,EAAE;EAEpC,IAAI,EAAE,qBAAqB,QAAW,MAAM,sBAAsB,EAAE;EACpE,IAAI,EAAE,kBAAkB,QAAW,MAAM,mBAAmB,EAAE;EAC9D,OAAO,KAAK,KAAK;CACnB;CACA,OAAO,MAAM,GAAG,MACd,OAAO,EAAE,oBAAoB,CAAC,CAAC,cAAc,OAAO,EAAE,oBAAoB,CAAC,CAC7E;CACA,OAAO;AACT;;;;;;;AAQA,SAAS,uBACP,gBACgC;CAChC,IAAI,CAAC,kBAAkB,eAAe,WAAW,GAAG,OAAO,CAAC;CAC5D,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,KAAK,gBAAgB;EAC9B,IAAI,CAAC,EAAE,YAAY;EACnB,MAAM,QAAiC,EAAE,YAAY,EAAE,WAAW;EAClE,IAAI,EAAE,SAAS,QAAW,MAAM,UAAU,EAAE;EAC5C,OAAO,KAAK,KAAK;CACnB;CACA,OAAO,MAAM,GAAG,MAAM,OAAO,EAAE,aAAa,CAAC,CAAC,cAAc,OAAO,EAAE,aAAa,CAAC,CAAC;CACpF,OAAO;AACT;;;;;;;AAQA,SAAS,sBACP,gBACgC;CAChC,IAAI,CAAC,kBAAkB,eAAe,WAAW,GAAG,OAAO,CAAC;CAC5D,MAAM,0BAAU,IAAI,IAAyB;CAC7C,KAAK,MAAM,KAAK,gBAAgB;EAC9B,IAAI,CAAC,EAAE,UAAU;EACjB,IAAI,MAAM,QAAQ,IAAI,EAAE,QAAQ;EAChC,IAAI,CAAC,KAAK;GACR,sBAAM,IAAI,IAAI;GACd,QAAQ,IAAI,EAAE,UAAU,GAAG;EAC7B;EACA,IAAI,EAAE,kBAAkB,IAAI,IAAI,EAAE,gBAAgB;CACpD;CACA,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,SAAS,MAAM,KAAK,QAAQ,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;EACrD,MAAM,QAAQ,MAAM,KAAK,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK;EACxD,OAAO,KAAK;GAAE,UAAU;GAAO,mBAAmB;EAAM,CAAC;CAC3D;CACA,OAAO;AACT"}
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as createAssetRedirectResolver, $t as ResourceTimeoutError, A as CloudControlProvider, At as MIGRATE_TMP_PREFIX, B as assertRegionMatch, Bt as setAwsClients, C as green, Ct as resolveSkipPrefix, D as collectInlinePolicyNamesManagedBySiblings, Dt as warnDeprecatedNoPrefixCliFlag, E as IAMRoleProvider, Et as resolveUseCdkBootstrapAssets, F as cfnRefValueFromPhysicalId, G as LockManager, Gt as LocalInvokeBuildError$1, H as DiffCalculator, Ht as CdkdError, I as refStateLookupFromResource, It as resolveBucketRegion, J as shouldRetainResource, K as S3StateBackend, Kt as LocalMigrateError, L as WAFv2WebACLProvider, Lt as AwsClients, M as disableInstanceApiTermination, Mt as uploadCfnTemplate, N as isTerminationProtectionPropagationError, Nt as expectedOwnerParam, O as ProviderRegistry, Ot as CFN_TEMPLATE_BODY_LIMIT, P as IntrinsicFunctionResolver, Pt as AssemblyReader, Q as buildAssetRedirectMap, Qt as ProvisioningError, R as normalizeAwsTagsToCfn, Rt as getAwsClients, S as gray, St as resolveCaptureObservedState, T as yellow, Tt as resolveStateBucketWithDefaultAndSource, U as DagBuilder, V as applyRoleArnIfSet, W as TemplateParser, X as stringifyValue, Xt as NestedStackChildDirectDestroyError, Y as AssetPublisher, Yt as MissingCdkCliError, Z as WorkGraph, Zt as PartialFailureError, _ as isStatefulRecreateTargetSync, _t as synthesisStatusMessage, a as DeploymentEventsStore, at as getBootstrapMarkerKey, b as bold, bt as resolveApp, c as replayFailedOperations, cn as withErrorHandling, ct as validateContainerRepoName, d as withRetry, dt as getDockerCmd, en as ResourceUpdateNotSupportedError, et as loadPublishableAssetManifest, f as isRetryableTransientError, ft as runDockerForeground, g as MULTI_REGION_RECREATE_BLOCKED_TYPES, gt as Synthesizer, h as extractDeploymentEventError, ht as getDockerImageBySourceHash, i as DeploymentEventsReader, it as ensureAssetStorage, j as slowCcOperationTimeoutMs, jt as findLargeInlineResources, k as findActionableSilentDrops, kt as CFN_TEMPLATE_URL_LIMIT, l as replayRollback, lt as buildDockerImage, m as computeImplicitDeleteEdges, mt as AssetManifestLoader, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as StackTerminationProtectionError, nt as AssetModeResolver, o as planFailedOps, ot as parseBootstrapMarker, p as IMPLICIT_DELETE_DEPENDENCIES, pt as runDockerStreaming, q as rebuildClientForBucketRegion, qt as LocalStartServiceError, r as DeployEngine, rt as BOOTSTRAP_MARKER_PREFIX, s as planRollback, sn as normalizeAwsError, st as validateAssetBucketName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as StackHasActiveImportsError, tt as rewriteTemplateAssetReferences, u as withResourceDeadline, ut as formatDockerLoginError, v as renderStatefulReason, vt as getDefaultStateBucketName, w as red, wt as resolveStateBucketWithDefault, x as cyan, xt as resolveAutoAssetStorage, y as formatResourceLine, yt as getLegacyStateBucketName, z as resolveExplicitPhysicalId, zt as resetAwsClients } from "./deploy-engine-
|
|
2
|
+
import { $ as createAssetRedirectResolver, $t as ResourceTimeoutError, A as CloudControlProvider, At as MIGRATE_TMP_PREFIX, B as assertRegionMatch, Bt as setAwsClients, C as green, Ct as resolveSkipPrefix, D as collectInlinePolicyNamesManagedBySiblings, Dt as warnDeprecatedNoPrefixCliFlag, E as IAMRoleProvider, Et as resolveUseCdkBootstrapAssets, F as cfnRefValueFromPhysicalId, G as LockManager, Gt as LocalInvokeBuildError$1, H as DiffCalculator, Ht as CdkdError, I as refStateLookupFromResource, It as resolveBucketRegion, J as shouldRetainResource, K as S3StateBackend, Kt as LocalMigrateError, L as WAFv2WebACLProvider, Lt as AwsClients, M as disableInstanceApiTermination, Mt as uploadCfnTemplate, N as isTerminationProtectionPropagationError, Nt as expectedOwnerParam, O as ProviderRegistry, Ot as CFN_TEMPLATE_BODY_LIMIT, P as IntrinsicFunctionResolver, Pt as AssemblyReader, Q as buildAssetRedirectMap, Qt as ProvisioningError, R as normalizeAwsTagsToCfn, Rt as getAwsClients, S as gray, St as resolveCaptureObservedState, T as yellow, Tt as resolveStateBucketWithDefaultAndSource, U as DagBuilder, V as applyRoleArnIfSet, W as TemplateParser, X as stringifyValue, Xt as NestedStackChildDirectDestroyError, Y as AssetPublisher, Yt as MissingCdkCliError, Z as WorkGraph, Zt as PartialFailureError, _ as isStatefulRecreateTargetSync, _t as synthesisStatusMessage, a as DeploymentEventsStore, at as getBootstrapMarkerKey, b as bold, bt as resolveApp, c as replayFailedOperations, cn as withErrorHandling, ct as validateContainerRepoName, d as withRetry, dt as getDockerCmd, en as ResourceUpdateNotSupportedError, et as loadPublishableAssetManifest, f as isRetryableTransientError, ft as runDockerForeground, g as MULTI_REGION_RECREATE_BLOCKED_TYPES, gt as Synthesizer, h as extractDeploymentEventError, ht as getDockerImageBySourceHash, i as DeploymentEventsReader, it as ensureAssetStorage, j as slowCcOperationTimeoutMs, jt as findLargeInlineResources, k as findActionableSilentDrops, kt as CFN_TEMPLATE_URL_LIMIT, l as replayRollback, lt as buildDockerImage, m as computeImplicitDeleteEdges, mt as AssetManifestLoader, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as StackTerminationProtectionError, nt as AssetModeResolver, o as planFailedOps, ot as parseBootstrapMarker, p as IMPLICIT_DELETE_DEPENDENCIES, pt as runDockerStreaming, q as rebuildClientForBucketRegion, qt as LocalStartServiceError, r as DeployEngine, rt as BOOTSTRAP_MARKER_PREFIX, s as planRollback, sn as normalizeAwsError, st as validateAssetBucketName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as StackHasActiveImportsError, tt as rewriteTemplateAssetReferences, u as withResourceDeadline, ut as formatDockerLoginError, v as renderStatefulReason, vt as getDefaultStateBucketName, w as red, wt as resolveStateBucketWithDefault, x as cyan, xt as resolveAutoAssetStorage, y as formatResourceLine, yt as getLegacyStateBucketName, z as resolveExplicitPhysicalId, zt as resetAwsClients } from "./deploy-engine-BIwwg2DG.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-Cvnv7x_Q.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -61953,7 +61953,7 @@ function createMigrateCommand() {
|
|
|
61953
61953
|
*/
|
|
61954
61954
|
function buildProgram() {
|
|
61955
61955
|
const program = new Command();
|
|
61956
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
61956
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.265.0");
|
|
61957
61957
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
61958
61958
|
const { profile } = actionCommand.optsWithGlobals();
|
|
61959
61959
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|
|
@@ -11260,7 +11260,7 @@ var CloudControlProvider = class {
|
|
|
11260
11260
|
this.logger.debug(`Deleting resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`);
|
|
11261
11261
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
11262
11262
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
11263
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
11263
|
+
const { ASGProvider } = await import("./asg-provider-Cvnv7x_Q.js").then((n) => n.n);
|
|
11264
11264
|
await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
11265
11265
|
return;
|
|
11266
11266
|
}
|
|
@@ -17143,7 +17143,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
17143
17143
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
17144
17144
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
17145
17145
|
function getCdkdVersion() {
|
|
17146
|
-
return "0.
|
|
17146
|
+
return "0.265.0";
|
|
17147
17147
|
}
|
|
17148
17148
|
/**
|
|
17149
17149
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -17981,7 +17981,10 @@ var DeployEngine = class {
|
|
|
17981
17981
|
this.logger.debug(`Loaded current state: ${Object.keys(currentState.resources).length} resources`);
|
|
17982
17982
|
try {
|
|
17983
17983
|
const journal = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
|
|
17984
|
-
if (journal && journal.segments.length > 0)
|
|
17984
|
+
if (journal && journal.segments.length > 0) {
|
|
17985
|
+
const failedOnly = journal.segments.every((s) => s.operations.length === 0) && journal.segments.some((s) => (s.failedOperations?.length ?? 0) > 0);
|
|
17986
|
+
this.logger.info(failedOnly ? `A previous deploy of '${stackName}' failed and was automatically rolled back. The failed resource may be partially applied — run 'cdkd rollback ${stackName} --revert-failed' to revert it, or continue deploying to fix forward (a successful deploy clears this note).` : `A previous deploy of '${stackName}' failed or was interrupted. Run 'cdkd rollback ${stackName}' to revert it, or continue deploying to fix forward.`);
|
|
17987
|
+
}
|
|
17985
17988
|
} catch {}
|
|
17986
17989
|
this.kickOffAutoRefreshObservedProperties(currentState.resources);
|
|
17987
17990
|
this.logger.debug(`Template has ${Object.keys(template.Resources || {}).length} resources`);
|
|
@@ -18052,6 +18055,7 @@ var DeployEngine = class {
|
|
|
18052
18055
|
this.logger.warn(`Failed to persist no-change state update: ${saveError instanceof Error ? saveError.message : String(saveError)} — drift baseline / outputs will be re-resolved on next deploy.`);
|
|
18053
18056
|
}
|
|
18054
18057
|
}
|
|
18058
|
+
if (!this.options.dryRun) await this.deleteRollbackJournalBestEffort(stackName);
|
|
18055
18059
|
return {
|
|
18056
18060
|
stackName,
|
|
18057
18061
|
created: 0,
|
|
@@ -18317,7 +18321,7 @@ var DeployEngine = class {
|
|
|
18317
18321
|
};
|
|
18318
18322
|
await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(postRollbackState), { ...currentEtag !== void 0 && { expectedEtag: currentEtag } });
|
|
18319
18323
|
this.logger.debug("State saved after deployment failure");
|
|
18320
|
-
if (autoRollbackClean) await this.
|
|
18324
|
+
if (autoRollbackClean) await this.settleJournalAfterCleanRollback(stackName, failedOperations, initialDeploy);
|
|
18321
18325
|
} catch (saveError) {
|
|
18322
18326
|
this.logger.debug(`Retrying state save after rollback (ETag mismatch): ${saveError instanceof Error ? saveError.message : String(saveError)}`);
|
|
18323
18327
|
try {
|
|
@@ -18334,7 +18338,7 @@ var DeployEngine = class {
|
|
|
18334
18338
|
};
|
|
18335
18339
|
await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(postRollbackState), { ...freshEtag !== void 0 && { expectedEtag: freshEtag } });
|
|
18336
18340
|
this.logger.debug("State saved after deployment failure (retry succeeded)");
|
|
18337
|
-
if (autoRollbackClean) await this.
|
|
18341
|
+
if (autoRollbackClean) await this.settleJournalAfterCleanRollback(stackName, failedOperations, initialDeploy);
|
|
18338
18342
|
} catch (retryError) {
|
|
18339
18343
|
this.logger.warn(`Failed to save state after rollback: ${retryError instanceof Error ? retryError.message : String(retryError)}`);
|
|
18340
18344
|
}
|
|
@@ -18441,6 +18445,36 @@ var DeployEngine = class {
|
|
|
18441
18445
|
this.logger.debug(`Failed to delete rollback journal for ${stackName}: ${err instanceof Error ? err.message : String(err)}`);
|
|
18442
18446
|
}
|
|
18443
18447
|
}
|
|
18448
|
+
/**
|
|
18449
|
+
* Settle the rollback journal after a CLEAN automatic rollback (issue
|
|
18450
|
+
* #1208). The completed ops are reverted, but the op that FAILED mid-deploy
|
|
18451
|
+
* may have left its resource half-applied — and its journaled record is the
|
|
18452
|
+
* ONLY input `cdkd rollback --revert-failed` has. So instead of deleting the
|
|
18453
|
+
* journal outright (which made --revert-failed unusable in the DEFAULT
|
|
18454
|
+
* deploy flow), pop this attempt's segment and re-record a failed-only one
|
|
18455
|
+
* (`operations: []` + the failed ops). Older segments from prior
|
|
18456
|
+
* un-reverted attempts are preserved by the pop. The next successful deploy
|
|
18457
|
+
* still deletes the whole journal, bounding the lingering window. With no
|
|
18458
|
+
* failed ops there is nothing left to revert — delete as before.
|
|
18459
|
+
*
|
|
18460
|
+
* Best-effort like every journal write: a pop failure warns and leaves the
|
|
18461
|
+
* full segment in place (the pre-#1208 partial-rollback shape — replay is
|
|
18462
|
+
* idempotent, so a later `cdkd rollback` is still safe).
|
|
18463
|
+
*/
|
|
18464
|
+
async settleJournalAfterCleanRollback(stackName, failedOperations, initialDeploy) {
|
|
18465
|
+
if (failedOperations.length === 0) {
|
|
18466
|
+
await this.deleteRollbackJournalBestEffort(stackName);
|
|
18467
|
+
return;
|
|
18468
|
+
}
|
|
18469
|
+
try {
|
|
18470
|
+
await this.stateBackend.popRollbackJournalSegment(stackName, this.stackRegion);
|
|
18471
|
+
} catch (err) {
|
|
18472
|
+
this.logger.warn(`Failed to settle the rollback journal after the clean rollback: ${err instanceof Error ? err.message : String(err)}. The journal keeps the full segment; a later 'cdkd rollback' replay is idempotent.`);
|
|
18473
|
+
return;
|
|
18474
|
+
}
|
|
18475
|
+
await this.writeRollbackJournalSegment(stackName, [], failedOperations, "auto-rollback-clean", initialDeploy);
|
|
18476
|
+
this.logger.info(`The automatic rollback restored the pre-deploy state. The failed resource's pre-failure record was kept — if it was left partially applied, run 'cdkd rollback ${stackName} --revert-failed' to revert it.`);
|
|
18477
|
+
}
|
|
18444
18478
|
/** Build the {@link RollbackExecutorContext} from the engine's fields. */
|
|
18445
18479
|
rollbackExecutorContext() {
|
|
18446
18480
|
return {
|
|
@@ -19080,4 +19114,4 @@ var DeployEngine = class {
|
|
|
19080
19114
|
|
|
19081
19115
|
//#endregion
|
|
19082
19116
|
export { createAssetRedirectResolver as $, ResourceTimeoutError as $t, CloudControlProvider as A, MIGRATE_TMP_PREFIX as At, assertRegionMatch as B, setAwsClients as Bt, green as C, resolveSkipPrefix as Ct, collectInlinePolicyNamesManagedBySiblings as D, warnDeprecatedNoPrefixCliFlag as Dt, IAMRoleProvider as E, resolveUseCdkBootstrapAssets as Et, cfnRefValueFromPhysicalId as F, clearBucketRegionCache as Ft, LockManager as G, LocalInvokeBuildError as Gt, DiffCalculator as H, CdkdError as Ht, refStateLookupFromResource as I, resolveBucketRegion as It, shouldRetainResource as J, LockError as Jt, S3StateBackend as K, LocalMigrateError as Kt, WAFv2WebACLProvider as L, AwsClients as Lt, disableInstanceApiTermination as M, uploadCfnTemplate as Mt, isTerminationProtectionPropagationError as N, expectedOwnerParam as Nt, ProviderRegistry as O, CFN_TEMPLATE_BODY_LIMIT as Ot, IntrinsicFunctionResolver as P, AssemblyReader as Pt, buildAssetRedirectMap as Q, ProvisioningError as Qt, normalizeAwsTagsToCfn as R, getAwsClients as Rt, gray as S, resolveCaptureObservedState as St, yellow as T, resolveStateBucketWithDefaultAndSource as Tt, DagBuilder as U, ConfigError as Ut, applyRoleArnIfSet as V, AssetError as Vt, TemplateParser as W, DependencyError as Wt, stringifyValue as X, NestedStackChildDirectDestroyError as Xt, AssetPublisher as Y, MissingCdkCliError as Yt, WorkGraph as Z, PartialFailureError as Zt, isStatefulRecreateTargetSync as _, synthesisStatusMessage as _t, DeploymentEventsStore as a, formatError as an, getBootstrapMarkerKey as at, bold as b, resolveApp as bt, replayFailedOperations as c, withErrorHandling as cn, validateContainerRepoName as ct, withRetry as d, getDockerCmd as dt, ResourceUpdateNotSupportedError as en, loadPublishableAssetManifest as et, isRetryableTransientError as f, runDockerForeground as ft, MULTI_REGION_RECREATE_BLOCKED_TYPES as g, Synthesizer as gt, extractDeploymentEventError as h, getDockerImageBySourceHash as ht, DeploymentEventsReader as i, SynthesisError as in, ensureAssetStorage as it, slowCcOperationTimeoutMs as j, findLargeInlineResources as jt, findActionableSilentDrops as k, CFN_TEMPLATE_URL_LIMIT as kt, replayRollback as l, __exportAll as ln, buildDockerImage as lt, computeImplicitDeleteEdges as m, AssetManifestLoader as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, StackTerminationProtectionError as nn, AssetModeResolver as nt, planFailedOps as o, isCdkdError as on, parseBootstrapMarker as ot, IMPLICIT_DELETE_DEPENDENCIES as p, runDockerStreaming as pt, rebuildClientForBucketRegion as q, LocalStartServiceError as qt, DeployEngine as r, StateError as rn, BOOTSTRAP_MARKER_PREFIX as rt, planRollback as s, normalizeAwsError as sn, validateAssetBucketName as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, StackHasActiveImportsError as tn, rewriteTemplateAssetReferences as tt, withResourceDeadline as u, formatDockerLoginError as ut, renderStatefulReason as v, getDefaultStateBucketName as vt, red as w, resolveStateBucketWithDefault as wt, cyan as x, resolveAutoAssetStorage as xt, formatResourceLine as y, getLegacyStateBucketName as yt, resolveExplicitPhysicalId as z, resetAwsClients as zt };
|
|
19083
|
-
//# sourceMappingURL=deploy-engine-
|
|
19117
|
+
//# sourceMappingURL=deploy-engine-BIwwg2DG.js.map
|