@go-to-k/cdkd 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3205,6 +3205,14 @@ import {
3205
3205
  ListObjectsV2Command,
3206
3206
  NoSuchKey
3207
3207
  } from "@aws-sdk/client-s3";
3208
+
3209
+ // src/types/state.ts
3210
+ var STATE_SCHEMA_VERSION_LEGACY = 1;
3211
+ var STATE_SCHEMA_VERSION_CURRENT = 2;
3212
+
3213
+ // src/state/s3-state-backend.ts
3214
+ var LEGACY_KEY_DEPTH = 2;
3215
+ var NEW_KEY_DEPTH = 3;
3208
3216
  var S3StateBackend = class {
3209
3217
  constructor(s3Client, config) {
3210
3218
  this.s3Client = s3Client;
@@ -3212,9 +3220,16 @@ var S3StateBackend = class {
3212
3220
  }
3213
3221
  logger = getLogger().child("S3StateBackend");
3214
3222
  /**
3215
- * Get the S3 key for a stack's state file
3223
+ * Get the new (region-scoped) S3 key for a stack's state file.
3216
3224
  */
3217
- getStateKey(stackName) {
3225
+ getStateKey(stackName, region) {
3226
+ return `${this.config.prefix}/${stackName}/${region}/state.json`;
3227
+ }
3228
+ /**
3229
+ * Get the legacy (pre-region-prefix) S3 key for a stack's state file.
3230
+ * Used for backwards-compatible reads and for the migration delete.
3231
+ */
3232
+ getLegacyStateKey(stackName) {
3218
3233
  return `${this.config.prefix}/${stackName}/state.json`;
3219
3234
  }
3220
3235
  /**
@@ -3240,101 +3255,143 @@ var S3StateBackend = class {
3240
3255
  }
3241
3256
  }
3242
3257
  /**
3243
- * Check if state exists for a stack
3244
- */
3245
- async stateExists(stackName) {
3246
- const key = this.getStateKey(stackName);
3247
- try {
3248
- await this.s3Client.send(
3249
- new HeadObjectCommand2({
3250
- Bucket: this.config.bucket,
3251
- Key: key
3252
- })
3253
- );
3258
+ * Check if state exists for a stack in the given region.
3259
+ *
3260
+ * Returns true for either layout: the new region-scoped key, or the legacy
3261
+ * key when its embedded `region` matches the requested region. This lets
3262
+ * `cdkd state rm <stack> --region X` and `cdkd destroy <stack>` see legacy
3263
+ * state without forcing a write-through migration first.
3264
+ */
3265
+ async stateExists(stackName, region) {
3266
+ const newKey = this.getStateKey(stackName, region);
3267
+ if (await this.headObject(newKey)) {
3254
3268
  return true;
3255
- } catch (error) {
3256
- if (error instanceof NoSuchKey || error.name === "NotFound") {
3257
- return false;
3258
- }
3259
- throw new StateError(
3260
- `Failed to check if state exists for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,
3261
- error instanceof Error ? error : void 0
3262
- );
3263
3269
  }
3270
+ return this.legacyMatchesRegion(stackName, region);
3264
3271
  }
3265
3272
  /**
3266
- * Get state for a stack
3273
+ * Get state for a stack, transparently falling back to the legacy key.
3274
+ *
3275
+ * Lookup order:
3276
+ * 1. `{prefix}/{stackName}/{region}/state.json` (current `version: 2` key).
3277
+ * 2. `{prefix}/{stackName}/state.json` (legacy `version: 1` key) — only
3278
+ * accepted if its embedded `region` matches the requested region.
3267
3279
  *
3268
- * Note: S3 returns ETag with surrounding quotes (e.g., "abc123").
3269
- * We preserve the quotes as they are required for IfMatch conditions.
3280
+ * When a legacy hit is returned, `migrationPending` is `true`. Callers that
3281
+ * subsequently `saveState` automatically migrate by writing the new key and
3282
+ * deleting the legacy one (see `saveState`'s `legacyMigration` argument).
3283
+ *
3284
+ * Note: S3 returns ETag with surrounding quotes (e.g., `"abc123"`). We
3285
+ * preserve the quotes — they are required for `IfMatch` conditions.
3270
3286
  */
3271
- async getState(stackName) {
3272
- const key = this.getStateKey(stackName);
3287
+ async getState(stackName, region) {
3288
+ const newKey = this.getStateKey(stackName, region);
3273
3289
  try {
3274
- this.logger.debug(`Getting state for stack: ${stackName}`);
3290
+ this.logger.debug(`Getting state for stack: ${stackName} (${region})`);
3275
3291
  const response = await this.s3Client.send(
3276
3292
  new GetObjectCommand({
3277
3293
  Bucket: this.config.bucket,
3278
- Key: key
3294
+ Key: newKey
3279
3295
  })
3280
3296
  );
3281
3297
  if (!response.Body) {
3282
- throw new StateError(`State file for stack '${stackName}' has no body`);
3298
+ throw new StateError(`State file for stack '${stackName}' (${region}) has no body`);
3283
3299
  }
3284
3300
  if (!response.ETag) {
3285
- throw new StateError(`State file for stack '${stackName}' has no ETag`);
3301
+ throw new StateError(`State file for stack '${stackName}' (${region}) has no ETag`);
3286
3302
  }
3287
3303
  const bodyString = await response.Body.transformToString();
3288
- const state = JSON.parse(bodyString);
3289
- this.logger.debug(`Retrieved state for stack: ${stackName}, ETag: ${response.ETag}`);
3290
- return {
3291
- state,
3292
- etag: response.ETag
3293
- };
3304
+ const state = this.parseStateBody(bodyString, stackName);
3305
+ this.logger.debug(`Retrieved state: ${stackName} (${region}), ETag: ${response.ETag}`);
3306
+ return { state, etag: response.ETag };
3294
3307
  } catch (error) {
3295
- if (error instanceof NoSuchKey || error.name === "NoSuchKey") {
3296
- this.logger.debug(`No existing state for stack: ${stackName}`);
3297
- return null;
3298
- }
3299
- if (error instanceof StateError) {
3300
- throw error;
3308
+ if (!isNoSuchKey(error)) {
3309
+ if (error instanceof StateError)
3310
+ throw error;
3311
+ throw new StateError(
3312
+ `Failed to get state for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`,
3313
+ error instanceof Error ? error : void 0
3314
+ );
3301
3315
  }
3302
- throw new StateError(
3303
- `Failed to get state for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,
3304
- error instanceof Error ? error : void 0
3316
+ this.logger.debug(`No state at new key for stack: ${stackName} (${region})`);
3317
+ }
3318
+ const legacy = await this.tryGetLegacy(stackName, region);
3319
+ if (legacy) {
3320
+ this.logger.warn(
3321
+ `Loaded legacy state for stack '${stackName}' from '${this.getLegacyStateKey(stackName)}'. It will be migrated to the region-scoped layout on next save.`
3305
3322
  );
3323
+ return { ...legacy, migrationPending: true };
3306
3324
  }
3325
+ return null;
3307
3326
  }
3308
3327
  /**
3309
- * Save state for a stack with optimistic locking
3328
+ * Save state for a stack with optimistic locking.
3329
+ *
3330
+ * Always writes to the new region-scoped key. The state body is rewritten
3331
+ * with `version: 2` and the supplied region.
3332
+ *
3333
+ * If the caller observed `migrationPending: true` from `getState`, it
3334
+ * should pass the legacy ETag back via `expectedEtag` AND set
3335
+ * `migrateLegacy: true`. After the new key is written successfully, the
3336
+ * legacy key is deleted to complete migration. The legacy delete is a
3337
+ * best-effort follow-up — a failure is logged but does not unwind the new
3338
+ * write.
3310
3339
  *
3311
3340
  * @param stackName Stack name
3341
+ * @param region Target region (load-bearing — part of the S3 key)
3312
3342
  * @param state State to save
3313
- * @param expectedEtag Expected ETag for optimistic locking (optional for new state).
3314
- * Must include quotes if provided (e.g., "abc123")
3315
- * @returns New ETag (with quotes, e.g., "abc123")
3316
- */
3317
- async saveState(stackName, state, expectedEtag) {
3318
- const key = this.getStateKey(stackName);
3343
+ * @param options Optimistic-lock ETag + legacy-migration flag
3344
+ * @returns New ETag (with quotes, e.g., `"abc123"`)
3345
+ */
3346
+ async saveState(stackName, region, state, options = {}) {
3347
+ const newKey = this.getStateKey(stackName, region);
3348
+ const { expectedEtag, migrateLegacy } = options;
3349
+ const body = {
3350
+ ...state,
3351
+ version: STATE_SCHEMA_VERSION_CURRENT,
3352
+ stackName,
3353
+ region
3354
+ };
3319
3355
  try {
3320
3356
  this.logger.debug(
3321
- `Saving state for stack: ${stackName}${expectedEtag ? `, expected ETag: ${expectedEtag}` : ""}`
3357
+ `Saving state: ${stackName} (${region})${expectedEtag ? `, expected ETag: ${expectedEtag}` : ""}`
3322
3358
  );
3323
- const body = JSON.stringify(state, null, 2);
3359
+ const bodyString = JSON.stringify(body, null, 2);
3324
3360
  const response = await this.s3Client.send(
3325
3361
  new PutObjectCommand2({
3326
3362
  Bucket: this.config.bucket,
3327
- Key: key,
3328
- Body: body,
3329
- ContentLength: Buffer.byteLength(body),
3363
+ Key: newKey,
3364
+ Body: bodyString,
3365
+ ContentLength: Buffer.byteLength(bodyString),
3330
3366
  ContentType: "application/json",
3331
- ...expectedEtag && { IfMatch: expectedEtag }
3367
+ // The legacy ETag is for a different key; only forward it when we're
3368
+ // updating in-place at the new key.
3369
+ ...!migrateLegacy && expectedEtag && { IfMatch: expectedEtag }
3332
3370
  })
3333
3371
  );
3334
3372
  if (!response.ETag) {
3335
- throw new StateError(`No ETag returned after saving state for stack '${stackName}'`);
3373
+ throw new StateError(
3374
+ `No ETag returned after saving state for stack '${stackName}' (${region})`
3375
+ );
3376
+ }
3377
+ this.logger.debug(`State saved: ${stackName} (${region}), new ETag: ${response.ETag}`);
3378
+ if (migrateLegacy) {
3379
+ try {
3380
+ await this.s3Client.send(
3381
+ new DeleteObjectCommand({
3382
+ Bucket: this.config.bucket,
3383
+ Key: this.getLegacyStateKey(stackName)
3384
+ })
3385
+ );
3386
+ this.logger.info(
3387
+ `Migrated state for stack '${stackName}' to region-scoped layout (${region})`
3388
+ );
3389
+ } catch (deleteError) {
3390
+ this.logger.warn(
3391
+ `Migrated stack '${stackName}' to new key, but failed to delete legacy key: ${deleteError instanceof Error ? deleteError.message : String(deleteError)}`
3392
+ );
3393
+ }
3336
3394
  }
3337
- this.logger.debug(`State saved for stack: ${stackName}, new ETag: ${response.ETag}`);
3338
3395
  return response.ETag;
3339
3396
  } catch (error) {
3340
3397
  if (error.name === "PreconditionFailed") {
@@ -3343,63 +3400,230 @@ var S3StateBackend = class {
3343
3400
  );
3344
3401
  }
3345
3402
  throw new StateError(
3346
- `Failed to save state for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,
3403
+ `Failed to save state for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`,
3347
3404
  error instanceof Error ? error : void 0
3348
3405
  );
3349
3406
  }
3350
3407
  }
3351
3408
  /**
3352
- * Delete state for a stack
3409
+ * Delete state for a stack in the given region.
3410
+ *
3411
+ * Removes both the new key and the legacy key (if present). Legacy removal
3412
+ * is region-conditional: a legacy state file with a different `region`
3413
+ * field is left alone.
3353
3414
  */
3354
- async deleteState(stackName) {
3355
- const key = this.getStateKey(stackName);
3415
+ async deleteState(stackName, region) {
3356
3416
  try {
3357
- this.logger.debug(`Deleting state for stack: ${stackName}`);
3417
+ this.logger.debug(`Deleting state: ${stackName} (${region})`);
3358
3418
  await this.s3Client.send(
3359
3419
  new DeleteObjectCommand({
3360
3420
  Bucket: this.config.bucket,
3361
- Key: key
3421
+ Key: this.getStateKey(stackName, region)
3362
3422
  })
3363
3423
  );
3364
- this.logger.debug(`State deleted for stack: ${stackName}`);
3424
+ if (await this.legacyMatchesRegion(stackName, region)) {
3425
+ await this.s3Client.send(
3426
+ new DeleteObjectCommand({
3427
+ Bucket: this.config.bucket,
3428
+ Key: this.getLegacyStateKey(stackName)
3429
+ })
3430
+ );
3431
+ this.logger.debug(`Deleted legacy state for stack: ${stackName}`);
3432
+ }
3433
+ this.logger.debug(`State deleted: ${stackName} (${region})`);
3365
3434
  } catch (error) {
3366
3435
  throw new StateError(
3367
- `Failed to delete state for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,
3436
+ `Failed to delete state for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`,
3368
3437
  error instanceof Error ? error : void 0
3369
3438
  );
3370
3439
  }
3371
3440
  }
3372
3441
  /**
3373
- * List all stacks with state
3442
+ * List all stacks with state in the bucket.
3443
+ *
3444
+ * Returns one `{stackName, region}` pair per state file. Both layouts
3445
+ * are enumerated:
3446
+ *
3447
+ * - `{prefix}/{stackName}/{region}/state.json` (new) — `region` is the
3448
+ * path segment.
3449
+ * - `{prefix}/{stackName}/state.json` (legacy) — `region` is read from the
3450
+ * state body when present, otherwise `undefined`.
3451
+ *
3452
+ * Pairs are deduplicated by `(stackName, region)` so a stack mid-migration
3453
+ * shows up exactly once.
3374
3454
  */
3375
3455
  async listStacks() {
3376
3456
  try {
3377
3457
  this.logger.debug("Listing all stacks");
3458
+ const prefix = `${this.config.prefix}/`;
3459
+ const refs = [];
3460
+ const seen = /* @__PURE__ */ new Set();
3461
+ let continuationToken;
3462
+ do {
3463
+ const response = await this.s3Client.send(
3464
+ new ListObjectsV2Command({
3465
+ Bucket: this.config.bucket,
3466
+ Prefix: prefix,
3467
+ ...continuationToken && { ContinuationToken: continuationToken }
3468
+ })
3469
+ );
3470
+ for (const obj of response.Contents ?? []) {
3471
+ const key = obj.Key;
3472
+ if (!key)
3473
+ continue;
3474
+ if (!key.endsWith("/state.json"))
3475
+ continue;
3476
+ const rest = key.slice(prefix.length);
3477
+ const segments = rest.split("/");
3478
+ if (segments.length === NEW_KEY_DEPTH) {
3479
+ const [stackName, region] = segments;
3480
+ if (!stackName || !region)
3481
+ continue;
3482
+ const dedupeKey = `${stackName}\0${region}`;
3483
+ if (!seen.has(dedupeKey)) {
3484
+ seen.add(dedupeKey);
3485
+ refs.push({ stackName, region });
3486
+ }
3487
+ continue;
3488
+ }
3489
+ if (segments.length === LEGACY_KEY_DEPTH) {
3490
+ const [stackName] = segments;
3491
+ if (!stackName)
3492
+ continue;
3493
+ const region = await this.readLegacyRegion(stackName);
3494
+ const dedupeKey = `${stackName}\0${region ?? ""}`;
3495
+ if (!seen.has(dedupeKey)) {
3496
+ seen.add(dedupeKey);
3497
+ refs.push({ stackName, ...region ? { region } : {} });
3498
+ }
3499
+ }
3500
+ }
3501
+ continuationToken = response.IsTruncated ? response.NextContinuationToken : void 0;
3502
+ } while (continuationToken);
3503
+ this.logger.debug(`Found ${refs.length} stack(s) across regions`);
3504
+ return refs;
3505
+ } catch (error) {
3506
+ throw new StateError(
3507
+ `Failed to list stacks: ${error instanceof Error ? error.message : String(error)}`,
3508
+ error instanceof Error ? error : void 0
3509
+ );
3510
+ }
3511
+ }
3512
+ /**
3513
+ * HeadObject probe — returns true on 200, false on NotFound. Other errors
3514
+ * propagate so we don't accidentally swallow IAM denials.
3515
+ */
3516
+ async headObject(key) {
3517
+ try {
3518
+ await this.s3Client.send(
3519
+ new HeadObjectCommand2({
3520
+ Bucket: this.config.bucket,
3521
+ Key: key
3522
+ })
3523
+ );
3524
+ return true;
3525
+ } catch (error) {
3526
+ if (isNoSuchKey(error) || error.name === "NotFound") {
3527
+ return false;
3528
+ }
3529
+ throw error;
3530
+ }
3531
+ }
3532
+ /**
3533
+ * Read the legacy state's `region` field. Used for region matching during
3534
+ * `stateExists` / `deleteState` and for assigning a region to legacy
3535
+ * entries during `listStacks`.
3536
+ */
3537
+ async readLegacyRegion(stackName) {
3538
+ try {
3378
3539
  const response = await this.s3Client.send(
3379
- new ListObjectsV2Command({
3540
+ new GetObjectCommand({
3380
3541
  Bucket: this.config.bucket,
3381
- Prefix: `${this.config.prefix}/`,
3382
- Delimiter: "/"
3542
+ Key: this.getLegacyStateKey(stackName)
3383
3543
  })
3384
3544
  );
3385
- if (!response.CommonPrefixes) {
3386
- return [];
3545
+ if (!response.Body)
3546
+ return void 0;
3547
+ const bodyString = await response.Body.transformToString();
3548
+ const state = JSON.parse(bodyString);
3549
+ return typeof state.region === "string" ? state.region : void 0;
3550
+ } catch (error) {
3551
+ if (isNoSuchKey(error))
3552
+ return void 0;
3553
+ this.logger.debug(
3554
+ `Could not read legacy state region for '${stackName}': ${error instanceof Error ? error.message : String(error)}`
3555
+ );
3556
+ return void 0;
3557
+ }
3558
+ }
3559
+ async legacyMatchesRegion(stackName, region) {
3560
+ const legacyRegion = await this.readLegacyRegion(stackName);
3561
+ return legacyRegion === region;
3562
+ }
3563
+ /**
3564
+ * Try to read the legacy `version: 1` state. Returns null when the legacy
3565
+ * key is missing or its embedded region does not match the caller's region.
3566
+ */
3567
+ async tryGetLegacy(stackName, region) {
3568
+ try {
3569
+ const response = await this.s3Client.send(
3570
+ new GetObjectCommand({
3571
+ Bucket: this.config.bucket,
3572
+ Key: this.getLegacyStateKey(stackName)
3573
+ })
3574
+ );
3575
+ if (!response.Body || !response.ETag) {
3576
+ return null;
3577
+ }
3578
+ const bodyString = await response.Body.transformToString();
3579
+ const state = this.parseStateBody(bodyString, stackName);
3580
+ if (state.region && state.region !== region) {
3581
+ this.logger.debug(
3582
+ `Legacy state for stack '${stackName}' has region '${state.region}', not '${region}' \u2014 skipping legacy fallback.`
3583
+ );
3584
+ return null;
3387
3585
  }
3388
- const stackNames = response.CommonPrefixes.map((prefix) => {
3389
- const prefixStr = prefix.Prefix || "";
3390
- const parts = prefixStr.split("/");
3391
- return parts[parts.length - 2];
3392
- }).filter((name) => Boolean(name));
3393
- this.logger.debug(`Found ${stackNames.length} stacks`);
3394
- return stackNames;
3586
+ return { state, etag: response.ETag };
3395
3587
  } catch (error) {
3588
+ if (isNoSuchKey(error))
3589
+ return null;
3396
3590
  throw new StateError(
3397
- `Failed to list stacks: ${error instanceof Error ? error.message : String(error)}`,
3591
+ `Failed to get legacy state for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,
3592
+ error instanceof Error ? error : void 0
3593
+ );
3594
+ }
3595
+ }
3596
+ /**
3597
+ * Parse a state body and validate the schema version. Future-proofs against
3598
+ * a binary that predates schema version `N` reading a `version: N+1` blob:
3599
+ * the old binary would otherwise treat unknown fields as defaults and
3600
+ * silently lose data on the next save.
3601
+ */
3602
+ parseStateBody(bodyString, stackName) {
3603
+ let parsed;
3604
+ try {
3605
+ parsed = JSON.parse(bodyString);
3606
+ } catch (error) {
3607
+ throw new StateError(
3608
+ `State file for stack '${stackName}' is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
3398
3609
  error instanceof Error ? error : void 0
3399
3610
  );
3400
3611
  }
3612
+ const v = parsed.version;
3613
+ if (v !== STATE_SCHEMA_VERSION_LEGACY && v !== STATE_SCHEMA_VERSION_CURRENT && v !== void 0) {
3614
+ throw new StateError(
3615
+ `Unsupported state schema version ${String(v)} for stack '${stackName}'. This cdkd binary supports versions ${String(STATE_SCHEMA_VERSION_LEGACY)} and ${String(STATE_SCHEMA_VERSION_CURRENT)}. Upgrade cdkd to a version that supports schema ${String(v)}.`
3616
+ );
3617
+ }
3618
+ return parsed;
3401
3619
  }
3402
3620
  };
3621
+ function isNoSuchKey(error) {
3622
+ if (error instanceof NoSuchKey)
3623
+ return true;
3624
+ const name = error?.name;
3625
+ return name === "NoSuchKey";
3626
+ }
3403
3627
 
3404
3628
  // src/state/lock-manager.ts
3405
3629
  import {
@@ -3420,10 +3644,24 @@ var LockManager = class {
3420
3644
  logger = getLogger().child("LockManager");
3421
3645
  ttlMs;
3422
3646
  /**
3423
- * Get the S3 key for a stack's lock file
3647
+ * Get the S3 key for a stack's lock file.
3648
+ *
3649
+ * Locks are region-scoped, mirroring the state key layout
3650
+ * (`{prefix}/{stackName}/{region}/lock.json`). Two regions of the same
3651
+ * stackName can therefore be operated on in parallel without contention,
3652
+ * matching cdkd's parallel execution model.
3653
+ *
3654
+ * The `region` argument is required for new callers; for backwards
3655
+ * compatibility with `state list --long` (which only sees stack names),
3656
+ * passing `undefined` falls back to the legacy `{prefix}/{stackName}/lock.json`
3657
+ * key — that mode is purely for legacy lock cleanup and is NOT used by
3658
+ * deploy / destroy / diff anymore.
3424
3659
  */
3425
- getLockKey(stackName) {
3426
- return `${this.config.prefix}/${stackName}/lock.json`;
3660
+ getLockKey(stackName, region) {
3661
+ if (region === void 0) {
3662
+ return `${this.config.prefix}/${stackName}/lock.json`;
3663
+ }
3664
+ return `${this.config.prefix}/${stackName}/${region}/lock.json`;
3427
3665
  }
3428
3666
  /**
3429
3667
  * Get default lock owner identifier
@@ -3462,11 +3700,12 @@ var LockManager = class {
3462
3700
  * If an expired lock exists, it will be cleaned up and re-acquired.
3463
3701
  *
3464
3702
  * @param stackName Stack name
3703
+ * @param region Target region (lock key is region-scoped)
3465
3704
  * @param owner Lock owner identifier (defaults to user@hostname:pid)
3466
3705
  * @param operation Operation being performed (e.g., "deploy", "destroy")
3467
3706
  */
3468
- async acquireLock(stackName, owner, operation) {
3469
- const key = this.getLockKey(stackName);
3707
+ async acquireLock(stackName, region, owner, operation) {
3708
+ const key = this.getLockKey(stackName, region);
3470
3709
  const lockOwner = owner || this.getDefaultOwner();
3471
3710
  const now = Date.now();
3472
3711
  const lockInfo = {
@@ -3476,7 +3715,7 @@ var LockManager = class {
3476
3715
  ...operation && { operation }
3477
3716
  };
3478
3717
  try {
3479
- this.logger.debug(`Attempting to acquire lock for stack: ${stackName}`);
3718
+ this.logger.debug(`Attempting to acquire lock for stack: ${stackName} (${region})`);
3480
3719
  const lockBody = JSON.stringify(lockInfo, null, 2);
3481
3720
  await this.s3Client.send(
3482
3721
  new PutObjectCommand3({
@@ -3489,17 +3728,17 @@ var LockManager = class {
3489
3728
  // Only succeed if object doesn't exist
3490
3729
  })
3491
3730
  );
3492
- this.logger.debug(`Lock acquired for stack: ${stackName}, owner: ${lockOwner}`);
3731
+ this.logger.debug(`Lock acquired for stack: ${stackName} (${region}), owner: ${lockOwner}`);
3493
3732
  return true;
3494
3733
  } catch (error) {
3495
3734
  if (error instanceof S3ServiceException && error.name === "PreconditionFailed") {
3496
- this.logger.debug(`Lock already exists for stack: ${stackName}`);
3497
- const existingLock = await this.getLockInfo(stackName);
3735
+ this.logger.debug(`Lock already exists for stack: ${stackName} (${region})`);
3736
+ const existingLock = await this.getLockInfo(stackName, region);
3498
3737
  if (existingLock && this.isLockExpired(existingLock)) {
3499
3738
  this.logger.info(
3500
- `Expired lock detected for stack: ${stackName} (owner: ${existingLock.owner}, expired ${this.formatDuration(now - existingLock.expiresAt)} ago). Cleaning up...`
3739
+ `Expired lock detected for stack: ${stackName} (${region}, owner: ${existingLock.owner}, expired ${this.formatDuration(now - existingLock.expiresAt)} ago). Cleaning up...`
3501
3740
  );
3502
- await this.deleteLock(stackName);
3741
+ await this.deleteLock(stackName, region);
3503
3742
  try {
3504
3743
  const retryBody = JSON.stringify(lockInfo, null, 2);
3505
3744
  await this.s3Client.send(
@@ -3513,13 +3752,13 @@ var LockManager = class {
3513
3752
  })
3514
3753
  );
3515
3754
  this.logger.debug(
3516
- `Lock acquired for stack: ${stackName} after expired lock cleanup, owner: ${lockOwner}`
3755
+ `Lock acquired for stack: ${stackName} (${region}) after expired lock cleanup, owner: ${lockOwner}`
3517
3756
  );
3518
3757
  return true;
3519
3758
  } catch (retryError) {
3520
3759
  if (retryError instanceof S3ServiceException && retryError.name === "PreconditionFailed") {
3521
3760
  this.logger.debug(
3522
- `Lock was acquired by another process during expired lock cleanup for stack: ${stackName}`
3761
+ `Lock was acquired by another process during expired lock cleanup for stack: ${stackName} (${region})`
3523
3762
  );
3524
3763
  return false;
3525
3764
  }
@@ -3529,16 +3768,20 @@ var LockManager = class {
3529
3768
  return false;
3530
3769
  }
3531
3770
  throw new LockError(
3532
- `Failed to acquire lock for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,
3771
+ `Failed to acquire lock for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`,
3533
3772
  error instanceof Error ? error : void 0
3534
3773
  );
3535
3774
  }
3536
3775
  }
3537
3776
  /**
3538
- * Get current lock information
3777
+ * Get current lock information.
3778
+ *
3779
+ * `region` is required for the new region-scoped lock layout. Pass
3780
+ * `undefined` only to inspect a legacy `{prefix}/{stackName}/lock.json`
3781
+ * file (e.g. for state-listing tools that don't yet know the region).
3539
3782
  */
3540
- async getLockInfo(stackName) {
3541
- const key = this.getLockKey(stackName);
3783
+ async getLockInfo(stackName, region) {
3784
+ const key = this.getLockKey(stackName, region);
3542
3785
  try {
3543
3786
  this.logger.debug(`Getting lock info for stack: ${stackName}`);
3544
3787
  const response = await this.s3Client.send(
@@ -3576,27 +3819,27 @@ var LockManager = class {
3576
3819
  * not for acquisition decisions — use `acquireLock` for that, which has its
3577
3820
  * own expired-lock cleanup logic.
3578
3821
  */
3579
- async isLocked(stackName) {
3580
- const lockInfo = await this.getLockInfo(stackName);
3822
+ async isLocked(stackName, region) {
3823
+ const lockInfo = await this.getLockInfo(stackName, region);
3581
3824
  return lockInfo !== null;
3582
3825
  }
3583
3826
  /**
3584
3827
  * Release a lock for a stack
3585
3828
  */
3586
- async releaseLock(stackName) {
3587
- const key = this.getLockKey(stackName);
3829
+ async releaseLock(stackName, region) {
3830
+ const key = this.getLockKey(stackName, region);
3588
3831
  try {
3589
- this.logger.debug(`Releasing lock for stack: ${stackName}`);
3832
+ this.logger.debug(`Releasing lock for stack: ${stackName} (${region})`);
3590
3833
  await this.s3Client.send(
3591
3834
  new DeleteObjectCommand2({
3592
3835
  Bucket: this.config.bucket,
3593
3836
  Key: key
3594
3837
  })
3595
3838
  );
3596
- this.logger.debug(`Lock released for stack: ${stackName}`);
3839
+ this.logger.debug(`Lock released for stack: ${stackName} (${region})`);
3597
3840
  } catch (error) {
3598
3841
  throw new LockError(
3599
- `Failed to release lock for stack '${stackName}': ${error instanceof Error ? error.message : String(error)}`,
3842
+ `Failed to release lock for stack '${stackName}' (${region}): ${error instanceof Error ? error.message : String(error)}`,
3600
3843
  error instanceof Error ? error : void 0
3601
3844
  );
3602
3845
  }
@@ -3606,23 +3849,28 @@ var LockManager = class {
3606
3849
  *
3607
3850
  * This is intended for CLI usage (e.g., --force-unlock flag) when a lock
3608
3851
  * is stuck and needs manual intervention.
3852
+ *
3853
+ * Pass `region: undefined` to operate on a legacy
3854
+ * `{prefix}/{stackName}/lock.json` file.
3609
3855
  */
3610
- async forceReleaseLock(stackName) {
3611
- const lockInfo = await this.getLockInfo(stackName);
3856
+ async forceReleaseLock(stackName, region) {
3857
+ const lockInfo = await this.getLockInfo(stackName, region);
3612
3858
  if (!lockInfo) {
3613
- this.logger.warn(`No lock to force release for stack: ${stackName}`);
3859
+ this.logger.warn(
3860
+ `No lock to force release for stack: ${stackName}${region ? ` (${region})` : ""}`
3861
+ );
3614
3862
  return;
3615
3863
  }
3616
3864
  this.logger.warn(
3617
- `Force releasing lock for stack: ${stackName}, owner: ${lockInfo.owner}${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ""}, expired: ${this.isLockExpired(lockInfo)}`
3865
+ `Force releasing lock for stack: ${stackName}${region ? ` (${region})` : ""}, owner: ${lockInfo.owner}${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ""}, expired: ${this.isLockExpired(lockInfo)}`
3618
3866
  );
3619
- await this.deleteLock(stackName);
3867
+ await this.deleteLock(stackName, region);
3620
3868
  }
3621
3869
  /**
3622
3870
  * Internal method to delete the lock file from S3
3623
3871
  */
3624
- async deleteLock(stackName) {
3625
- const key = this.getLockKey(stackName);
3872
+ async deleteLock(stackName, region) {
3873
+ const key = this.getLockKey(stackName, region);
3626
3874
  await this.s3Client.send(
3627
3875
  new DeleteObjectCommand2({
3628
3876
  Bucket: this.config.bucket,
@@ -3643,28 +3891,28 @@ var LockManager = class {
3643
3891
  * @param maxRetries Maximum number of retries (default: 3)
3644
3892
  * @param retryDelay Delay between retries in milliseconds (default: 2000)
3645
3893
  */
3646
- async acquireLockWithRetry(stackName, owner, operation, maxRetries = 3, retryDelay = 2e3) {
3894
+ async acquireLockWithRetry(stackName, region, owner, operation, maxRetries = 3, retryDelay = 2e3) {
3647
3895
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
3648
- const acquired = await this.acquireLock(stackName, owner, operation);
3896
+ const acquired = await this.acquireLock(stackName, region, owner, operation);
3649
3897
  if (acquired) {
3650
3898
  return;
3651
3899
  }
3652
- const lockInfo2 = await this.getLockInfo(stackName);
3900
+ const lockInfo2 = await this.getLockInfo(stackName, region);
3653
3901
  if (lockInfo2) {
3654
3902
  const remainingMs = lockInfo2.expiresAt - Date.now();
3655
3903
  if (attempt < maxRetries) {
3656
3904
  this.logger.info(
3657
- `Stack '${stackName}' is locked by ${lockInfo2.owner}${lockInfo2.operation ? ` (operation: ${lockInfo2.operation})` : ""}. Lock expires in ${this.formatDuration(remainingMs)}. Retrying in ${this.formatDuration(retryDelay)}... (attempt ${attempt + 1}/${maxRetries})`
3905
+ `Stack '${stackName}' (${region}) is locked by ${lockInfo2.owner}${lockInfo2.operation ? ` (operation: ${lockInfo2.operation})` : ""}. Lock expires in ${this.formatDuration(remainingMs)}. Retrying in ${this.formatDuration(retryDelay)}... (attempt ${attempt + 1}/${maxRetries})`
3658
3906
  );
3659
3907
  await new Promise((resolve4) => setTimeout(resolve4, retryDelay));
3660
3908
  continue;
3661
3909
  }
3662
3910
  }
3663
3911
  }
3664
- const lockInfo = await this.getLockInfo(stackName);
3912
+ const lockInfo = await this.getLockInfo(stackName, region);
3665
3913
  const expiresIn = lockInfo ? this.formatDuration(lockInfo.expiresAt - Date.now()) : "unknown";
3666
3914
  throw new LockError(
3667
- `Failed to acquire lock for stack '${stackName}' after ${maxRetries + 1} attempts. ` + (lockInfo ? `Locked by: ${lockInfo.owner}${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ""}, expires in: ${expiresIn}. Use --force-unlock to manually release the lock.` : "Lock exists but could not read lock info.")
3915
+ `Failed to acquire lock for stack '${stackName}' (${region}) after ${maxRetries + 1} attempts. ` + (lockInfo ? `Locked by: ${lockInfo.owner}${lockInfo.operation ? `, operation: ${lockInfo.operation}` : ""}, expires in: ${expiresIn}. Use --force-unlock to manually release the lock.` : "Lock exists but could not read lock info.")
3668
3916
  );
3669
3917
  }
3670
3918
  };
@@ -5447,35 +5695,45 @@ var IntrinsicFunctionResolver = class {
5447
5695
  }
5448
5696
  this.logger.debug(`Resolving Fn::ImportValue: ${exportName}`);
5449
5697
  const allStacks = await context.stateBackend.listStacks();
5450
- this.logger.debug(`Found ${allStacks.length} stacks to search for export: ${exportName}`);
5451
- for (const stackName of allStacks) {
5452
- if (context.stackName && stackName === context.stackName) {
5453
- this.logger.debug(`Skipping current stack: ${stackName}`);
5698
+ this.logger.debug(
5699
+ `Found ${allStacks.length} state record(s) to search for export: ${exportName}`
5700
+ );
5701
+ for (const ref of allStacks) {
5702
+ const { stackName: refStack, region: refRegion } = ref;
5703
+ if (context.stackName && refStack === context.stackName) {
5704
+ this.logger.debug(`Skipping current stack: ${refStack}`);
5454
5705
  continue;
5455
5706
  }
5456
5707
  try {
5457
- const stateData = await context.stateBackend.getState(stackName);
5708
+ const lookupRegion = refRegion ?? this.resolverRegion ?? "";
5709
+ if (!lookupRegion) {
5710
+ this.logger.debug(
5711
+ `No region available for stack '${refStack}' \u2014 skipping (cdkd cannot read state without a region)`
5712
+ );
5713
+ continue;
5714
+ }
5715
+ const stateData = await context.stateBackend.getState(refStack, lookupRegion);
5458
5716
  if (!stateData) {
5459
- this.logger.debug(`No state found for stack: ${stackName}`);
5717
+ this.logger.debug(`No state found for stack: ${refStack} (${lookupRegion})`);
5460
5718
  continue;
5461
5719
  }
5462
5720
  const { state } = stateData;
5463
5721
  if (state.outputs && exportName in state.outputs) {
5464
5722
  const value = state.outputs[exportName];
5465
5723
  this.logger.info(
5466
- `Resolved Fn::ImportValue: ${exportName} = ${JSON.stringify(value)} (from stack: ${stackName})`
5724
+ `Resolved Fn::ImportValue: ${exportName} = ${JSON.stringify(value)} (from stack: ${refStack} / ${lookupRegion})`
5467
5725
  );
5468
5726
  return value;
5469
5727
  }
5470
5728
  } catch (error) {
5471
5729
  this.logger.warn(
5472
- `Failed to read state for stack ${stackName}: ${error instanceof Error ? error.message : String(error)}`
5730
+ `Failed to read state for stack ${refStack}: ${error instanceof Error ? error.message : String(error)}`
5473
5731
  );
5474
5732
  continue;
5475
5733
  }
5476
5734
  }
5477
5735
  throw new Error(
5478
- `Fn::ImportValue: export '${exportName}' not found in any stack. Searched ${allStacks.length} stacks. Make sure the exporting stack has been deployed and the Output has an Export.Name property.`
5736
+ `Fn::ImportValue: export '${exportName}' not found in any stack. Searched ${allStacks.length} state record(s). Make sure the exporting stack has been deployed and the Output has an Export.Name property.`
5479
5737
  );
5480
5738
  }
5481
5739
  /**
@@ -5938,6 +6196,29 @@ var JsonPatchGenerator = class {
5938
6196
  }
5939
6197
  };
5940
6198
 
6199
+ // src/provisioning/region-check.ts
6200
+ function assertRegionMatch(clientRegion, expectedRegion, resourceType, logicalId, physicalId) {
6201
+ if (!expectedRegion) {
6202
+ return;
6203
+ }
6204
+ if (!clientRegion) {
6205
+ throw new ProvisioningError(
6206
+ `Refusing to treat NotFound as idempotent delete success for ${logicalId} (${resourceType}): AWS client region is unknown but stack state expects ${expectedRegion}. The resource may exist in ${expectedRegion} and would be silently removed from state if this NotFound were trusted.`,
6207
+ resourceType,
6208
+ logicalId,
6209
+ physicalId
6210
+ );
6211
+ }
6212
+ if (clientRegion !== expectedRegion) {
6213
+ throw new ProvisioningError(
6214
+ `Refusing to treat NotFound as idempotent delete success for ${logicalId} (${resourceType}): AWS client region ${clientRegion} does not match stack state region ${expectedRegion}. The resource likely still exists in ${expectedRegion}; rerun the destroy with the correct region (e.g. --region ${expectedRegion}).`,
6215
+ resourceType,
6216
+ logicalId,
6217
+ physicalId
6218
+ );
6219
+ }
6220
+ }
6221
+
5941
6222
  // src/provisioning/cloud-control-provider.ts
5942
6223
  var JSON_STRING_PROPERTIES = {
5943
6224
  "AWS::Events::Rule": /* @__PURE__ */ new Set(["EventPattern"])
@@ -6113,7 +6394,7 @@ var CloudControlProvider = class {
6113
6394
  /**
6114
6395
  * Delete a resource using Cloud Control API
6115
6396
  */
6116
- async delete(logicalId, physicalId, resourceType, _properties) {
6397
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
6117
6398
  this.logger.debug(
6118
6399
  `Deleting resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`
6119
6400
  );
@@ -6140,6 +6421,14 @@ var CloudControlProvider = class {
6140
6421
  } catch (error) {
6141
6422
  const err = error;
6142
6423
  if (err.name === "ResourceNotFoundException" || err.message?.includes("does not exist") || err.message?.includes("not found") || err.message?.includes("NotFound")) {
6424
+ const clientRegion = await this.cloudControlClient.config.region();
6425
+ assertRegionMatch(
6426
+ clientRegion,
6427
+ context?.expectedRegion,
6428
+ resourceType,
6429
+ logicalId,
6430
+ physicalId
6431
+ );
6143
6432
  this.logger.debug(`Resource ${logicalId} already deleted (not found), treating as success`);
6144
6433
  return;
6145
6434
  }
@@ -6704,7 +6993,7 @@ var CustomResourceProvider = class _CustomResourceProvider {
6704
6993
  /**
6705
6994
  * Delete a custom resource by invoking its Lambda handler
6706
6995
  */
6707
- async delete(logicalId, physicalId, resourceType, properties) {
6996
+ async delete(logicalId, physicalId, resourceType, properties, _context) {
6708
6997
  this.logger.debug(`Deleting custom resource ${logicalId}: ${physicalId} (${resourceType})`);
6709
6998
  if (!properties) {
6710
6999
  this.logger.warn(
@@ -7526,13 +7815,21 @@ var IAMRoleProvider = class {
7526
7815
  * 3. Remove role from all instance profiles
7527
7816
  * 4. Delete the role itself
7528
7817
  */
7529
- async delete(logicalId, physicalId, resourceType, _properties) {
7818
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
7530
7819
  this.logger.debug(`Deleting IAM role ${logicalId}: ${physicalId}`);
7531
7820
  try {
7532
7821
  try {
7533
7822
  await this.iamClient.send(new GetRoleCommand({ RoleName: physicalId }));
7534
7823
  } catch (error) {
7535
7824
  if (error instanceof NoSuchEntityException) {
7825
+ const clientRegion = await this.iamClient.config.region();
7826
+ assertRegionMatch(
7827
+ clientRegion,
7828
+ context?.expectedRegion,
7829
+ resourceType,
7830
+ logicalId,
7831
+ physicalId
7832
+ );
7536
7833
  this.logger.debug(`Role ${physicalId} does not exist, skipping deletion`);
7537
7834
  return;
7538
7835
  }
@@ -8027,13 +8324,23 @@ var IAMPolicyProvider = class {
8027
8324
  /**
8028
8325
  * Delete an IAM inline policy
8029
8326
  */
8030
- async delete(logicalId, physicalId, resourceType, properties) {
8327
+ async delete(logicalId, physicalId, resourceType, properties, context) {
8031
8328
  this.logger.debug(`Deleting IAM policy ${logicalId}: ${physicalId}`);
8032
8329
  const policyName = physicalId.includes(":") ? physicalId.split(":")[0] : physicalId;
8033
8330
  if (!policyName) {
8034
8331
  this.logger.warn(`Invalid physical ID format: ${physicalId}, skipping deletion`);
8035
8332
  return;
8036
8333
  }
8334
+ const onNotFound = async (target) => {
8335
+ const clientRegion = await this.iamClient.config.region();
8336
+ assertRegionMatch(
8337
+ clientRegion,
8338
+ context?.expectedRegion,
8339
+ resourceType,
8340
+ logicalId,
8341
+ `${physicalId} (${target})`
8342
+ );
8343
+ };
8037
8344
  try {
8038
8345
  const roles = properties?.["Roles"];
8039
8346
  const groups = properties?.["Groups"];
@@ -8050,7 +8357,9 @@ var IAMPolicyProvider = class {
8050
8357
  );
8051
8358
  this.logger.debug(`Deleted inline policy ${policyName} from role ${firstRole}`);
8052
8359
  } catch (error) {
8053
- if (!(error instanceof NoSuchEntityException2)) {
8360
+ if (error instanceof NoSuchEntityException2) {
8361
+ await onNotFound(`role ${firstRole}`);
8362
+ } else {
8054
8363
  throw error;
8055
8364
  }
8056
8365
  }
@@ -8067,7 +8376,9 @@ var IAMPolicyProvider = class {
8067
8376
  );
8068
8377
  this.logger.debug(`Deleted inline policy ${policyName} from role ${roleName}`);
8069
8378
  } catch (error) {
8070
- if (!(error instanceof NoSuchEntityException2)) {
8379
+ if (error instanceof NoSuchEntityException2) {
8380
+ await onNotFound(`role ${roleName}`);
8381
+ } else {
8071
8382
  throw error;
8072
8383
  }
8073
8384
  }
@@ -8084,7 +8395,9 @@ var IAMPolicyProvider = class {
8084
8395
  );
8085
8396
  this.logger.debug(`Deleted inline policy ${policyName} from group ${groupName}`);
8086
8397
  } catch (error) {
8087
- if (!(error instanceof NoSuchEntityException2)) {
8398
+ if (error instanceof NoSuchEntityException2) {
8399
+ await onNotFound(`group ${groupName}`);
8400
+ } else {
8088
8401
  throw error;
8089
8402
  }
8090
8403
  }
@@ -8101,7 +8414,9 @@ var IAMPolicyProvider = class {
8101
8414
  );
8102
8415
  this.logger.debug(`Deleted inline policy ${policyName} from user ${userName}`);
8103
8416
  } catch (error) {
8104
- if (!(error instanceof NoSuchEntityException2)) {
8417
+ if (error instanceof NoSuchEntityException2) {
8418
+ await onNotFound(`user ${userName}`);
8419
+ } else {
8105
8420
  throw error;
8106
8421
  }
8107
8422
  }
@@ -8259,7 +8574,7 @@ var IAMInstanceProfileProvider = class {
8259
8574
  *
8260
8575
  * Before deleting, removes all roles from the instance profile.
8261
8576
  */
8262
- async delete(logicalId, physicalId, resourceType, _properties) {
8577
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
8263
8578
  this.logger.debug(`Deleting IAM instance profile ${logicalId}: ${physicalId}`);
8264
8579
  try {
8265
8580
  let roles = [];
@@ -8272,6 +8587,14 @@ var IAMInstanceProfileProvider = class {
8272
8587
  ) || [];
8273
8588
  } catch (error) {
8274
8589
  if (error instanceof NoSuchEntityException3) {
8590
+ const clientRegion = await this.iamClient.config.region();
8591
+ assertRegionMatch(
8592
+ clientRegion,
8593
+ context?.expectedRegion,
8594
+ resourceType,
8595
+ logicalId,
8596
+ physicalId
8597
+ );
8275
8598
  this.logger.debug(`Instance profile ${physicalId} does not exist, skipping deletion`);
8276
8599
  return;
8277
8600
  }
@@ -8298,6 +8621,14 @@ var IAMInstanceProfileProvider = class {
8298
8621
  this.logger.debug(`Successfully deleted IAM instance profile ${logicalId}`);
8299
8622
  } catch (error) {
8300
8623
  if (error instanceof NoSuchEntityException3) {
8624
+ const clientRegion = await this.iamClient.config.region();
8625
+ assertRegionMatch(
8626
+ clientRegion,
8627
+ context?.expectedRegion,
8628
+ resourceType,
8629
+ logicalId,
8630
+ physicalId
8631
+ );
8301
8632
  this.logger.debug(`Instance profile ${physicalId} does not exist, skipping deletion`);
8302
8633
  return;
8303
8634
  }
@@ -8417,14 +8748,20 @@ var IAMUserGroupProvider = class {
8417
8748
  );
8418
8749
  }
8419
8750
  }
8420
- async delete(logicalId, physicalId, resourceType, properties) {
8751
+ async delete(logicalId, physicalId, resourceType, properties, context) {
8421
8752
  switch (resourceType) {
8422
8753
  case "AWS::IAM::User":
8423
- return this.deleteUser(logicalId, physicalId, resourceType);
8754
+ return this.deleteUser(logicalId, physicalId, resourceType, context);
8424
8755
  case "AWS::IAM::Group":
8425
- return this.deleteGroup(logicalId, physicalId, resourceType);
8756
+ return this.deleteGroup(logicalId, physicalId, resourceType, context);
8426
8757
  case "AWS::IAM::UserToGroupAddition":
8427
- return this.deleteUserToGroupAddition(logicalId, physicalId, resourceType, properties);
8758
+ return this.deleteUserToGroupAddition(
8759
+ logicalId,
8760
+ physicalId,
8761
+ resourceType,
8762
+ properties,
8763
+ context
8764
+ );
8428
8765
  default:
8429
8766
  throw new ProvisioningError(
8430
8767
  `Unsupported resource type: ${resourceType}`,
@@ -8627,13 +8964,21 @@ var IAMUserGroupProvider = class {
8627
8964
  );
8628
8965
  }
8629
8966
  }
8630
- async deleteUser(logicalId, physicalId, resourceType) {
8967
+ async deleteUser(logicalId, physicalId, resourceType, context) {
8631
8968
  this.logger.debug(`Deleting IAM user ${logicalId}: ${physicalId}`);
8632
8969
  try {
8633
8970
  try {
8634
8971
  await this.iamClient.send(new GetUserCommand({ UserName: physicalId }));
8635
8972
  } catch (error) {
8636
8973
  if (error instanceof NoSuchEntityException4) {
8974
+ const clientRegion = await this.iamClient.config.region();
8975
+ assertRegionMatch(
8976
+ clientRegion,
8977
+ context?.expectedRegion,
8978
+ resourceType,
8979
+ logicalId,
8980
+ physicalId
8981
+ );
8637
8982
  this.logger.debug(`User ${physicalId} does not exist, skipping deletion`);
8638
8983
  return;
8639
8984
  }
@@ -8973,7 +9318,7 @@ var IAMUserGroupProvider = class {
8973
9318
  );
8974
9319
  }
8975
9320
  }
8976
- async deleteGroup(logicalId, physicalId, resourceType) {
9321
+ async deleteGroup(logicalId, physicalId, resourceType, context) {
8977
9322
  this.logger.debug(`Deleting IAM group ${logicalId}: ${physicalId}`);
8978
9323
  try {
8979
9324
  await this.detachAllGroupPolicies(physicalId);
@@ -8983,6 +9328,14 @@ var IAMUserGroupProvider = class {
8983
9328
  this.logger.debug(`Successfully deleted IAM group ${logicalId}`);
8984
9329
  } catch (error) {
8985
9330
  if (error instanceof NoSuchEntityException4) {
9331
+ const clientRegion = await this.iamClient.config.region();
9332
+ assertRegionMatch(
9333
+ clientRegion,
9334
+ context?.expectedRegion,
9335
+ resourceType,
9336
+ logicalId,
9337
+ physicalId
9338
+ );
8986
9339
  this.logger.debug(`Group ${physicalId} does not exist, skipping deletion`);
8987
9340
  return;
8988
9341
  }
@@ -9257,7 +9610,7 @@ var IAMUserGroupProvider = class {
9257
9610
  );
9258
9611
  }
9259
9612
  }
9260
- async deleteUserToGroupAddition(logicalId, physicalId, resourceType, properties) {
9613
+ async deleteUserToGroupAddition(logicalId, physicalId, resourceType, properties, _context) {
9261
9614
  this.logger.debug(`Deleting IAM UserToGroupAddition ${logicalId}`);
9262
9615
  if (!properties) {
9263
9616
  this.logger.debug(`No properties for UserToGroupAddition ${logicalId}, skipping deletion`);
@@ -10131,12 +10484,20 @@ var S3BucketProvider = class {
10131
10484
  *
10132
10485
  * Note: The bucket must be empty before deletion.
10133
10486
  */
10134
- async delete(logicalId, physicalId, resourceType, _properties) {
10487
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
10135
10488
  this.logger.debug(`Deleting S3 bucket ${logicalId}: ${physicalId}`);
10136
10489
  try {
10137
10490
  await this.deleteBucketWithEmptyRetry(logicalId, physicalId);
10138
10491
  } catch (error) {
10139
10492
  if (error instanceof NoSuchBucket) {
10493
+ const clientRegion = await this.s3Client.config.region();
10494
+ assertRegionMatch(
10495
+ clientRegion,
10496
+ context?.expectedRegion,
10497
+ resourceType,
10498
+ logicalId,
10499
+ physicalId
10500
+ );
10140
10501
  this.logger.debug(`Bucket ${physicalId} does not exist, skipping deletion`);
10141
10502
  return;
10142
10503
  }
@@ -10331,7 +10692,7 @@ var S3BucketPolicyProvider = class {
10331
10692
  /**
10332
10693
  * Delete an S3 bucket policy
10333
10694
  */
10334
- async delete(logicalId, physicalId, resourceType, _properties) {
10695
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
10335
10696
  this.logger.debug(`Deleting S3 bucket policy ${logicalId}: ${physicalId}`);
10336
10697
  try {
10337
10698
  try {
@@ -10343,10 +10704,26 @@ var S3BucketPolicyProvider = class {
10343
10704
  this.logger.debug(`Successfully deleted S3 bucket policy ${logicalId}`);
10344
10705
  } catch (error) {
10345
10706
  if (error instanceof NoSuchBucket2) {
10707
+ const clientRegion = await this.s3Client.config.region();
10708
+ assertRegionMatch(
10709
+ clientRegion,
10710
+ context?.expectedRegion,
10711
+ resourceType,
10712
+ logicalId,
10713
+ physicalId
10714
+ );
10346
10715
  this.logger.debug(`Bucket ${physicalId} does not exist, skipping policy deletion`);
10347
10716
  return;
10348
10717
  }
10349
10718
  if (error instanceof Error && (error.name === "NoSuchBucketPolicy" || error.message.includes("does not have"))) {
10719
+ const clientRegion = await this.s3Client.config.region();
10720
+ assertRegionMatch(
10721
+ clientRegion,
10722
+ context?.expectedRegion,
10723
+ resourceType,
10724
+ logicalId,
10725
+ physicalId
10726
+ );
10350
10727
  this.logger.debug(`Bucket policy for ${physicalId} does not exist, skipping`);
10351
10728
  return;
10352
10729
  }
@@ -10536,13 +10913,21 @@ var SQSQueueProvider = class {
10536
10913
  /**
10537
10914
  * Delete an SQS queue
10538
10915
  */
10539
- async delete(logicalId, physicalId, resourceType, _properties) {
10916
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
10540
10917
  this.logger.debug(`Deleting SQS queue ${logicalId}: ${physicalId}`);
10541
10918
  try {
10542
10919
  await this.sqsClient.send(new DeleteQueueCommand({ QueueUrl: physicalId }));
10543
10920
  this.logger.debug(`Successfully deleted SQS queue ${logicalId}`);
10544
10921
  } catch (error) {
10545
10922
  if (error instanceof QueueDoesNotExist) {
10923
+ const clientRegion = await this.sqsClient.config.region();
10924
+ assertRegionMatch(
10925
+ clientRegion,
10926
+ context?.expectedRegion,
10927
+ resourceType,
10928
+ logicalId,
10929
+ physicalId
10930
+ );
10546
10931
  this.logger.debug(`SQS queue ${physicalId} does not exist, skipping deletion`);
10547
10932
  return;
10548
10933
  }
@@ -10691,7 +11076,7 @@ var SQSQueuePolicyProvider = class {
10691
11076
  /**
10692
11077
  * Delete an SQS queue policy
10693
11078
  */
10694
- async delete(logicalId, physicalId, resourceType, _properties) {
11079
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
10695
11080
  this.logger.debug(`Deleting SQS queue policy ${logicalId}: ${physicalId}`);
10696
11081
  try {
10697
11082
  await this.sqsClient.send(
@@ -10705,6 +11090,14 @@ var SQSQueuePolicyProvider = class {
10705
11090
  this.logger.debug(`Successfully deleted SQS queue policy ${logicalId}`);
10706
11091
  } catch (error) {
10707
11092
  if (error instanceof Error && (error.name === "QueueDoesNotExist" || error.message.includes("does not exist"))) {
11093
+ const clientRegion = await this.sqsClient.config.region();
11094
+ assertRegionMatch(
11095
+ clientRegion,
11096
+ context?.expectedRegion,
11097
+ resourceType,
11098
+ logicalId,
11099
+ physicalId
11100
+ );
10708
11101
  this.logger.debug(`Queue ${physicalId} does not exist, skipping policy deletion`);
10709
11102
  return;
10710
11103
  }
@@ -10985,13 +11378,21 @@ var SNSTopicProvider = class {
10985
11378
  /**
10986
11379
  * Delete an SNS topic
10987
11380
  */
10988
- async delete(logicalId, physicalId, resourceType, _properties) {
11381
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
10989
11382
  this.logger.debug(`Deleting SNS topic ${logicalId}: ${physicalId}`);
10990
11383
  try {
10991
11384
  await this.snsClient.send(new DeleteTopicCommand({ TopicArn: physicalId }));
10992
11385
  this.logger.debug(`Successfully deleted SNS topic ${logicalId}`);
10993
11386
  } catch (error) {
10994
11387
  if (error instanceof NotFoundException) {
11388
+ const clientRegion = await this.snsClient.config.region();
11389
+ assertRegionMatch(
11390
+ clientRegion,
11391
+ context?.expectedRegion,
11392
+ resourceType,
11393
+ logicalId,
11394
+ physicalId
11395
+ );
10995
11396
  this.logger.debug(`SNS topic ${physicalId} does not exist, skipping deletion`);
10996
11397
  return;
10997
11398
  }
@@ -11110,7 +11511,7 @@ var SNSSubscriptionProvider = class {
11110
11511
  /**
11111
11512
  * Delete an SNS subscription
11112
11513
  */
11113
- async delete(logicalId, physicalId, resourceType, _properties) {
11514
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
11114
11515
  this.logger.debug(`Deleting SNS subscription ${logicalId}: ${physicalId}`);
11115
11516
  try {
11116
11517
  await this.snsClient.send(
@@ -11121,6 +11522,14 @@ var SNSSubscriptionProvider = class {
11121
11522
  this.logger.debug(`Successfully deleted SNS subscription ${logicalId}`);
11122
11523
  } catch (error) {
11123
11524
  if (error instanceof NotFoundException2) {
11525
+ const clientRegion = await this.snsClient.config.region();
11526
+ assertRegionMatch(
11527
+ clientRegion,
11528
+ context?.expectedRegion,
11529
+ resourceType,
11530
+ logicalId,
11531
+ physicalId
11532
+ );
11124
11533
  this.logger.debug(`Subscription ${physicalId} does not exist, skipping deletion`);
11125
11534
  return;
11126
11535
  }
@@ -11241,7 +11650,7 @@ var SNSTopicPolicyProvider = class {
11241
11650
  *
11242
11651
  * Removes the policy from each topic by setting an empty policy.
11243
11652
  */
11244
- async delete(logicalId, physicalId, resourceType, _properties) {
11653
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
11245
11654
  this.logger.debug(`Deleting SNS topic policy ${logicalId}: ${physicalId}`);
11246
11655
  const topicArns = physicalId.split(",");
11247
11656
  for (const topicArn of topicArns) {
@@ -11250,6 +11659,14 @@ var SNSTopicPolicyProvider = class {
11250
11659
  this.logger.debug(`Removed policy from topic ${topicArn}`);
11251
11660
  } catch (error) {
11252
11661
  if (error instanceof Error && (error.name === "NotFoundException" || error.name === "NotFound" || error.message.includes("not found") || error.message.includes("does not exist") || error.message.includes("Invalid parameter"))) {
11662
+ const clientRegion = await getAwsClients().sns.config.region();
11663
+ assertRegionMatch(
11664
+ clientRegion,
11665
+ context?.expectedRegion,
11666
+ resourceType,
11667
+ logicalId,
11668
+ topicArn
11669
+ );
11253
11670
  this.logger.debug(`Topic ${topicArn} not found or policy already removed, skipping`);
11254
11671
  continue;
11255
11672
  }
@@ -11514,7 +11931,7 @@ var LambdaFunctionProvider = class {
11514
11931
  * security groups, we poll DescribeNetworkInterfaces for the function's
11515
11932
  * managed ENIs and only return once they are gone (or the timeout elapses).
11516
11933
  */
11517
- async delete(logicalId, physicalId, resourceType, properties) {
11934
+ async delete(logicalId, physicalId, resourceType, properties, context) {
11518
11935
  this.logger.debug(`Deleting Lambda function ${logicalId}: ${physicalId}`);
11519
11936
  const hasVpcConfig = this.hasVpcConfig(properties?.["VpcConfig"]);
11520
11937
  if (hasVpcConfig) {
@@ -11528,6 +11945,14 @@ var LambdaFunctionProvider = class {
11528
11945
  this.logger.debug(`Detached VPC config from Lambda ${physicalId} before deletion`);
11529
11946
  } catch (error) {
11530
11947
  if (error instanceof ResourceNotFoundException) {
11948
+ const clientRegion = await this.lambdaClient.config.region();
11949
+ assertRegionMatch(
11950
+ clientRegion,
11951
+ context?.expectedRegion,
11952
+ resourceType,
11953
+ logicalId,
11954
+ physicalId
11955
+ );
11531
11956
  return;
11532
11957
  }
11533
11958
  this.logger.warn(
@@ -11541,6 +11966,14 @@ var LambdaFunctionProvider = class {
11541
11966
  this.logger.debug(`Successfully deleted Lambda function ${logicalId}`);
11542
11967
  } catch (error) {
11543
11968
  if (error instanceof ResourceNotFoundException) {
11969
+ const clientRegion = await this.lambdaClient.config.region();
11970
+ assertRegionMatch(
11971
+ clientRegion,
11972
+ context?.expectedRegion,
11973
+ resourceType,
11974
+ logicalId,
11975
+ physicalId
11976
+ );
11544
11977
  this.logger.debug(`Lambda function ${physicalId} does not exist, skipping deletion`);
11545
11978
  return;
11546
11979
  }
@@ -12007,7 +12440,7 @@ var LambdaPermissionProvider = class {
12007
12440
  /**
12008
12441
  * Delete a Lambda permission
12009
12442
  */
12010
- async delete(logicalId, physicalId, resourceType, properties) {
12443
+ async delete(logicalId, physicalId, resourceType, properties, context) {
12011
12444
  this.logger.debug(`Deleting Lambda permission ${logicalId}: ${physicalId}`);
12012
12445
  const functionName = properties?.["FunctionName"];
12013
12446
  if (!functionName) {
@@ -12030,6 +12463,14 @@ var LambdaPermissionProvider = class {
12030
12463
  this.logger.debug(`Successfully deleted Lambda permission ${logicalId}`);
12031
12464
  } catch (error) {
12032
12465
  if (error instanceof ResourceNotFoundException2) {
12466
+ const clientRegion = await this.lambdaClient.config.region();
12467
+ assertRegionMatch(
12468
+ clientRegion,
12469
+ context?.expectedRegion,
12470
+ resourceType,
12471
+ logicalId,
12472
+ physicalId
12473
+ );
12033
12474
  this.logger.debug(`Lambda permission ${physicalId} does not exist, skipping deletion`);
12034
12475
  return;
12035
12476
  }
@@ -12146,7 +12587,7 @@ var LambdaUrlProvider = class {
12146
12587
  /**
12147
12588
  * Delete a Lambda Function URL
12148
12589
  */
12149
- async delete(logicalId, physicalId, resourceType, _properties) {
12590
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
12150
12591
  this.logger.debug(`Deleting Lambda URL ${logicalId}: ${physicalId}`);
12151
12592
  try {
12152
12593
  await this.lambdaClient.send(
@@ -12155,6 +12596,14 @@ var LambdaUrlProvider = class {
12155
12596
  this.logger.debug(`Successfully deleted Lambda URL ${logicalId}`);
12156
12597
  } catch (error) {
12157
12598
  if (error instanceof ResourceNotFoundException3) {
12599
+ const clientRegion = await this.lambdaClient.config.region();
12600
+ assertRegionMatch(
12601
+ clientRegion,
12602
+ context?.expectedRegion,
12603
+ resourceType,
12604
+ logicalId,
12605
+ physicalId
12606
+ );
12158
12607
  this.logger.debug(`Lambda URL ${physicalId} does not exist, skipping deletion`);
12159
12608
  return;
12160
12609
  }
@@ -12365,13 +12814,21 @@ var LambdaEventSourceMappingProvider = class {
12365
12814
  /**
12366
12815
  * Delete a Lambda Event Source Mapping
12367
12816
  */
12368
- async delete(logicalId, physicalId, resourceType, _properties) {
12817
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
12369
12818
  this.logger.debug(`Deleting event source mapping ${logicalId}: ${physicalId}`);
12370
12819
  try {
12371
12820
  try {
12372
12821
  await this.lambdaClient.send(new GetEventSourceMappingCommand({ UUID: physicalId }));
12373
12822
  } catch (error) {
12374
12823
  if (error instanceof ResourceNotFoundException4) {
12824
+ const clientRegion = await this.lambdaClient.config.region();
12825
+ assertRegionMatch(
12826
+ clientRegion,
12827
+ context?.expectedRegion,
12828
+ resourceType,
12829
+ logicalId,
12830
+ physicalId
12831
+ );
12375
12832
  this.logger.debug(`Event source mapping ${physicalId} does not exist, skipping deletion`);
12376
12833
  return;
12377
12834
  }
@@ -12381,6 +12838,14 @@ var LambdaEventSourceMappingProvider = class {
12381
12838
  this.logger.debug(`Successfully deleted event source mapping ${logicalId}`);
12382
12839
  } catch (error) {
12383
12840
  if (error instanceof ResourceNotFoundException4) {
12841
+ const clientRegion = await this.lambdaClient.config.region();
12842
+ assertRegionMatch(
12843
+ clientRegion,
12844
+ context?.expectedRegion,
12845
+ resourceType,
12846
+ logicalId,
12847
+ physicalId
12848
+ );
12384
12849
  this.logger.debug(`Event source mapping ${physicalId} does not exist, skipping deletion`);
12385
12850
  return;
12386
12851
  }
@@ -12494,7 +12959,7 @@ var LambdaLayerVersionProvider = class {
12494
12959
  /**
12495
12960
  * Delete a Lambda layer version
12496
12961
  */
12497
- async delete(logicalId, physicalId, resourceType, _properties) {
12962
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
12498
12963
  this.logger.debug(`Deleting Lambda layer version ${logicalId}: ${physicalId}`);
12499
12964
  const arnParts = physicalId.split(":");
12500
12965
  if (arnParts.length < 8) {
@@ -12517,6 +12982,14 @@ var LambdaLayerVersionProvider = class {
12517
12982
  this.logger.debug(`Successfully deleted Lambda layer version ${logicalId}`);
12518
12983
  } catch (error) {
12519
12984
  if (error instanceof ResourceNotFoundException5) {
12985
+ const clientRegion = await this.lambdaClient.config.region();
12986
+ assertRegionMatch(
12987
+ clientRegion,
12988
+ context?.expectedRegion,
12989
+ resourceType,
12990
+ logicalId,
12991
+ physicalId
12992
+ );
12520
12993
  this.logger.debug(`Lambda layer version ${physicalId} does not exist, skipping deletion`);
12521
12994
  return;
12522
12995
  }
@@ -12693,13 +13166,21 @@ var DynamoDBTableProvider = class {
12693
13166
  /**
12694
13167
  * Delete a DynamoDB table
12695
13168
  */
12696
- async delete(logicalId, physicalId, resourceType, _properties) {
13169
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
12697
13170
  this.logger.debug(`Deleting DynamoDB table ${logicalId}: ${physicalId}`);
12698
13171
  try {
12699
13172
  await this.dynamoDBClient.send(new DeleteTableCommand({ TableName: physicalId }));
12700
13173
  this.logger.debug(`Successfully deleted DynamoDB table ${logicalId}`);
12701
13174
  } catch (error) {
12702
13175
  if (error instanceof ResourceNotFoundException6) {
13176
+ const clientRegion = await this.dynamoDBClient.config.region();
13177
+ assertRegionMatch(
13178
+ clientRegion,
13179
+ context?.expectedRegion,
13180
+ resourceType,
13181
+ logicalId,
13182
+ physicalId
13183
+ );
12703
13184
  this.logger.debug(`DynamoDB table ${physicalId} does not exist, skipping deletion`);
12704
13185
  return;
12705
13186
  }
@@ -12928,13 +13409,21 @@ var LogsLogGroupProvider = class {
12928
13409
  /**
12929
13410
  * Delete a CloudWatch Logs log group
12930
13411
  */
12931
- async delete(logicalId, physicalId, resourceType, _properties) {
13412
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
12932
13413
  this.logger.debug(`Deleting log group ${logicalId}: ${physicalId}`);
12933
13414
  try {
12934
13415
  await this.logsClient.send(new DeleteLogGroupCommand({ logGroupName: physicalId }));
12935
13416
  this.logger.debug(`Successfully deleted log group ${logicalId}`);
12936
13417
  } catch (error) {
12937
13418
  if (error instanceof ResourceNotFoundException7) {
13419
+ const clientRegion = await this.logsClient.config.region();
13420
+ assertRegionMatch(
13421
+ clientRegion,
13422
+ context?.expectedRegion,
13423
+ resourceType,
13424
+ logicalId,
13425
+ physicalId
13426
+ );
12938
13427
  this.logger.debug(`Log group ${physicalId} does not exist, skipping deletion`);
12939
13428
  return;
12940
13429
  }
@@ -13065,7 +13554,7 @@ var CloudWatchAlarmProvider = class {
13065
13554
  /**
13066
13555
  * Delete a CloudWatch alarm
13067
13556
  */
13068
- async delete(logicalId, physicalId, resourceType, _properties) {
13557
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
13069
13558
  this.logger.debug(`Deleting CloudWatch alarm ${logicalId}: ${physicalId}`);
13070
13559
  try {
13071
13560
  await this.cloudWatchClient.send(
@@ -13076,6 +13565,14 @@ var CloudWatchAlarmProvider = class {
13076
13565
  this.logger.debug(`Successfully deleted CloudWatch alarm ${logicalId}`);
13077
13566
  } catch (error) {
13078
13567
  if (error instanceof Error && error.name === "ResourceNotFound") {
13568
+ const clientRegion = await this.cloudWatchClient.config.region();
13569
+ assertRegionMatch(
13570
+ clientRegion,
13571
+ context?.expectedRegion,
13572
+ resourceType,
13573
+ logicalId,
13574
+ physicalId
13575
+ );
13079
13576
  this.logger.debug(`Alarm ${physicalId} does not exist, skipping deletion`);
13080
13577
  return;
13081
13578
  }
@@ -13369,7 +13866,7 @@ var SecretsManagerSecretProvider = class {
13369
13866
  /**
13370
13867
  * Delete a Secrets Manager secret
13371
13868
  */
13372
- async delete(logicalId, physicalId, resourceType, _properties) {
13869
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
13373
13870
  this.logger.debug(`Deleting secret ${logicalId}: ${physicalId}`);
13374
13871
  try {
13375
13872
  await this.smClient.send(
@@ -13381,6 +13878,14 @@ var SecretsManagerSecretProvider = class {
13381
13878
  this.logger.debug(`Successfully deleted secret ${logicalId}`);
13382
13879
  } catch (error) {
13383
13880
  if (error instanceof ResourceNotFoundException8) {
13881
+ const clientRegion = await this.smClient.config.region();
13882
+ assertRegionMatch(
13883
+ clientRegion,
13884
+ context?.expectedRegion,
13885
+ resourceType,
13886
+ logicalId,
13887
+ physicalId
13888
+ );
13384
13889
  this.logger.debug(`Secret ${physicalId} does not exist, skipping deletion`);
13385
13890
  return;
13386
13891
  }
@@ -13625,7 +14130,7 @@ var SSMParameterProvider = class {
13625
14130
  /**
13626
14131
  * Delete an SSM parameter
13627
14132
  */
13628
- async delete(logicalId, physicalId, resourceType, _properties) {
14133
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
13629
14134
  this.logger.debug(`Deleting SSM parameter ${logicalId}: ${physicalId}`);
13630
14135
  try {
13631
14136
  await this.ssmClient.send(
@@ -13636,6 +14141,14 @@ var SSMParameterProvider = class {
13636
14141
  this.logger.debug(`Successfully deleted SSM parameter ${logicalId}`);
13637
14142
  } catch (error) {
13638
14143
  if (error instanceof ParameterNotFound) {
14144
+ const clientRegion = await this.ssmClient.config.region();
14145
+ assertRegionMatch(
14146
+ clientRegion,
14147
+ context?.expectedRegion,
14148
+ resourceType,
14149
+ logicalId,
14150
+ physicalId
14151
+ );
13639
14152
  this.logger.debug(`Parameter ${physicalId} does not exist, skipping deletion`);
13640
14153
  return;
13641
14154
  }
@@ -13854,7 +14367,7 @@ var EventBridgeRuleProvider = class {
13854
14367
  *
13855
14368
  * Before deleting, removes all targets (required by EventBridge API).
13856
14369
  */
13857
- async delete(logicalId, physicalId, resourceType, _properties) {
14370
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
13858
14371
  this.logger.debug(`Deleting EventBridge rule ${logicalId}: ${physicalId}`);
13859
14372
  const ruleName = this.extractRuleNameFromArn(physicalId);
13860
14373
  try {
@@ -13866,6 +14379,14 @@ var EventBridgeRuleProvider = class {
13866
14379
  targetIds = (targetsResponse.Targets || []).map((t) => t.Id).filter((id) => id !== void 0);
13867
14380
  } catch (error) {
13868
14381
  if (error instanceof ResourceNotFoundException9) {
14382
+ const clientRegion = await this.eventBridgeClient.config.region();
14383
+ assertRegionMatch(
14384
+ clientRegion,
14385
+ context?.expectedRegion,
14386
+ resourceType,
14387
+ logicalId,
14388
+ physicalId
14389
+ );
13869
14390
  this.logger.debug(`Rule ${ruleName} does not exist, skipping deletion`);
13870
14391
  return;
13871
14392
  }
@@ -13884,6 +14405,14 @@ var EventBridgeRuleProvider = class {
13884
14405
  this.logger.debug(`Successfully deleted EventBridge rule ${logicalId}`);
13885
14406
  } catch (error) {
13886
14407
  if (error instanceof ResourceNotFoundException9) {
14408
+ const clientRegion = await this.eventBridgeClient.config.region();
14409
+ assertRegionMatch(
14410
+ clientRegion,
14411
+ context?.expectedRegion,
14412
+ resourceType,
14413
+ logicalId,
14414
+ physicalId
14415
+ );
13887
14416
  this.logger.debug(`Rule ${ruleName} does not exist, skipping deletion`);
13888
14417
  return;
13889
14418
  }
@@ -14069,7 +14598,7 @@ var EventBridgeBusProvider = class {
14069
14598
  }
14070
14599
  return { physicalId, wasReplaced: false, attributes: {} };
14071
14600
  }
14072
- async delete(logicalId, physicalId, resourceType) {
14601
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
14073
14602
  this.logger.debug(`Deleting EventBus ${logicalId}: ${physicalId}`);
14074
14603
  try {
14075
14604
  await this.cleanupRulesOnBus(physicalId);
@@ -14077,11 +14606,27 @@ var EventBridgeBusProvider = class {
14077
14606
  this.logger.debug(`Successfully deleted EventBus ${logicalId}`);
14078
14607
  } catch (error) {
14079
14608
  if (error instanceof ResourceNotFoundException10) {
14609
+ const clientRegion = await this.eventBridgeClient.config.region();
14610
+ assertRegionMatch(
14611
+ clientRegion,
14612
+ context?.expectedRegion,
14613
+ resourceType,
14614
+ logicalId,
14615
+ physicalId
14616
+ );
14080
14617
  this.logger.debug(`EventBus ${physicalId} does not exist, skipping`);
14081
14618
  return;
14082
14619
  }
14083
14620
  const msg = error instanceof Error ? error.message : String(error);
14084
14621
  if (msg.includes("does not exist")) {
14622
+ const clientRegion = await this.eventBridgeClient.config.region();
14623
+ assertRegionMatch(
14624
+ clientRegion,
14625
+ context?.expectedRegion,
14626
+ resourceType,
14627
+ logicalId,
14628
+ physicalId
14629
+ );
14085
14630
  this.logger.debug(`EventBus ${physicalId} does not exist, skipping`);
14086
14631
  return;
14087
14632
  }
@@ -14381,32 +14926,38 @@ var EC2Provider = class {
14381
14926
  );
14382
14927
  }
14383
14928
  }
14384
- async delete(logicalId, physicalId, resourceType, properties) {
14929
+ async delete(logicalId, physicalId, resourceType, properties, context) {
14385
14930
  switch (resourceType) {
14386
14931
  case "AWS::EC2::VPC":
14387
- return this.deleteVpc(logicalId, physicalId, resourceType);
14932
+ return this.deleteVpc(logicalId, physicalId, resourceType, context);
14388
14933
  case "AWS::EC2::Subnet":
14389
- return this.deleteSubnet(logicalId, physicalId, resourceType);
14934
+ return this.deleteSubnet(logicalId, physicalId, resourceType, context);
14390
14935
  case "AWS::EC2::InternetGateway":
14391
- return this.deleteInternetGateway(logicalId, physicalId, resourceType);
14936
+ return this.deleteInternetGateway(logicalId, physicalId, resourceType, context);
14392
14937
  case "AWS::EC2::VPCGatewayAttachment":
14393
- return this.deleteVpcGatewayAttachment(logicalId, physicalId, resourceType);
14938
+ return this.deleteVpcGatewayAttachment(logicalId, physicalId, resourceType, context);
14394
14939
  case "AWS::EC2::RouteTable":
14395
- return this.deleteRouteTable(logicalId, physicalId, resourceType);
14940
+ return this.deleteRouteTable(logicalId, physicalId, resourceType, context);
14396
14941
  case "AWS::EC2::Route":
14397
- return this.deleteRoute(logicalId, physicalId, resourceType);
14942
+ return this.deleteRoute(logicalId, physicalId, resourceType, context);
14398
14943
  case "AWS::EC2::SubnetRouteTableAssociation":
14399
- return this.deleteSubnetRouteTableAssociation(logicalId, physicalId, resourceType);
14944
+ return this.deleteSubnetRouteTableAssociation(logicalId, physicalId, resourceType, context);
14400
14945
  case "AWS::EC2::SecurityGroup":
14401
- return this.deleteSecurityGroup(logicalId, physicalId, resourceType);
14946
+ return this.deleteSecurityGroup(logicalId, physicalId, resourceType, context);
14402
14947
  case "AWS::EC2::SecurityGroupIngress":
14403
- return this.deleteSecurityGroupIngress(logicalId, physicalId, resourceType, properties);
14948
+ return this.deleteSecurityGroupIngress(
14949
+ logicalId,
14950
+ physicalId,
14951
+ resourceType,
14952
+ properties,
14953
+ context
14954
+ );
14404
14955
  case "AWS::EC2::Instance":
14405
- return this.deleteInstance(logicalId, physicalId, resourceType);
14956
+ return this.deleteInstance(logicalId, physicalId, resourceType, context);
14406
14957
  case "AWS::EC2::NetworkAcl":
14407
- return this.deleteNetworkAcl(logicalId, physicalId, resourceType);
14958
+ return this.deleteNetworkAcl(logicalId, physicalId, resourceType, context);
14408
14959
  case "AWS::EC2::NetworkAclEntry":
14409
- return this.deleteNetworkAclEntry(logicalId, physicalId, resourceType);
14960
+ return this.deleteNetworkAclEntry(logicalId, physicalId, resourceType, context);
14410
14961
  case "AWS::EC2::SubnetNetworkAclAssociation":
14411
14962
  this.logger.debug(`SubnetNetworkAclAssociation ${logicalId} delete is a no-op`);
14412
14963
  return;
@@ -14547,7 +15098,7 @@ var EC2Provider = class {
14547
15098
  );
14548
15099
  }
14549
15100
  }
14550
- async deleteVpc(logicalId, physicalId, resourceType) {
15101
+ async deleteVpc(logicalId, physicalId, resourceType, context) {
14551
15102
  this.logger.debug(`Deleting VPC ${logicalId}: ${physicalId}`);
14552
15103
  const maxAttempts = 10;
14553
15104
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -14557,6 +15108,14 @@ var EC2Provider = class {
14557
15108
  return;
14558
15109
  } catch (error) {
14559
15110
  if (this.isNotFoundError(error)) {
15111
+ const clientRegion = await this.ec2Client.config.region();
15112
+ assertRegionMatch(
15113
+ clientRegion,
15114
+ context?.expectedRegion,
15115
+ resourceType,
15116
+ logicalId,
15117
+ physicalId
15118
+ );
14560
15119
  this.logger.debug(`VPC ${physicalId} does not exist, skipping deletion`);
14561
15120
  return;
14562
15121
  }
@@ -14662,7 +15221,7 @@ var EC2Provider = class {
14662
15221
  this.logger.debug(`Updating Subnet ${logicalId}: ${physicalId} (no-op, immutable properties)`);
14663
15222
  return Promise.resolve({ physicalId, wasReplaced: false });
14664
15223
  }
14665
- async deleteSubnet(logicalId, physicalId, resourceType) {
15224
+ async deleteSubnet(logicalId, physicalId, resourceType, context) {
14666
15225
  this.logger.debug(`Deleting Subnet ${logicalId}: ${physicalId}`);
14667
15226
  const maxAttempts = 10;
14668
15227
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -14672,6 +15231,14 @@ var EC2Provider = class {
14672
15231
  return;
14673
15232
  } catch (error) {
14674
15233
  if (this.isNotFoundError(error)) {
15234
+ const clientRegion = await this.ec2Client.config.region();
15235
+ assertRegionMatch(
15236
+ clientRegion,
15237
+ context?.expectedRegion,
15238
+ resourceType,
15239
+ logicalId,
15240
+ physicalId
15241
+ );
14675
15242
  this.logger.debug(`Subnet ${physicalId} does not exist, skipping deletion`);
14676
15243
  return;
14677
15244
  }
@@ -14790,7 +15357,7 @@ var EC2Provider = class {
14790
15357
  this.logger.debug(`Updating InternetGateway ${logicalId}: ${physicalId} (no-op)`);
14791
15358
  return Promise.resolve({ physicalId, wasReplaced: false });
14792
15359
  }
14793
- async deleteInternetGateway(logicalId, physicalId, resourceType) {
15360
+ async deleteInternetGateway(logicalId, physicalId, resourceType, context) {
14794
15361
  this.logger.debug(`Deleting InternetGateway ${logicalId}: ${physicalId}`);
14795
15362
  try {
14796
15363
  await this.ec2Client.send(
@@ -14799,6 +15366,14 @@ var EC2Provider = class {
14799
15366
  this.logger.debug(`Successfully deleted InternetGateway ${logicalId}`);
14800
15367
  } catch (error) {
14801
15368
  if (this.isNotFoundError(error)) {
15369
+ const clientRegion = await this.ec2Client.config.region();
15370
+ assertRegionMatch(
15371
+ clientRegion,
15372
+ context?.expectedRegion,
15373
+ resourceType,
15374
+ logicalId,
15375
+ physicalId
15376
+ );
14802
15377
  this.logger.debug(`InternetGateway ${physicalId} does not exist, skipping deletion`);
14803
15378
  return;
14804
15379
  }
@@ -14852,7 +15427,7 @@ var EC2Provider = class {
14852
15427
  this.logger.debug(`Updating VPCGatewayAttachment ${logicalId}: ${physicalId} (no-op)`);
14853
15428
  return Promise.resolve({ physicalId, wasReplaced: false });
14854
15429
  }
14855
- async deleteVpcGatewayAttachment(logicalId, physicalId, resourceType) {
15430
+ async deleteVpcGatewayAttachment(logicalId, physicalId, resourceType, context) {
14856
15431
  this.logger.debug(`Deleting VPCGatewayAttachment ${logicalId}: ${physicalId}`);
14857
15432
  const parts = physicalId.split("|");
14858
15433
  if (parts.length !== 2) {
@@ -14874,6 +15449,14 @@ var EC2Provider = class {
14874
15449
  this.logger.debug(`Successfully deleted VPCGatewayAttachment ${logicalId}`);
14875
15450
  } catch (error) {
14876
15451
  if (this.isNotFoundError(error)) {
15452
+ const clientRegion = await this.ec2Client.config.region();
15453
+ assertRegionMatch(
15454
+ clientRegion,
15455
+ context?.expectedRegion,
15456
+ resourceType,
15457
+ logicalId,
15458
+ physicalId
15459
+ );
14877
15460
  this.logger.debug(`VPCGatewayAttachment ${physicalId} does not exist, skipping deletion`);
14878
15461
  return;
14879
15462
  }
@@ -14924,13 +15507,21 @@ var EC2Provider = class {
14924
15507
  this.logger.debug(`Updating RouteTable ${logicalId}: ${physicalId} (no-op)`);
14925
15508
  return Promise.resolve({ physicalId, wasReplaced: false });
14926
15509
  }
14927
- async deleteRouteTable(logicalId, physicalId, resourceType) {
15510
+ async deleteRouteTable(logicalId, physicalId, resourceType, context) {
14928
15511
  this.logger.debug(`Deleting RouteTable ${logicalId}: ${physicalId}`);
14929
15512
  try {
14930
15513
  await this.ec2Client.send(new DeleteRouteTableCommand({ RouteTableId: physicalId }));
14931
15514
  this.logger.debug(`Successfully deleted RouteTable ${logicalId}`);
14932
15515
  } catch (error) {
14933
15516
  if (this.isNotFoundError(error)) {
15517
+ const clientRegion = await this.ec2Client.config.region();
15518
+ assertRegionMatch(
15519
+ clientRegion,
15520
+ context?.expectedRegion,
15521
+ resourceType,
15522
+ logicalId,
15523
+ physicalId
15524
+ );
14934
15525
  this.logger.debug(`RouteTable ${physicalId} does not exist, skipping deletion`);
14935
15526
  return;
14936
15527
  }
@@ -15010,7 +15601,7 @@ var EC2Provider = class {
15010
15601
  );
15011
15602
  }
15012
15603
  }
15013
- async deleteRoute(logicalId, physicalId, resourceType) {
15604
+ async deleteRoute(logicalId, physicalId, resourceType, context) {
15014
15605
  this.logger.debug(`Deleting Route ${logicalId}: ${physicalId}`);
15015
15606
  const parts = physicalId.split("|");
15016
15607
  if (parts.length !== 2) {
@@ -15033,6 +15624,14 @@ var EC2Provider = class {
15033
15624
  this.logger.debug(`Successfully deleted Route ${logicalId}`);
15034
15625
  } catch (error) {
15035
15626
  if (this.isNotFoundError(error)) {
15627
+ const clientRegion = await this.ec2Client.config.region();
15628
+ assertRegionMatch(
15629
+ clientRegion,
15630
+ context?.expectedRegion,
15631
+ resourceType,
15632
+ logicalId,
15633
+ physicalId
15634
+ );
15036
15635
  this.logger.debug(`Route ${physicalId} does not exist, skipping deletion`);
15037
15636
  return;
15038
15637
  }
@@ -15090,13 +15689,21 @@ var EC2Provider = class {
15090
15689
  );
15091
15690
  return Promise.resolve({ physicalId, wasReplaced: false });
15092
15691
  }
15093
- async deleteSubnetRouteTableAssociation(logicalId, physicalId, resourceType) {
15692
+ async deleteSubnetRouteTableAssociation(logicalId, physicalId, resourceType, context) {
15094
15693
  this.logger.debug(`Deleting SubnetRouteTableAssociation ${logicalId}: ${physicalId}`);
15095
15694
  try {
15096
15695
  await this.ec2Client.send(new DisassociateRouteTableCommand({ AssociationId: physicalId }));
15097
15696
  this.logger.debug(`Successfully deleted SubnetRouteTableAssociation ${logicalId}`);
15098
15697
  } catch (error) {
15099
15698
  if (this.isNotFoundError(error)) {
15699
+ const clientRegion = await this.ec2Client.config.region();
15700
+ assertRegionMatch(
15701
+ clientRegion,
15702
+ context?.expectedRegion,
15703
+ resourceType,
15704
+ logicalId,
15705
+ physicalId
15706
+ );
15100
15707
  this.logger.debug(
15101
15708
  `SubnetRouteTableAssociation ${physicalId} does not exist, skipping deletion`
15102
15709
  );
@@ -15227,7 +15834,7 @@ var EC2Provider = class {
15227
15834
  );
15228
15835
  }
15229
15836
  }
15230
- async deleteSecurityGroup(logicalId, physicalId, resourceType) {
15837
+ async deleteSecurityGroup(logicalId, physicalId, resourceType, context) {
15231
15838
  this.logger.debug(`Deleting SecurityGroup ${logicalId}: ${physicalId}`);
15232
15839
  const maxAttempts = 10;
15233
15840
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -15237,6 +15844,14 @@ var EC2Provider = class {
15237
15844
  return;
15238
15845
  } catch (error) {
15239
15846
  if (this.isNotFoundError(error)) {
15847
+ const clientRegion = await this.ec2Client.config.region();
15848
+ assertRegionMatch(
15849
+ clientRegion,
15850
+ context?.expectedRegion,
15851
+ resourceType,
15852
+ logicalId,
15853
+ physicalId
15854
+ );
15240
15855
  this.logger.debug(`SecurityGroup ${physicalId} does not exist, skipping deletion`);
15241
15856
  return;
15242
15857
  }
@@ -15394,7 +16009,7 @@ var EC2Provider = class {
15394
16009
  );
15395
16010
  }
15396
16011
  }
15397
- async deleteSecurityGroupIngress(logicalId, physicalId, resourceType, properties) {
16012
+ async deleteSecurityGroupIngress(logicalId, physicalId, resourceType, properties, context) {
15398
16013
  this.logger.debug(`Deleting SecurityGroupIngress ${logicalId}: ${physicalId}`);
15399
16014
  const parts = physicalId.split("|");
15400
16015
  if (parts.length !== 4) {
@@ -15421,6 +16036,14 @@ var EC2Provider = class {
15421
16036
  this.logger.debug(`Successfully deleted SecurityGroupIngress ${logicalId}`);
15422
16037
  } catch (error) {
15423
16038
  if (this.isNotFoundError(error)) {
16039
+ const clientRegion = await this.ec2Client.config.region();
16040
+ assertRegionMatch(
16041
+ clientRegion,
16042
+ context?.expectedRegion,
16043
+ resourceType,
16044
+ logicalId,
16045
+ physicalId
16046
+ );
15424
16047
  this.logger.debug(`SecurityGroupIngress ${physicalId} does not exist, skipping deletion`);
15425
16048
  return;
15426
16049
  }
@@ -15537,7 +16160,7 @@ var EC2Provider = class {
15537
16160
  );
15538
16161
  }
15539
16162
  }
15540
- async deleteInstance(logicalId, physicalId, resourceType) {
16163
+ async deleteInstance(logicalId, physicalId, resourceType, context) {
15541
16164
  this.logger.debug(`Terminating EC2 Instance ${logicalId}: ${physicalId}`);
15542
16165
  try {
15543
16166
  await this.ec2Client.send(new TerminateInstancesCommand({ InstanceIds: [physicalId] }));
@@ -15549,6 +16172,14 @@ var EC2Provider = class {
15549
16172
  this.logger.debug(`EC2 Instance ${logicalId} terminated: ${physicalId}`);
15550
16173
  } catch (error) {
15551
16174
  if (this.isNotFoundError(error)) {
16175
+ const clientRegion = await this.ec2Client.config.region();
16176
+ assertRegionMatch(
16177
+ clientRegion,
16178
+ context?.expectedRegion,
16179
+ resourceType,
16180
+ logicalId,
16181
+ physicalId
16182
+ );
15552
16183
  this.logger.debug(
15553
16184
  `EC2 Instance ${physicalId} already terminated (not found), treating as success`
15554
16185
  );
@@ -15784,13 +16415,21 @@ var EC2Provider = class {
15784
16415
  );
15785
16416
  }
15786
16417
  }
15787
- async deleteNetworkAcl(logicalId, physicalId, resourceType) {
16418
+ async deleteNetworkAcl(logicalId, physicalId, resourceType, context) {
15788
16419
  this.logger.debug(`Deleting NetworkAcl ${logicalId}: ${physicalId}`);
15789
16420
  try {
15790
16421
  await this.ec2Client.send(new DeleteNetworkAclCommand({ NetworkAclId: physicalId }));
15791
16422
  this.logger.debug(`Successfully deleted NetworkAcl ${logicalId}`);
15792
16423
  } catch (error) {
15793
16424
  if (this.isNotFoundError(error)) {
16425
+ const clientRegion = await this.ec2Client.config.region();
16426
+ assertRegionMatch(
16427
+ clientRegion,
16428
+ context?.expectedRegion,
16429
+ resourceType,
16430
+ logicalId,
16431
+ physicalId
16432
+ );
15794
16433
  this.logger.debug(`NetworkAcl ${physicalId} does not exist, skipping deletion`);
15795
16434
  return;
15796
16435
  }
@@ -15860,7 +16499,7 @@ var EC2Provider = class {
15860
16499
  );
15861
16500
  }
15862
16501
  }
15863
- async deleteNetworkAclEntry(logicalId, physicalId, resourceType) {
16502
+ async deleteNetworkAclEntry(logicalId, physicalId, resourceType, context) {
15864
16503
  this.logger.debug(`Deleting NetworkAclEntry ${logicalId}: ${physicalId}`);
15865
16504
  const parts = physicalId.split("|");
15866
16505
  if (parts.length < 3) {
@@ -15881,6 +16520,14 @@ var EC2Provider = class {
15881
16520
  this.logger.debug(`Successfully deleted NetworkAclEntry ${logicalId}`);
15882
16521
  } catch (error) {
15883
16522
  if (this.isNotFoundError(error)) {
16523
+ const clientRegion = await this.ec2Client.config.region();
16524
+ assertRegionMatch(
16525
+ clientRegion,
16526
+ context?.expectedRegion,
16527
+ resourceType,
16528
+ logicalId,
16529
+ physicalId
16530
+ );
15884
16531
  this.logger.debug(`NetworkAclEntry ${physicalId} does not exist, skipping deletion`);
15885
16532
  return;
15886
16533
  }
@@ -16123,20 +16770,20 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16123
16770
  /**
16124
16771
  * Delete a resource
16125
16772
  */
16126
- async delete(logicalId, physicalId, resourceType, properties) {
16773
+ async delete(logicalId, physicalId, resourceType, properties, context) {
16127
16774
  switch (resourceType) {
16128
16775
  case "AWS::ApiGateway::Account":
16129
16776
  return this.deleteAccount(logicalId, physicalId, resourceType);
16130
16777
  case "AWS::ApiGateway::Authorizer":
16131
- return this.deleteAuthorizer(logicalId, physicalId, resourceType, properties);
16778
+ return this.deleteAuthorizer(logicalId, physicalId, resourceType, properties, context);
16132
16779
  case "AWS::ApiGateway::Resource":
16133
- return this.deleteResource(logicalId, physicalId, resourceType, properties);
16780
+ return this.deleteResource(logicalId, physicalId, resourceType, properties, context);
16134
16781
  case "AWS::ApiGateway::Deployment":
16135
- return this.deleteDeployment(logicalId, physicalId, resourceType, properties);
16782
+ return this.deleteDeployment(logicalId, physicalId, resourceType, properties, context);
16136
16783
  case "AWS::ApiGateway::Stage":
16137
- return this.deleteStage(logicalId, physicalId, resourceType, properties);
16784
+ return this.deleteStage(logicalId, physicalId, resourceType, properties, context);
16138
16785
  case "AWS::ApiGateway::Method":
16139
- return this.deleteMethod(logicalId, physicalId, resourceType);
16786
+ return this.deleteMethod(logicalId, physicalId, resourceType, context);
16140
16787
  default:
16141
16788
  throw new ProvisioningError(
16142
16789
  `Unsupported resource type: ${resourceType}`,
@@ -16358,7 +17005,7 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16358
17005
  /**
16359
17006
  * Delete an API Gateway Authorizer
16360
17007
  */
16361
- async deleteAuthorizer(logicalId, physicalId, resourceType, properties) {
17008
+ async deleteAuthorizer(logicalId, physicalId, resourceType, properties, context) {
16362
17009
  this.logger.debug(`Deleting API Gateway Authorizer ${logicalId}: ${physicalId}`);
16363
17010
  const restApiId = properties?.["RestApiId"];
16364
17011
  if (!restApiId) {
@@ -16379,6 +17026,14 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16379
17026
  this.logger.debug(`Successfully deleted API Gateway Authorizer ${logicalId}`);
16380
17027
  } catch (error) {
16381
17028
  if (error instanceof NotFoundException3) {
17029
+ const clientRegion = await this.apiGatewayClient.config.region();
17030
+ assertRegionMatch(
17031
+ clientRegion,
17032
+ context?.expectedRegion,
17033
+ resourceType,
17034
+ logicalId,
17035
+ physicalId
17036
+ );
16382
17037
  this.logger.debug(`API Gateway Authorizer ${physicalId} does not exist, skipping deletion`);
16383
17038
  return;
16384
17039
  }
@@ -16483,7 +17138,7 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16483
17138
  /**
16484
17139
  * Delete an API Gateway Resource
16485
17140
  */
16486
- async deleteResource(logicalId, physicalId, resourceType, properties) {
17141
+ async deleteResource(logicalId, physicalId, resourceType, properties, context) {
16487
17142
  this.logger.debug(`Deleting API Gateway Resource ${logicalId}: ${physicalId}`);
16488
17143
  const restApiId = properties?.["RestApiId"];
16489
17144
  if (!restApiId) {
@@ -16504,6 +17159,14 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16504
17159
  this.logger.debug(`Successfully deleted API Gateway Resource ${logicalId}`);
16505
17160
  } catch (error) {
16506
17161
  if (error instanceof NotFoundException3) {
17162
+ const clientRegion = await this.apiGatewayClient.config.region();
17163
+ assertRegionMatch(
17164
+ clientRegion,
17165
+ context?.expectedRegion,
17166
+ resourceType,
17167
+ logicalId,
17168
+ physicalId
17169
+ );
16507
17170
  this.logger.debug(`API Gateway Resource ${physicalId} does not exist, skipping deletion`);
16508
17171
  return;
16509
17172
  }
@@ -16587,7 +17250,7 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16587
17250
  /**
16588
17251
  * Delete an API Gateway Deployment
16589
17252
  */
16590
- async deleteDeployment(logicalId, physicalId, resourceType, properties) {
17253
+ async deleteDeployment(logicalId, physicalId, resourceType, properties, context) {
16591
17254
  this.logger.debug(`Deleting API Gateway Deployment ${logicalId}: ${physicalId}`);
16592
17255
  const restApiId = properties?.["RestApiId"];
16593
17256
  if (!restApiId) {
@@ -16608,6 +17271,14 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16608
17271
  this.logger.debug(`Successfully deleted API Gateway Deployment ${logicalId}`);
16609
17272
  } catch (error) {
16610
17273
  if (error instanceof NotFoundException3) {
17274
+ const clientRegion = await this.apiGatewayClient.config.region();
17275
+ assertRegionMatch(
17276
+ clientRegion,
17277
+ context?.expectedRegion,
17278
+ resourceType,
17279
+ logicalId,
17280
+ physicalId
17281
+ );
16611
17282
  this.logger.debug(`API Gateway Deployment ${physicalId} does not exist, skipping deletion`);
16612
17283
  return;
16613
17284
  }
@@ -16745,7 +17416,7 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16745
17416
  /**
16746
17417
  * Delete an API Gateway Stage
16747
17418
  */
16748
- async deleteStage(logicalId, physicalId, resourceType, properties) {
17419
+ async deleteStage(logicalId, physicalId, resourceType, properties, context) {
16749
17420
  this.logger.debug(`Deleting API Gateway Stage ${logicalId}: ${physicalId}`);
16750
17421
  const restApiId = properties?.["RestApiId"];
16751
17422
  if (!restApiId) {
@@ -16766,6 +17437,14 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16766
17437
  this.logger.debug(`Successfully deleted API Gateway Stage ${logicalId}`);
16767
17438
  } catch (error) {
16768
17439
  if (error instanceof NotFoundException3) {
17440
+ const clientRegion = await this.apiGatewayClient.config.region();
17441
+ assertRegionMatch(
17442
+ clientRegion,
17443
+ context?.expectedRegion,
17444
+ resourceType,
17445
+ logicalId,
17446
+ physicalId
17447
+ );
16769
17448
  this.logger.debug(`API Gateway Stage ${physicalId} does not exist, skipping deletion`);
16770
17449
  return;
16771
17450
  }
@@ -16885,7 +17564,7 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16885
17564
  * Parses the composite physicalId (`restApiId|resourceId|httpMethod`) and
16886
17565
  * calls DeleteMethodCommand. Handles NotFoundException gracefully.
16887
17566
  */
16888
- async deleteMethod(logicalId, physicalId, resourceType) {
17567
+ async deleteMethod(logicalId, physicalId, resourceType, context) {
16889
17568
  this.logger.debug(`Deleting API Gateway Method ${logicalId}: ${physicalId}`);
16890
17569
  const parts = physicalId.split("|");
16891
17570
  if (parts.length !== 3) {
@@ -16908,6 +17587,14 @@ var ApiGatewayProvider = class _ApiGatewayProvider {
16908
17587
  this.logger.debug(`Successfully deleted API Gateway Method ${logicalId}`);
16909
17588
  } catch (error) {
16910
17589
  if (error instanceof NotFoundException3) {
17590
+ const clientRegion = await this.apiGatewayClient.config.region();
17591
+ assertRegionMatch(
17592
+ clientRegion,
17593
+ context?.expectedRegion,
17594
+ resourceType,
17595
+ logicalId,
17596
+ physicalId
17597
+ );
16911
17598
  this.logger.debug(`API Gateway Method ${physicalId} does not exist, skipping deletion`);
16912
17599
  return;
16913
17600
  }
@@ -17048,18 +17735,18 @@ var ApiGatewayV2Provider = class {
17048
17735
  attributes: {}
17049
17736
  });
17050
17737
  }
17051
- async delete(logicalId, physicalId, resourceType, properties) {
17738
+ async delete(logicalId, physicalId, resourceType, properties, context) {
17052
17739
  switch (resourceType) {
17053
17740
  case "AWS::ApiGatewayV2::Api":
17054
- return this.deleteApi(logicalId, physicalId, resourceType);
17741
+ return this.deleteApi(logicalId, physicalId, resourceType, context);
17055
17742
  case "AWS::ApiGatewayV2::Stage":
17056
- return this.deleteStage(logicalId, physicalId, resourceType, properties);
17743
+ return this.deleteStage(logicalId, physicalId, resourceType, properties, context);
17057
17744
  case "AWS::ApiGatewayV2::Integration":
17058
- return this.deleteIntegration(logicalId, physicalId, resourceType, properties);
17745
+ return this.deleteIntegration(logicalId, physicalId, resourceType, properties, context);
17059
17746
  case "AWS::ApiGatewayV2::Route":
17060
- return this.deleteRoute(logicalId, physicalId, resourceType, properties);
17747
+ return this.deleteRoute(logicalId, physicalId, resourceType, properties, context);
17061
17748
  case "AWS::ApiGatewayV2::Authorizer":
17062
- return this.deleteAuthorizer(logicalId, physicalId, resourceType, properties);
17749
+ return this.deleteAuthorizer(logicalId, physicalId, resourceType, properties, context);
17063
17750
  default:
17064
17751
  throw new ProvisioningError(
17065
17752
  `Unsupported resource type: ${resourceType}`,
@@ -17130,13 +17817,21 @@ var ApiGatewayV2Provider = class {
17130
17817
  );
17131
17818
  }
17132
17819
  }
17133
- async deleteApi(logicalId, physicalId, resourceType) {
17820
+ async deleteApi(logicalId, physicalId, resourceType, context) {
17134
17821
  this.logger.debug(`Deleting API Gateway V2 Api ${logicalId}: ${physicalId}`);
17135
17822
  try {
17136
17823
  await this.getClient().send(new DeleteApiCommand({ ApiId: physicalId }));
17137
17824
  this.logger.debug(`Successfully deleted API Gateway V2 Api ${logicalId}`);
17138
17825
  } catch (error) {
17139
17826
  if (error instanceof NotFoundException4) {
17827
+ const clientRegion = await this.getClient().config.region();
17828
+ assertRegionMatch(
17829
+ clientRegion,
17830
+ context?.expectedRegion,
17831
+ resourceType,
17832
+ logicalId,
17833
+ physicalId
17834
+ );
17140
17835
  this.logger.debug(`API Gateway V2 Api ${physicalId} does not exist, skipping deletion`);
17141
17836
  return;
17142
17837
  }
@@ -17193,7 +17888,7 @@ var ApiGatewayV2Provider = class {
17193
17888
  );
17194
17889
  }
17195
17890
  }
17196
- async deleteStage(logicalId, physicalId, resourceType, properties) {
17891
+ async deleteStage(logicalId, physicalId, resourceType, properties, context) {
17197
17892
  this.logger.debug(`Deleting API Gateway V2 Stage ${logicalId}: ${physicalId}`);
17198
17893
  const apiId = properties?.["ApiId"];
17199
17894
  if (!apiId) {
@@ -17209,6 +17904,14 @@ var ApiGatewayV2Provider = class {
17209
17904
  this.logger.debug(`Successfully deleted API Gateway V2 Stage ${logicalId}`);
17210
17905
  } catch (error) {
17211
17906
  if (error instanceof NotFoundException4) {
17907
+ const clientRegion = await this.getClient().config.region();
17908
+ assertRegionMatch(
17909
+ clientRegion,
17910
+ context?.expectedRegion,
17911
+ resourceType,
17912
+ logicalId,
17913
+ physicalId
17914
+ );
17212
17915
  this.logger.debug(`API Gateway V2 Stage ${physicalId} does not exist, skipping deletion`);
17213
17916
  return;
17214
17917
  }
@@ -17270,7 +17973,7 @@ var ApiGatewayV2Provider = class {
17270
17973
  );
17271
17974
  }
17272
17975
  }
17273
- async deleteIntegration(logicalId, physicalId, resourceType, properties) {
17976
+ async deleteIntegration(logicalId, physicalId, resourceType, properties, context) {
17274
17977
  this.logger.debug(`Deleting API Gateway V2 Integration ${logicalId}: ${physicalId}`);
17275
17978
  const apiId = properties?.["ApiId"];
17276
17979
  if (!apiId) {
@@ -17288,6 +17991,14 @@ var ApiGatewayV2Provider = class {
17288
17991
  this.logger.debug(`Successfully deleted API Gateway V2 Integration ${logicalId}`);
17289
17992
  } catch (error) {
17290
17993
  if (error instanceof NotFoundException4) {
17994
+ const clientRegion = await this.getClient().config.region();
17995
+ assertRegionMatch(
17996
+ clientRegion,
17997
+ context?.expectedRegion,
17998
+ resourceType,
17999
+ logicalId,
18000
+ physicalId
18001
+ );
17291
18002
  this.logger.debug(
17292
18003
  `API Gateway V2 Integration ${physicalId} does not exist, skipping deletion`
17293
18004
  );
@@ -17349,7 +18060,7 @@ var ApiGatewayV2Provider = class {
17349
18060
  );
17350
18061
  }
17351
18062
  }
17352
- async deleteRoute(logicalId, physicalId, resourceType, properties) {
18063
+ async deleteRoute(logicalId, physicalId, resourceType, properties, context) {
17353
18064
  this.logger.debug(`Deleting API Gateway V2 Route ${logicalId}: ${physicalId}`);
17354
18065
  const apiId = properties?.["ApiId"];
17355
18066
  if (!apiId) {
@@ -17365,6 +18076,14 @@ var ApiGatewayV2Provider = class {
17365
18076
  this.logger.debug(`Successfully deleted API Gateway V2 Route ${logicalId}`);
17366
18077
  } catch (error) {
17367
18078
  if (error instanceof NotFoundException4) {
18079
+ const clientRegion = await this.getClient().config.region();
18080
+ assertRegionMatch(
18081
+ clientRegion,
18082
+ context?.expectedRegion,
18083
+ resourceType,
18084
+ logicalId,
18085
+ physicalId
18086
+ );
17368
18087
  this.logger.debug(`API Gateway V2 Route ${physicalId} does not exist, skipping deletion`);
17369
18088
  return;
17370
18089
  }
@@ -17429,7 +18148,7 @@ var ApiGatewayV2Provider = class {
17429
18148
  );
17430
18149
  }
17431
18150
  }
17432
- async deleteAuthorizer(logicalId, physicalId, resourceType, properties) {
18151
+ async deleteAuthorizer(logicalId, physicalId, resourceType, properties, context) {
17433
18152
  this.logger.debug(`Deleting API Gateway V2 Authorizer ${logicalId}: ${physicalId}`);
17434
18153
  const apiId = properties?.["ApiId"];
17435
18154
  if (!apiId) {
@@ -17447,6 +18166,14 @@ var ApiGatewayV2Provider = class {
17447
18166
  this.logger.debug(`Successfully deleted API Gateway V2 Authorizer ${logicalId}`);
17448
18167
  } catch (error) {
17449
18168
  if (error instanceof NotFoundException4) {
18169
+ const clientRegion = await this.getClient().config.region();
18170
+ assertRegionMatch(
18171
+ clientRegion,
18172
+ context?.expectedRegion,
18173
+ resourceType,
18174
+ logicalId,
18175
+ physicalId
18176
+ );
17450
18177
  this.logger.debug(
17451
18178
  `API Gateway V2 Authorizer ${physicalId} does not exist, skipping deletion`
17452
18179
  );
@@ -17554,7 +18281,7 @@ var CloudFrontOAIProvider = class {
17554
18281
  *
17555
18282
  * Requires fetching the ETag first, then passing it as IfMatch for deletion.
17556
18283
  */
17557
- async delete(logicalId, physicalId, resourceType, _properties) {
18284
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
17558
18285
  this.logger.debug(`Deleting CloudFront OAI ${logicalId}: ${physicalId}`);
17559
18286
  try {
17560
18287
  let etag;
@@ -17565,6 +18292,14 @@ var CloudFrontOAIProvider = class {
17565
18292
  etag = getResponse.ETag;
17566
18293
  } catch (error) {
17567
18294
  if (error instanceof NoSuchCloudFrontOriginAccessIdentity) {
18295
+ const clientRegion = await this.cloudFrontClient.config.region();
18296
+ assertRegionMatch(
18297
+ clientRegion,
18298
+ context?.expectedRegion,
18299
+ resourceType,
18300
+ logicalId,
18301
+ physicalId
18302
+ );
17568
18303
  this.logger.debug(`OAI ${physicalId} does not exist, skipping deletion`);
17569
18304
  return;
17570
18305
  }
@@ -17579,6 +18314,14 @@ var CloudFrontOAIProvider = class {
17579
18314
  this.logger.debug(`Successfully deleted CloudFront OAI ${logicalId}`);
17580
18315
  } catch (error) {
17581
18316
  if (error instanceof NoSuchCloudFrontOriginAccessIdentity) {
18317
+ const clientRegion = await this.cloudFrontClient.config.region();
18318
+ assertRegionMatch(
18319
+ clientRegion,
18320
+ context?.expectedRegion,
18321
+ resourceType,
18322
+ logicalId,
18323
+ physicalId
18324
+ );
17582
18325
  this.logger.debug(`OAI ${physicalId} does not exist, skipping deletion`);
17583
18326
  return;
17584
18327
  }
@@ -17752,7 +18495,7 @@ var CloudFrontDistributionProvider = class {
17752
18495
  * 2. If Enabled, update to Enabled=false and wait
17753
18496
  * 3. Delete with the latest ETag
17754
18497
  */
17755
- async delete(logicalId, physicalId, resourceType, _properties) {
18498
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
17756
18499
  this.logger.debug(`Deleting CloudFront Distribution ${logicalId}: ${physicalId}`);
17757
18500
  try {
17758
18501
  let etag;
@@ -17765,6 +18508,14 @@ var CloudFrontDistributionProvider = class {
17765
18508
  config = getConfigResponse.DistributionConfig;
17766
18509
  } catch (error) {
17767
18510
  if (error instanceof NoSuchDistribution) {
18511
+ const clientRegion = await this.cloudFrontClient.config.region();
18512
+ assertRegionMatch(
18513
+ clientRegion,
18514
+ context?.expectedRegion,
18515
+ resourceType,
18516
+ logicalId,
18517
+ physicalId
18518
+ );
17768
18519
  this.logger.debug(`Distribution ${physicalId} does not exist, skipping deletion`);
17769
18520
  return;
17770
18521
  }
@@ -17796,6 +18547,14 @@ var CloudFrontDistributionProvider = class {
17796
18547
  this.logger.debug(`Successfully deleted CloudFront Distribution ${logicalId}`);
17797
18548
  } catch (error) {
17798
18549
  if (error instanceof NoSuchDistribution) {
18550
+ const clientRegion = await this.cloudFrontClient.config.region();
18551
+ assertRegionMatch(
18552
+ clientRegion,
18553
+ context?.expectedRegion,
18554
+ resourceType,
18555
+ logicalId,
18556
+ physicalId
18557
+ );
17799
18558
  this.logger.debug(`Distribution ${physicalId} does not exist, skipping deletion`);
17800
18559
  return;
17801
18560
  }
@@ -18215,7 +18974,7 @@ var AgentCoreRuntimeProvider = class {
18215
18974
  /**
18216
18975
  * Delete a BedrockAgentCore Runtime
18217
18976
  */
18218
- async delete(logicalId, physicalId, resourceType, _properties) {
18977
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
18219
18978
  this.logger.debug(`Deleting BedrockAgentCore Runtime ${logicalId}: ${physicalId}`);
18220
18979
  try {
18221
18980
  await this.client.send(
@@ -18226,6 +18985,14 @@ var AgentCoreRuntimeProvider = class {
18226
18985
  this.logger.debug(`Successfully deleted BedrockAgentCore Runtime ${logicalId}`);
18227
18986
  } catch (error) {
18228
18987
  if (error instanceof ResourceNotFoundException11) {
18988
+ const clientRegion = await this.client.config.region();
18989
+ assertRegionMatch(
18990
+ clientRegion,
18991
+ context?.expectedRegion,
18992
+ resourceType,
18993
+ logicalId,
18994
+ physicalId
18995
+ );
18229
18996
  this.logger.debug(`Runtime ${physicalId} does not exist, skipping deletion`);
18230
18997
  return;
18231
18998
  }
@@ -18421,13 +19188,21 @@ var StepFunctionsProvider = class {
18421
19188
  /**
18422
19189
  * Delete a Step Functions state machine
18423
19190
  */
18424
- async delete(logicalId, physicalId, resourceType, _properties) {
19191
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
18425
19192
  this.logger.debug(`Deleting Step Functions state machine ${logicalId}: ${physicalId}`);
18426
19193
  try {
18427
19194
  await this.getClient().send(new DeleteStateMachineCommand({ stateMachineArn: physicalId }));
18428
19195
  this.logger.debug(`Successfully deleted Step Functions state machine ${logicalId}`);
18429
19196
  } catch (error) {
18430
19197
  if (error instanceof StateMachineDoesNotExist) {
19198
+ const clientRegion = await this.getClient().config.region();
19199
+ assertRegionMatch(
19200
+ clientRegion,
19201
+ context?.expectedRegion,
19202
+ resourceType,
19203
+ logicalId,
19204
+ physicalId
19205
+ );
18431
19206
  this.logger.debug(
18432
19207
  `Step Functions state machine ${physicalId} does not exist, skipping deletion`
18433
19208
  );
@@ -18594,14 +19369,14 @@ var ECSProvider = class {
18594
19369
  );
18595
19370
  }
18596
19371
  }
18597
- async delete(logicalId, physicalId, resourceType, properties) {
19372
+ async delete(logicalId, physicalId, resourceType, properties, context) {
18598
19373
  switch (resourceType) {
18599
19374
  case "AWS::ECS::Cluster":
18600
- return this.deleteCluster(logicalId, physicalId, resourceType);
19375
+ return this.deleteCluster(logicalId, physicalId, resourceType, context);
18601
19376
  case "AWS::ECS::TaskDefinition":
18602
- return this.deleteTaskDefinition(logicalId, physicalId, resourceType);
19377
+ return this.deleteTaskDefinition(logicalId, physicalId, resourceType, context);
18603
19378
  case "AWS::ECS::Service":
18604
- return this.deleteService(logicalId, physicalId, resourceType, properties);
19379
+ return this.deleteService(logicalId, physicalId, resourceType, properties, context);
18605
19380
  default:
18606
19381
  throw new ProvisioningError(
18607
19382
  `Unsupported resource type: ${resourceType}`,
@@ -18702,7 +19477,7 @@ var ECSProvider = class {
18702
19477
  );
18703
19478
  }
18704
19479
  }
18705
- async deleteCluster(logicalId, physicalId, resourceType) {
19480
+ async deleteCluster(logicalId, physicalId, resourceType, context) {
18706
19481
  this.logger.debug(`Deleting ECS cluster ${logicalId}: ${physicalId}`);
18707
19482
  const client = this.getClient();
18708
19483
  try {
@@ -18710,6 +19485,14 @@ var ECSProvider = class {
18710
19485
  this.logger.debug(`Successfully deleted ECS cluster ${logicalId}`);
18711
19486
  } catch (error) {
18712
19487
  if (this.isClusterNotFoundException(error)) {
19488
+ const clientRegion = await client.config.region();
19489
+ assertRegionMatch(
19490
+ clientRegion,
19491
+ context?.expectedRegion,
19492
+ resourceType,
19493
+ logicalId,
19494
+ physicalId
19495
+ );
18713
19496
  this.logger.debug(`ECS cluster ${physicalId} not found, skipping deletion`);
18714
19497
  return;
18715
19498
  }
@@ -18809,7 +19592,7 @@ var ECSProvider = class {
18809
19592
  attributes: result.attributes ?? {}
18810
19593
  };
18811
19594
  }
18812
- async deleteTaskDefinition(logicalId, physicalId, resourceType) {
19595
+ async deleteTaskDefinition(logicalId, physicalId, resourceType, context) {
18813
19596
  this.logger.debug(`Deleting ECS task definition ${logicalId}: ${physicalId}`);
18814
19597
  const client = this.getClient();
18815
19598
  try {
@@ -18817,6 +19600,14 @@ var ECSProvider = class {
18817
19600
  this.logger.debug(`Successfully deregistered ECS task definition ${logicalId}`);
18818
19601
  } catch (error) {
18819
19602
  if (this.isNotFoundException(error)) {
19603
+ const clientRegion = await client.config.region();
19604
+ assertRegionMatch(
19605
+ clientRegion,
19606
+ context?.expectedRegion,
19607
+ resourceType,
19608
+ logicalId,
19609
+ physicalId
19610
+ );
18820
19611
  this.logger.debug(`ECS task definition ${physicalId} not found, skipping deletion`);
18821
19612
  return;
18822
19613
  }
@@ -18955,7 +19746,7 @@ var ECSProvider = class {
18955
19746
  );
18956
19747
  }
18957
19748
  }
18958
- async deleteService(logicalId, physicalId, resourceType, properties) {
19749
+ async deleteService(logicalId, physicalId, resourceType, properties, context) {
18959
19750
  this.logger.debug(`Deleting ECS service ${logicalId}: ${physicalId}`);
18960
19751
  const client = this.getClient();
18961
19752
  const cluster = properties?.["Cluster"];
@@ -18971,6 +19762,14 @@ var ECSProvider = class {
18971
19762
  this.logger.debug(`Scaled down ECS service ${physicalId} to 0`);
18972
19763
  } catch (error) {
18973
19764
  if (this.isServiceNotFoundException(error)) {
19765
+ const clientRegion = await client.config.region();
19766
+ assertRegionMatch(
19767
+ clientRegion,
19768
+ context?.expectedRegion,
19769
+ resourceType,
19770
+ logicalId,
19771
+ physicalId
19772
+ );
18974
19773
  this.logger.debug(
18975
19774
  `ECS service ${physicalId} not found during scale down, skipping deletion`
18976
19775
  );
@@ -18988,6 +19787,14 @@ var ECSProvider = class {
18988
19787
  this.logger.debug(`Successfully deleted ECS service ${logicalId}`);
18989
19788
  } catch (error) {
18990
19789
  if (this.isServiceNotFoundException(error)) {
19790
+ const clientRegion = await client.config.region();
19791
+ assertRegionMatch(
19792
+ clientRegion,
19793
+ context?.expectedRegion,
19794
+ resourceType,
19795
+ logicalId,
19796
+ physicalId
19797
+ );
18991
19798
  this.logger.debug(`ECS service ${physicalId} not found, skipping deletion`);
18992
19799
  return;
18993
19800
  }
@@ -19286,14 +20093,14 @@ var ELBv2Provider = class {
19286
20093
  );
19287
20094
  }
19288
20095
  }
19289
- async delete(logicalId, physicalId, resourceType, _properties) {
20096
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
19290
20097
  switch (resourceType) {
19291
20098
  case "AWS::ElasticLoadBalancingV2::LoadBalancer":
19292
- return this.deleteLoadBalancer(logicalId, physicalId, resourceType);
20099
+ return this.deleteLoadBalancer(logicalId, physicalId, resourceType, context);
19293
20100
  case "AWS::ElasticLoadBalancingV2::TargetGroup":
19294
- return this.deleteTargetGroup(logicalId, physicalId, resourceType);
20101
+ return this.deleteTargetGroup(logicalId, physicalId, resourceType, context);
19295
20102
  case "AWS::ElasticLoadBalancingV2::Listener":
19296
- return this.deleteListener(logicalId, physicalId, resourceType);
20103
+ return this.deleteListener(logicalId, physicalId, resourceType, context);
19297
20104
  default:
19298
20105
  throw new ProvisioningError(
19299
20106
  `Unsupported resource type: ${resourceType}`,
@@ -19394,13 +20201,21 @@ var ELBv2Provider = class {
19394
20201
  );
19395
20202
  }
19396
20203
  }
19397
- async deleteLoadBalancer(logicalId, physicalId, resourceType) {
20204
+ async deleteLoadBalancer(logicalId, physicalId, resourceType, context) {
19398
20205
  this.logger.debug(`Deleting LoadBalancer ${logicalId}: ${physicalId}`);
19399
20206
  try {
19400
20207
  await this.getClient().send(new DeleteLoadBalancerCommand({ LoadBalancerArn: physicalId }));
19401
20208
  this.logger.debug(`Successfully deleted LoadBalancer ${logicalId}`);
19402
20209
  } catch (error) {
19403
20210
  if (this.isNotFoundError(error)) {
20211
+ const clientRegion = await this.getClient().config.region();
20212
+ assertRegionMatch(
20213
+ clientRegion,
20214
+ context?.expectedRegion,
20215
+ resourceType,
20216
+ logicalId,
20217
+ physicalId
20218
+ );
19404
20219
  this.logger.debug(`LoadBalancer ${physicalId} does not exist, skipping deletion`);
19405
20220
  return;
19406
20221
  }
@@ -19510,13 +20325,21 @@ var ELBv2Provider = class {
19510
20325
  );
19511
20326
  }
19512
20327
  }
19513
- async deleteTargetGroup(logicalId, physicalId, resourceType) {
20328
+ async deleteTargetGroup(logicalId, physicalId, resourceType, context) {
19514
20329
  this.logger.debug(`Deleting TargetGroup ${logicalId}: ${physicalId}`);
19515
20330
  try {
19516
20331
  await this.getClient().send(new DeleteTargetGroupCommand({ TargetGroupArn: physicalId }));
19517
20332
  this.logger.debug(`Successfully deleted TargetGroup ${logicalId}`);
19518
20333
  } catch (error) {
19519
20334
  if (this.isNotFoundError(error)) {
20335
+ const clientRegion = await this.getClient().config.region();
20336
+ assertRegionMatch(
20337
+ clientRegion,
20338
+ context?.expectedRegion,
20339
+ resourceType,
20340
+ logicalId,
20341
+ physicalId
20342
+ );
19520
20343
  this.logger.debug(`TargetGroup ${physicalId} does not exist, skipping deletion`);
19521
20344
  return;
19522
20345
  }
@@ -19612,13 +20435,21 @@ var ELBv2Provider = class {
19612
20435
  );
19613
20436
  }
19614
20437
  }
19615
- async deleteListener(logicalId, physicalId, resourceType) {
20438
+ async deleteListener(logicalId, physicalId, resourceType, context) {
19616
20439
  this.logger.debug(`Deleting Listener ${logicalId}: ${physicalId}`);
19617
20440
  try {
19618
20441
  await this.getClient().send(new DeleteListenerCommand({ ListenerArn: physicalId }));
19619
20442
  this.logger.debug(`Successfully deleted Listener ${logicalId}`);
19620
20443
  } catch (error) {
19621
20444
  if (this.isNotFoundError(error)) {
20445
+ const clientRegion = await this.getClient().config.region();
20446
+ assertRegionMatch(
20447
+ clientRegion,
20448
+ context?.expectedRegion,
20449
+ resourceType,
20450
+ logicalId,
20451
+ physicalId
20452
+ );
19622
20453
  this.logger.debug(`Listener ${physicalId} does not exist, skipping deletion`);
19623
20454
  return;
19624
20455
  }
@@ -19768,14 +20599,14 @@ var RDSProvider = class {
19768
20599
  );
19769
20600
  }
19770
20601
  }
19771
- async delete(logicalId, physicalId, resourceType, _properties) {
20602
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
19772
20603
  switch (resourceType) {
19773
20604
  case "AWS::RDS::DBSubnetGroup":
19774
- return this.deleteDBSubnetGroup(logicalId, physicalId, resourceType);
20605
+ return this.deleteDBSubnetGroup(logicalId, physicalId, resourceType, context);
19775
20606
  case "AWS::RDS::DBCluster":
19776
- return this.deleteDBCluster(logicalId, physicalId, resourceType);
20607
+ return this.deleteDBCluster(logicalId, physicalId, resourceType, context);
19777
20608
  case "AWS::RDS::DBInstance":
19778
- return this.deleteDBInstance(logicalId, physicalId, resourceType);
20609
+ return this.deleteDBInstance(logicalId, physicalId, resourceType, context);
19779
20610
  default:
19780
20611
  throw new ProvisioningError(
19781
20612
  `Unsupported resource type: ${resourceType}`,
@@ -19846,7 +20677,7 @@ var RDSProvider = class {
19846
20677
  );
19847
20678
  }
19848
20679
  }
19849
- async deleteDBSubnetGroup(logicalId, physicalId, resourceType) {
20680
+ async deleteDBSubnetGroup(logicalId, physicalId, resourceType, context) {
19850
20681
  this.logger.debug(`Deleting DBSubnetGroup ${logicalId}: ${physicalId}`);
19851
20682
  try {
19852
20683
  await this.getClient().send(
@@ -19857,6 +20688,14 @@ var RDSProvider = class {
19857
20688
  this.logger.debug(`Successfully deleted DBSubnetGroup ${logicalId}`);
19858
20689
  } catch (error) {
19859
20690
  if (this.isNotFoundError(error, "DBSubnetGroupNotFoundFault")) {
20691
+ const clientRegion = await this.getClient().config.region();
20692
+ assertRegionMatch(
20693
+ clientRegion,
20694
+ context?.expectedRegion,
20695
+ resourceType,
20696
+ logicalId,
20697
+ physicalId
20698
+ );
19860
20699
  this.logger.debug(`DBSubnetGroup ${physicalId} does not exist, skipping deletion`);
19861
20700
  return;
19862
20701
  }
@@ -19979,7 +20818,7 @@ var RDSProvider = class {
19979
20818
  );
19980
20819
  }
19981
20820
  }
19982
- async deleteDBCluster(logicalId, physicalId, resourceType) {
20821
+ async deleteDBCluster(logicalId, physicalId, resourceType, context) {
19983
20822
  this.logger.debug(`Deleting DBCluster ${logicalId}: ${physicalId}`);
19984
20823
  try {
19985
20824
  try {
@@ -20006,6 +20845,14 @@ var RDSProvider = class {
20006
20845
  await this.waitForClusterDeleted(physicalId);
20007
20846
  } catch (error) {
20008
20847
  if (this.isNotFoundError(error, "DBClusterNotFoundFault")) {
20848
+ const clientRegion = await this.getClient().config.region();
20849
+ assertRegionMatch(
20850
+ clientRegion,
20851
+ context?.expectedRegion,
20852
+ resourceType,
20853
+ logicalId,
20854
+ physicalId
20855
+ );
20009
20856
  this.logger.debug(`DBCluster ${physicalId} does not exist, skipping deletion`);
20010
20857
  return;
20011
20858
  }
@@ -20099,7 +20946,7 @@ var RDSProvider = class {
20099
20946
  );
20100
20947
  }
20101
20948
  }
20102
- async deleteDBInstance(logicalId, physicalId, resourceType) {
20949
+ async deleteDBInstance(logicalId, physicalId, resourceType, context) {
20103
20950
  this.logger.debug(`Deleting DBInstance ${logicalId}: ${physicalId}`);
20104
20951
  try {
20105
20952
  try {
@@ -20127,6 +20974,14 @@ var RDSProvider = class {
20127
20974
  await this.waitForInstanceDeleted(physicalId);
20128
20975
  } catch (error) {
20129
20976
  if (this.isNotFoundError(error, "DBInstanceNotFoundFault")) {
20977
+ const clientRegion = await this.getClient().config.region();
20978
+ assertRegionMatch(
20979
+ clientRegion,
20980
+ context?.expectedRegion,
20981
+ resourceType,
20982
+ logicalId,
20983
+ physicalId
20984
+ );
20130
20985
  this.logger.debug(`DBInstance ${physicalId} does not exist, skipping deletion`);
20131
20986
  return;
20132
20987
  }
@@ -20340,12 +21195,12 @@ var Route53Provider = class {
20340
21195
  );
20341
21196
  }
20342
21197
  }
20343
- async delete(logicalId, physicalId, resourceType, properties) {
21198
+ async delete(logicalId, physicalId, resourceType, properties, context) {
20344
21199
  switch (resourceType) {
20345
21200
  case "AWS::Route53::HostedZone":
20346
- return this.deleteHostedZone(logicalId, physicalId, resourceType);
21201
+ return this.deleteHostedZone(logicalId, physicalId, resourceType, context);
20347
21202
  case "AWS::Route53::RecordSet":
20348
- return this.deleteRecordSet(logicalId, physicalId, resourceType, properties);
21203
+ return this.deleteRecordSet(logicalId, physicalId, resourceType, properties, context);
20349
21204
  default:
20350
21205
  throw new ProvisioningError(
20351
21206
  `Unsupported resource type: ${resourceType}`,
@@ -20479,7 +21334,7 @@ var Route53Provider = class {
20479
21334
  );
20480
21335
  }
20481
21336
  }
20482
- async deleteHostedZone(logicalId, physicalId, resourceType) {
21337
+ async deleteHostedZone(logicalId, physicalId, resourceType, context) {
20483
21338
  this.logger.debug(`Deleting Route 53 hosted zone ${logicalId}: ${physicalId}`);
20484
21339
  try {
20485
21340
  await this.deleteQueryLoggingConfigForZone(physicalId, logicalId);
@@ -20487,6 +21342,14 @@ var Route53Provider = class {
20487
21342
  this.logger.debug(`Successfully deleted hosted zone ${logicalId}`);
20488
21343
  } catch (error) {
20489
21344
  if (error instanceof Error && error.name === "NoSuchHostedZone") {
21345
+ const clientRegion = await this.getClient().config.region();
21346
+ assertRegionMatch(
21347
+ clientRegion,
21348
+ context?.expectedRegion,
21349
+ resourceType,
21350
+ logicalId,
21351
+ physicalId
21352
+ );
20490
21353
  this.logger.debug(`Hosted zone ${physicalId} does not exist, skipping deletion`);
20491
21354
  return;
20492
21355
  }
@@ -20599,7 +21462,7 @@ var Route53Provider = class {
20599
21462
  );
20600
21463
  }
20601
21464
  }
20602
- async deleteRecordSet(logicalId, physicalId, resourceType, properties) {
21465
+ async deleteRecordSet(logicalId, physicalId, resourceType, properties, context) {
20603
21466
  this.logger.debug(`Deleting Route 53 record set ${logicalId}: ${physicalId}`);
20604
21467
  const parts = physicalId.split("|");
20605
21468
  if (parts.length !== 3) {
@@ -20637,6 +21500,14 @@ var Route53Provider = class {
20637
21500
  this.logger.debug(`Successfully deleted record set ${logicalId}`);
20638
21501
  } catch (error) {
20639
21502
  if (error instanceof Error && (error.name === "InvalidChangeBatch" || error.message.includes("it was not found"))) {
21503
+ const clientRegion = await this.getClient().config.region();
21504
+ assertRegionMatch(
21505
+ clientRegion,
21506
+ context?.expectedRegion,
21507
+ resourceType,
21508
+ logicalId,
21509
+ physicalId
21510
+ );
20640
21511
  this.logger.debug(`Record set ${physicalId} does not exist, skipping deletion`);
20641
21512
  return;
20642
21513
  }
@@ -21077,7 +21948,7 @@ var WAFv2WebACLProvider = class {
21077
21948
  * DeleteWebACL requires LockToken obtained from GetWebACL.
21078
21949
  * WAFNonexistentItemException is treated as success (idempotent delete).
21079
21950
  */
21080
- async delete(logicalId, physicalId, resourceType, _properties) {
21951
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
21081
21952
  this.logger.debug(`Deleting WAFv2 WebACL ${logicalId}: ${physicalId}`);
21082
21953
  try {
21083
21954
  const { id, name, scope } = parseWebACLArn(physicalId);
@@ -21103,6 +21974,14 @@ var WAFv2WebACLProvider = class {
21103
21974
  this.logger.debug(`Successfully deleted WAFv2 WebACL ${logicalId}`);
21104
21975
  } catch (error) {
21105
21976
  if (error instanceof WAFNonexistentItemException) {
21977
+ const clientRegion = await this.getClient().config.region();
21978
+ assertRegionMatch(
21979
+ clientRegion,
21980
+ context?.expectedRegion,
21981
+ resourceType,
21982
+ logicalId,
21983
+ physicalId
21984
+ );
21106
21985
  this.logger.debug(`WAFv2 WebACL ${physicalId} does not exist, skipping deletion`);
21107
21986
  return;
21108
21987
  }
@@ -21387,7 +22266,7 @@ var CognitoUserPoolProvider = class {
21387
22266
  *
21388
22267
  * If DeletionProtection is ACTIVE, it is automatically disabled before deletion.
21389
22268
  */
21390
- async delete(logicalId, physicalId, resourceType, properties) {
22269
+ async delete(logicalId, physicalId, resourceType, properties, context) {
21391
22270
  this.logger.debug(`Deleting Cognito User Pool ${logicalId}: ${physicalId}`);
21392
22271
  try {
21393
22272
  const deletionProtection = properties?.["DeletionProtection"];
@@ -21419,6 +22298,14 @@ var CognitoUserPoolProvider = class {
21419
22298
  }
21420
22299
  } catch (descError) {
21421
22300
  if (descError instanceof ResourceNotFoundException12) {
22301
+ const clientRegion = await this.getClient().config.region();
22302
+ assertRegionMatch(
22303
+ clientRegion,
22304
+ context?.expectedRegion,
22305
+ resourceType,
22306
+ logicalId,
22307
+ physicalId
22308
+ );
21422
22309
  this.logger.debug(`Cognito User Pool ${physicalId} does not exist, skipping deletion`);
21423
22310
  return;
21424
22311
  }
@@ -21431,6 +22318,14 @@ var CognitoUserPoolProvider = class {
21431
22318
  this.logger.debug(`Successfully deleted Cognito User Pool ${logicalId}`);
21432
22319
  } catch (error) {
21433
22320
  if (error instanceof ResourceNotFoundException12) {
22321
+ const clientRegion = await this.getClient().config.region();
22322
+ assertRegionMatch(
22323
+ clientRegion,
22324
+ context?.expectedRegion,
22325
+ resourceType,
22326
+ logicalId,
22327
+ physicalId
22328
+ );
21434
22329
  this.logger.debug(`Cognito User Pool ${physicalId} does not exist, skipping deletion`);
21435
22330
  return;
21436
22331
  }
@@ -21533,12 +22428,12 @@ var ElastiCacheProvider = class {
21533
22428
  );
21534
22429
  }
21535
22430
  }
21536
- async delete(logicalId, physicalId, resourceType, _properties) {
22431
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
21537
22432
  switch (resourceType) {
21538
22433
  case "AWS::ElastiCache::SubnetGroup":
21539
- return this.deleteSubnetGroup(logicalId, physicalId, resourceType);
22434
+ return this.deleteSubnetGroup(logicalId, physicalId, resourceType, context);
21540
22435
  case "AWS::ElastiCache::CacheCluster":
21541
- return this.deleteCacheCluster(logicalId, physicalId, resourceType);
22436
+ return this.deleteCacheCluster(logicalId, physicalId, resourceType, context);
21542
22437
  default:
21543
22438
  throw new ProvisioningError(
21544
22439
  `Unsupported resource type: ${resourceType}`,
@@ -21609,7 +22504,7 @@ var ElastiCacheProvider = class {
21609
22504
  );
21610
22505
  }
21611
22506
  }
21612
- async deleteSubnetGroup(logicalId, physicalId, resourceType) {
22507
+ async deleteSubnetGroup(logicalId, physicalId, resourceType, context) {
21613
22508
  this.logger.debug(`Deleting CacheSubnetGroup ${logicalId}: ${physicalId}`);
21614
22509
  try {
21615
22510
  await this.getClient().send(
@@ -21620,6 +22515,14 @@ var ElastiCacheProvider = class {
21620
22515
  this.logger.debug(`Successfully deleted CacheSubnetGroup ${logicalId}`);
21621
22516
  } catch (error) {
21622
22517
  if (this.isNotFoundError(error, "CacheSubnetGroupNotFoundFault")) {
22518
+ const clientRegion = await this.getClient().config.region();
22519
+ assertRegionMatch(
22520
+ clientRegion,
22521
+ context?.expectedRegion,
22522
+ resourceType,
22523
+ logicalId,
22524
+ physicalId
22525
+ );
21623
22526
  this.logger.debug(`CacheSubnetGroup ${physicalId} does not exist, skipping deletion`);
21624
22527
  return;
21625
22528
  }
@@ -21753,7 +22656,7 @@ var ElastiCacheProvider = class {
21753
22656
  );
21754
22657
  }
21755
22658
  }
21756
- async deleteCacheCluster(logicalId, physicalId, resourceType) {
22659
+ async deleteCacheCluster(logicalId, physicalId, resourceType, context) {
21757
22660
  this.logger.debug(`Deleting CacheCluster ${logicalId}: ${physicalId}`);
21758
22661
  try {
21759
22662
  await this.getClient().send(
@@ -21765,6 +22668,14 @@ var ElastiCacheProvider = class {
21765
22668
  await this.waitForClusterDeleted(physicalId);
21766
22669
  } catch (error) {
21767
22670
  if (this.isNotFoundError(error, "CacheClusterNotFoundFault")) {
22671
+ const clientRegion = await this.getClient().config.region();
22672
+ assertRegionMatch(
22673
+ clientRegion,
22674
+ context?.expectedRegion,
22675
+ resourceType,
22676
+ logicalId,
22677
+ physicalId
22678
+ );
21768
22679
  this.logger.debug(`CacheCluster ${physicalId} does not exist, skipping deletion`);
21769
22680
  return;
21770
22681
  }
@@ -21923,12 +22834,12 @@ var ServiceDiscoveryProvider = class {
21923
22834
  );
21924
22835
  }
21925
22836
  }
21926
- async delete(logicalId, physicalId, resourceType, _properties) {
22837
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
21927
22838
  switch (resourceType) {
21928
22839
  case "AWS::ServiceDiscovery::PrivateDnsNamespace":
21929
- return this.deleteNamespace(logicalId, physicalId, resourceType);
22840
+ return this.deleteNamespace(logicalId, physicalId, resourceType, context);
21930
22841
  case "AWS::ServiceDiscovery::Service":
21931
- return this.deleteService(logicalId, physicalId, resourceType);
22842
+ return this.deleteService(logicalId, physicalId, resourceType, context);
21932
22843
  default:
21933
22844
  throw new ProvisioningError(
21934
22845
  `Unsupported resource type: ${resourceType}`,
@@ -22006,7 +22917,7 @@ var ServiceDiscoveryProvider = class {
22006
22917
  }
22007
22918
  });
22008
22919
  }
22009
- async deleteNamespace(logicalId, physicalId, resourceType) {
22920
+ async deleteNamespace(logicalId, physicalId, resourceType, context) {
22010
22921
  this.logger.debug(`Deleting private DNS namespace ${logicalId}: ${physicalId}`);
22011
22922
  const client = this.getClient();
22012
22923
  try {
@@ -22018,6 +22929,14 @@ var ServiceDiscoveryProvider = class {
22018
22929
  this.logger.debug(`Successfully deleted private DNS namespace ${logicalId}`);
22019
22930
  } catch (error) {
22020
22931
  if (error instanceof NamespaceNotFound) {
22932
+ const clientRegion = await this.getClient().config.region();
22933
+ assertRegionMatch(
22934
+ clientRegion,
22935
+ context?.expectedRegion,
22936
+ resourceType,
22937
+ logicalId,
22938
+ physicalId
22939
+ );
22021
22940
  this.logger.debug(`Namespace ${physicalId} does not exist, skipping deletion`);
22022
22941
  return;
22023
22942
  }
@@ -22101,7 +23020,7 @@ var ServiceDiscoveryProvider = class {
22101
23020
  }
22102
23021
  });
22103
23022
  }
22104
- async deleteService(logicalId, physicalId, resourceType) {
23023
+ async deleteService(logicalId, physicalId, resourceType, context) {
22105
23024
  this.logger.debug(`Deleting service discovery service ${logicalId}: ${physicalId}`);
22106
23025
  const client = this.getClient();
22107
23026
  try {
@@ -22109,6 +23028,14 @@ var ServiceDiscoveryProvider = class {
22109
23028
  this.logger.debug(`Successfully deleted service discovery service ${logicalId}`);
22110
23029
  } catch (error) {
22111
23030
  if (error instanceof ServiceNotFound) {
23031
+ const clientRegion = await this.getClient().config.region();
23032
+ assertRegionMatch(
23033
+ clientRegion,
23034
+ context?.expectedRegion,
23035
+ resourceType,
23036
+ logicalId,
23037
+ physicalId
23038
+ );
22112
23039
  this.logger.debug(`Service ${physicalId} does not exist, skipping deletion`);
22113
23040
  return;
22114
23041
  }
@@ -22255,19 +23182,19 @@ var AppSyncProvider = class {
22255
23182
  this.logger.debug(`Update for ${resourceType} ${logicalId} (${physicalId}) - no-op, immutable`);
22256
23183
  return Promise.resolve({ physicalId, wasReplaced: false });
22257
23184
  }
22258
- async delete(logicalId, physicalId, resourceType, _properties) {
23185
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
22259
23186
  switch (resourceType) {
22260
23187
  case "AWS::AppSync::GraphQLApi":
22261
- return this.deleteGraphQLApi(logicalId, physicalId, resourceType);
23188
+ return this.deleteGraphQLApi(logicalId, physicalId, resourceType, context);
22262
23189
  case "AWS::AppSync::GraphQLSchema":
22263
23190
  this.logger.debug(`Schema ${logicalId} is deleted with its API, skipping`);
22264
23191
  return;
22265
23192
  case "AWS::AppSync::DataSource":
22266
- return this.deleteDataSource(logicalId, physicalId, resourceType);
23193
+ return this.deleteDataSource(logicalId, physicalId, resourceType, context);
22267
23194
  case "AWS::AppSync::Resolver":
22268
- return this.deleteResolver(logicalId, physicalId, resourceType);
23195
+ return this.deleteResolver(logicalId, physicalId, resourceType, context);
22269
23196
  case "AWS::AppSync::ApiKey":
22270
- return this.deleteApiKey(logicalId, physicalId, resourceType);
23197
+ return this.deleteApiKey(logicalId, physicalId, resourceType, context);
22271
23198
  default:
22272
23199
  throw new ProvisioningError(
22273
23200
  `Unsupported resource type: ${resourceType}`,
@@ -22341,13 +23268,21 @@ var AppSyncProvider = class {
22341
23268
  );
22342
23269
  }
22343
23270
  }
22344
- async deleteGraphQLApi(logicalId, physicalId, resourceType) {
23271
+ async deleteGraphQLApi(logicalId, physicalId, resourceType, context) {
22345
23272
  this.logger.debug(`Deleting GraphQL API ${logicalId}: ${physicalId}`);
22346
23273
  try {
22347
23274
  await this.getClient().send(new DeleteGraphqlApiCommand({ apiId: physicalId }));
22348
23275
  this.logger.debug(`Successfully deleted GraphQL API ${logicalId}`);
22349
23276
  } catch (error) {
22350
23277
  if (this.isNotFoundError(error)) {
23278
+ const clientRegion = await this.getClient().config.region();
23279
+ assertRegionMatch(
23280
+ clientRegion,
23281
+ context?.expectedRegion,
23282
+ resourceType,
23283
+ logicalId,
23284
+ physicalId
23285
+ );
22351
23286
  this.logger.debug(`GraphQL API ${physicalId} does not exist, skipping deletion`);
22352
23287
  return;
22353
23288
  }
@@ -22467,7 +23402,7 @@ var AppSyncProvider = class {
22467
23402
  );
22468
23403
  }
22469
23404
  }
22470
- async deleteDataSource(logicalId, physicalId, resourceType) {
23405
+ async deleteDataSource(logicalId, physicalId, resourceType, context) {
22471
23406
  this.logger.debug(`Deleting DataSource ${logicalId}: ${physicalId}`);
22472
23407
  const [apiId, name] = physicalId.split("|");
22473
23408
  if (!apiId || !name) {
@@ -22479,6 +23414,14 @@ var AppSyncProvider = class {
22479
23414
  this.logger.debug(`Successfully deleted DataSource ${logicalId}`);
22480
23415
  } catch (error) {
22481
23416
  if (this.isNotFoundError(error)) {
23417
+ const clientRegion = await this.getClient().config.region();
23418
+ assertRegionMatch(
23419
+ clientRegion,
23420
+ context?.expectedRegion,
23421
+ resourceType,
23422
+ logicalId,
23423
+ physicalId
23424
+ );
22482
23425
  this.logger.debug(`DataSource ${physicalId} does not exist, skipping deletion`);
22483
23426
  return;
22484
23427
  }
@@ -22559,7 +23502,7 @@ var AppSyncProvider = class {
22559
23502
  );
22560
23503
  }
22561
23504
  }
22562
- async deleteResolver(logicalId, physicalId, resourceType) {
23505
+ async deleteResolver(logicalId, physicalId, resourceType, context) {
22563
23506
  this.logger.debug(`Deleting Resolver ${logicalId}: ${physicalId}`);
22564
23507
  const parts = physicalId.split("|");
22565
23508
  if (parts.length < 3) {
@@ -22572,6 +23515,14 @@ var AppSyncProvider = class {
22572
23515
  this.logger.debug(`Successfully deleted Resolver ${logicalId}`);
22573
23516
  } catch (error) {
22574
23517
  if (this.isNotFoundError(error)) {
23518
+ const clientRegion = await this.getClient().config.region();
23519
+ assertRegionMatch(
23520
+ clientRegion,
23521
+ context?.expectedRegion,
23522
+ resourceType,
23523
+ logicalId,
23524
+ physicalId
23525
+ );
22575
23526
  this.logger.debug(`Resolver ${physicalId} does not exist, skipping deletion`);
22576
23527
  return;
22577
23528
  }
@@ -22625,7 +23576,7 @@ var AppSyncProvider = class {
22625
23576
  );
22626
23577
  }
22627
23578
  }
22628
- async deleteApiKey(logicalId, physicalId, resourceType) {
23579
+ async deleteApiKey(logicalId, physicalId, resourceType, context) {
22629
23580
  this.logger.debug(`Deleting ApiKey ${logicalId}: ${physicalId}`);
22630
23581
  const [apiId, apiKeyId] = physicalId.split("|");
22631
23582
  if (!apiId || !apiKeyId) {
@@ -22637,6 +23588,14 @@ var AppSyncProvider = class {
22637
23588
  this.logger.debug(`Successfully deleted ApiKey ${logicalId}`);
22638
23589
  } catch (error) {
22639
23590
  if (this.isNotFoundError(error)) {
23591
+ const clientRegion = await this.getClient().config.region();
23592
+ assertRegionMatch(
23593
+ clientRegion,
23594
+ context?.expectedRegion,
23595
+ resourceType,
23596
+ logicalId,
23597
+ physicalId
23598
+ );
22640
23599
  this.logger.debug(`ApiKey ${physicalId} does not exist, skipping deletion`);
22641
23600
  return;
22642
23601
  }
@@ -22717,12 +23676,12 @@ var GlueProvider = class {
22717
23676
  );
22718
23677
  }
22719
23678
  }
22720
- async delete(logicalId, physicalId, resourceType, _properties) {
23679
+ async delete(logicalId, physicalId, resourceType, properties, context) {
22721
23680
  switch (resourceType) {
22722
23681
  case "AWS::Glue::Database":
22723
- return this.deleteDatabase(logicalId, physicalId, resourceType);
23682
+ return this.deleteDatabase(logicalId, physicalId, resourceType, properties, context);
22724
23683
  case "AWS::Glue::Table":
22725
- return this.deleteTable(logicalId, physicalId, resourceType);
23684
+ return this.deleteTable(logicalId, physicalId, resourceType, properties, context);
22726
23685
  default:
22727
23686
  throw new ProvisioningError(
22728
23687
  `Unsupported resource type: ${resourceType}`,
@@ -22780,7 +23739,7 @@ var GlueProvider = class {
22780
23739
  );
22781
23740
  }
22782
23741
  }
22783
- async deleteDatabase(logicalId, physicalId, resourceType, properties) {
23742
+ async deleteDatabase(logicalId, physicalId, resourceType, properties, context) {
22784
23743
  this.logger.debug(`Deleting Glue Database ${logicalId}: ${physicalId}`);
22785
23744
  try {
22786
23745
  const catalogId = properties?.["CatalogId"];
@@ -22793,6 +23752,14 @@ var GlueProvider = class {
22793
23752
  this.logger.debug(`Successfully deleted Glue Database ${logicalId}`);
22794
23753
  } catch (error) {
22795
23754
  if (error instanceof EntityNotFoundException) {
23755
+ const clientRegion = await this.getClient().config.region();
23756
+ assertRegionMatch(
23757
+ clientRegion,
23758
+ context?.expectedRegion,
23759
+ resourceType,
23760
+ logicalId,
23761
+ physicalId
23762
+ );
22796
23763
  this.logger.debug(`Glue Database ${physicalId} does not exist, skipping deletion`);
22797
23764
  return;
22798
23765
  }
@@ -22904,7 +23871,7 @@ var GlueProvider = class {
22904
23871
  );
22905
23872
  }
22906
23873
  }
22907
- async deleteTable(logicalId, physicalId, resourceType) {
23874
+ async deleteTable(logicalId, physicalId, resourceType, _properties, context) {
22908
23875
  this.logger.debug(`Deleting Glue Table ${logicalId}: ${physicalId}`);
22909
23876
  const [databaseName, tableName] = physicalId.split("|");
22910
23877
  if (!databaseName || !tableName) {
@@ -22921,6 +23888,14 @@ var GlueProvider = class {
22921
23888
  this.logger.debug(`Successfully deleted Glue Table ${logicalId}`);
22922
23889
  } catch (error) {
22923
23890
  if (error instanceof EntityNotFoundException) {
23891
+ const clientRegion = await this.getClient().config.region();
23892
+ assertRegionMatch(
23893
+ clientRegion,
23894
+ context?.expectedRegion,
23895
+ resourceType,
23896
+ logicalId,
23897
+ physicalId
23898
+ );
22924
23899
  this.logger.debug(`Glue Table ${physicalId} does not exist, skipping deletion`);
22925
23900
  return;
22926
23901
  }
@@ -23106,12 +24081,12 @@ var KMSProvider = class {
23106
24081
  );
23107
24082
  }
23108
24083
  }
23109
- async delete(logicalId, physicalId, resourceType, _properties) {
24084
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
23110
24085
  switch (resourceType) {
23111
24086
  case "AWS::KMS::Key":
23112
- return this.deleteKey(logicalId, physicalId, resourceType, _properties);
24087
+ return this.deleteKey(logicalId, physicalId, resourceType, _properties, context);
23113
24088
  case "AWS::KMS::Alias":
23114
- return this.deleteAlias(logicalId, physicalId, resourceType);
24089
+ return this.deleteAlias(logicalId, physicalId, resourceType, context);
23115
24090
  default:
23116
24091
  throw new ProvisioningError(
23117
24092
  `Unsupported resource type: ${resourceType}`,
@@ -23275,7 +24250,7 @@ var KMSProvider = class {
23275
24250
  );
23276
24251
  }
23277
24252
  }
23278
- async deleteKey(logicalId, physicalId, resourceType, properties) {
24253
+ async deleteKey(logicalId, physicalId, resourceType, properties, context) {
23279
24254
  this.logger.debug(`Scheduling deletion for KMS Key ${logicalId}: ${physicalId}`);
23280
24255
  const pendingWindowInDays = properties?.["PendingWindowInDays"] ?? 7;
23281
24256
  try {
@@ -23288,6 +24263,14 @@ var KMSProvider = class {
23288
24263
  this.logger.debug(`Successfully scheduled deletion for KMS Key ${logicalId}`);
23289
24264
  } catch (error) {
23290
24265
  if (error instanceof NotFoundException5) {
24266
+ const clientRegion = await this.getClient().config.region();
24267
+ assertRegionMatch(
24268
+ clientRegion,
24269
+ context?.expectedRegion,
24270
+ resourceType,
24271
+ logicalId,
24272
+ physicalId
24273
+ );
23291
24274
  this.logger.debug(`KMS Key ${physicalId} does not exist, skipping deletion`);
23292
24275
  return;
23293
24276
  }
@@ -23374,7 +24357,7 @@ var KMSProvider = class {
23374
24357
  );
23375
24358
  }
23376
24359
  }
23377
- async deleteAlias(logicalId, physicalId, resourceType) {
24360
+ async deleteAlias(logicalId, physicalId, resourceType, context) {
23378
24361
  this.logger.debug(`Deleting KMS Alias ${logicalId}: ${physicalId}`);
23379
24362
  try {
23380
24363
  await this.getClient().send(
@@ -23385,6 +24368,14 @@ var KMSProvider = class {
23385
24368
  this.logger.debug(`Successfully deleted KMS Alias ${logicalId}`);
23386
24369
  } catch (error) {
23387
24370
  if (error instanceof NotFoundException5) {
24371
+ const clientRegion = await this.getClient().config.region();
24372
+ assertRegionMatch(
24373
+ clientRegion,
24374
+ context?.expectedRegion,
24375
+ resourceType,
24376
+ logicalId,
24377
+ physicalId
24378
+ );
23388
24379
  this.logger.debug(`KMS Alias ${physicalId} does not exist, skipping deletion`);
23389
24380
  return;
23390
24381
  }
@@ -23631,7 +24622,7 @@ var KinesisStreamProvider = class {
23631
24622
  /**
23632
24623
  * Delete a Kinesis stream
23633
24624
  */
23634
- async delete(logicalId, physicalId, resourceType, _properties) {
24625
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
23635
24626
  this.logger.debug(`Deleting Kinesis stream ${logicalId}: ${physicalId}`);
23636
24627
  try {
23637
24628
  await this.getClient().send(
@@ -23643,6 +24634,14 @@ var KinesisStreamProvider = class {
23643
24634
  this.logger.debug(`Successfully deleted Kinesis stream ${logicalId}`);
23644
24635
  } catch (error) {
23645
24636
  if (error instanceof ResourceNotFoundException13) {
24637
+ const clientRegion = await this.getClient().config.region();
24638
+ assertRegionMatch(
24639
+ clientRegion,
24640
+ context?.expectedRegion,
24641
+ resourceType,
24642
+ logicalId,
24643
+ physicalId
24644
+ );
23646
24645
  this.logger.debug(`Kinesis stream ${physicalId} does not exist, skipping deletion`);
23647
24646
  return;
23648
24647
  }
@@ -23759,14 +24758,14 @@ var EFSProvider = class {
23759
24758
  }
23760
24759
  return Promise.resolve({ physicalId, wasReplaced: false });
23761
24760
  }
23762
- async delete(logicalId, physicalId, resourceType, _properties) {
24761
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
23763
24762
  switch (resourceType) {
23764
24763
  case "AWS::EFS::FileSystem":
23765
- return this.deleteFileSystem(logicalId, physicalId, resourceType);
24764
+ return this.deleteFileSystem(logicalId, physicalId, resourceType, context);
23766
24765
  case "AWS::EFS::MountTarget":
23767
- return this.deleteMountTarget(logicalId, physicalId, resourceType);
24766
+ return this.deleteMountTarget(logicalId, physicalId, resourceType, context);
23768
24767
  case "AWS::EFS::AccessPoint":
23769
- return this.deleteAccessPoint(logicalId, physicalId, resourceType);
24768
+ return this.deleteAccessPoint(logicalId, physicalId, resourceType, context);
23770
24769
  default:
23771
24770
  throw new ProvisioningError(
23772
24771
  `Unsupported resource type: ${resourceType}`,
@@ -23815,7 +24814,7 @@ var EFSProvider = class {
23815
24814
  );
23816
24815
  }
23817
24816
  }
23818
- async deleteFileSystem(logicalId, physicalId, resourceType) {
24817
+ async deleteFileSystem(logicalId, physicalId, resourceType, context) {
23819
24818
  this.logger.debug(`Deleting EFS FileSystem ${logicalId}: ${physicalId}`);
23820
24819
  try {
23821
24820
  await this.getClient().send(
@@ -23826,6 +24825,14 @@ var EFSProvider = class {
23826
24825
  this.logger.debug(`Successfully deleted EFS FileSystem ${logicalId}`);
23827
24826
  } catch (error) {
23828
24827
  if (error instanceof FileSystemNotFound) {
24828
+ const clientRegion = await this.getClient().config.region();
24829
+ assertRegionMatch(
24830
+ clientRegion,
24831
+ context?.expectedRegion,
24832
+ resourceType,
24833
+ logicalId,
24834
+ physicalId
24835
+ );
23829
24836
  this.logger.debug(`EFS FileSystem ${physicalId} does not exist, skipping deletion`);
23830
24837
  return;
23831
24838
  }
@@ -23941,7 +24948,7 @@ var EFSProvider = class {
23941
24948
  mountTargetId
23942
24949
  );
23943
24950
  }
23944
- async deleteMountTarget(logicalId, physicalId, resourceType) {
24951
+ async deleteMountTarget(logicalId, physicalId, resourceType, context) {
23945
24952
  this.logger.debug(`Deleting EFS MountTarget ${logicalId}: ${physicalId}`);
23946
24953
  try {
23947
24954
  await this.getClient().send(
@@ -23953,6 +24960,14 @@ var EFSProvider = class {
23953
24960
  this.logger.debug(`Successfully deleted EFS MountTarget ${logicalId}`);
23954
24961
  } catch (error) {
23955
24962
  if (error instanceof MountTargetNotFound) {
24963
+ const clientRegion = await this.getClient().config.region();
24964
+ assertRegionMatch(
24965
+ clientRegion,
24966
+ context?.expectedRegion,
24967
+ resourceType,
24968
+ logicalId,
24969
+ physicalId
24970
+ );
23956
24971
  this.logger.debug(`EFS MountTarget ${physicalId} does not exist, skipping deletion`);
23957
24972
  return;
23958
24973
  }
@@ -24051,7 +25066,7 @@ var EFSProvider = class {
24051
25066
  );
24052
25067
  }
24053
25068
  }
24054
- async deleteAccessPoint(logicalId, physicalId, resourceType) {
25069
+ async deleteAccessPoint(logicalId, physicalId, resourceType, context) {
24055
25070
  this.logger.debug(`Deleting EFS AccessPoint ${logicalId}: ${physicalId}`);
24056
25071
  try {
24057
25072
  await this.getClient().send(
@@ -24062,6 +25077,14 @@ var EFSProvider = class {
24062
25077
  this.logger.debug(`Successfully deleted EFS AccessPoint ${logicalId}`);
24063
25078
  } catch (error) {
24064
25079
  if (error instanceof AccessPointNotFound) {
25080
+ const clientRegion = await this.getClient().config.region();
25081
+ assertRegionMatch(
25082
+ clientRegion,
25083
+ context?.expectedRegion,
25084
+ resourceType,
25085
+ logicalId,
25086
+ physicalId
25087
+ );
24065
25088
  this.logger.debug(`EFS AccessPoint ${physicalId} does not exist, skipping deletion`);
24066
25089
  return;
24067
25090
  }
@@ -24299,7 +25322,7 @@ var FirehoseProvider = class {
24299
25322
  /**
24300
25323
  * Delete a Firehose delivery stream
24301
25324
  */
24302
- async delete(logicalId, physicalId, resourceType, _properties) {
25325
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
24303
25326
  this.logger.debug(`Deleting Firehose delivery stream ${logicalId}: ${physicalId}`);
24304
25327
  try {
24305
25328
  await this.getClient().send(
@@ -24310,6 +25333,14 @@ var FirehoseProvider = class {
24310
25333
  this.logger.debug(`Successfully deleted Firehose delivery stream ${logicalId}`);
24311
25334
  } catch (error) {
24312
25335
  if (error instanceof ResourceNotFoundException14) {
25336
+ const clientRegion = await this.getClient().config.region();
25337
+ assertRegionMatch(
25338
+ clientRegion,
25339
+ context?.expectedRegion,
25340
+ resourceType,
25341
+ logicalId,
25342
+ physicalId
25343
+ );
24313
25344
  this.logger.debug(
24314
25345
  `Firehose delivery stream ${physicalId} does not exist, skipping deletion`
24315
25346
  );
@@ -24641,7 +25672,7 @@ var CloudTrailProvider = class {
24641
25672
  );
24642
25673
  }
24643
25674
  }
24644
- async delete(logicalId, physicalId, resourceType, _properties) {
25675
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
24645
25676
  this.logger.debug(`Deleting CloudTrail Trail ${logicalId}: ${physicalId}`);
24646
25677
  try {
24647
25678
  try {
@@ -24652,6 +25683,14 @@ var CloudTrailProvider = class {
24652
25683
  this.logger.debug(`Successfully deleted CloudTrail Trail ${logicalId}`);
24653
25684
  } catch (error) {
24654
25685
  if (error instanceof TrailNotFoundException) {
25686
+ const clientRegion = await this.getClient().config.region();
25687
+ assertRegionMatch(
25688
+ clientRegion,
25689
+ context?.expectedRegion,
25690
+ resourceType,
25691
+ logicalId,
25692
+ physicalId
25693
+ );
24655
25694
  this.logger.debug(`CloudTrail Trail ${physicalId} does not exist, skipping deletion`);
24656
25695
  return;
24657
25696
  }
@@ -24903,13 +25942,21 @@ var CodeBuildProvider = class {
24903
25942
  );
24904
25943
  }
24905
25944
  }
24906
- async delete(logicalId, physicalId, resourceType, _properties) {
25945
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
24907
25946
  this.logger.debug(`Deleting CodeBuild Project ${logicalId}: ${physicalId}`);
24908
25947
  try {
24909
25948
  await this.getClient().send(new DeleteProjectCommand({ name: physicalId }));
24910
25949
  this.logger.debug(`Successfully deleted CodeBuild Project ${logicalId}`);
24911
25950
  } catch (error) {
24912
25951
  if (error instanceof ResourceNotFoundException15) {
25952
+ const clientRegion = await this.getClient().config.region();
25953
+ assertRegionMatch(
25954
+ clientRegion,
25955
+ context?.expectedRegion,
25956
+ resourceType,
25957
+ logicalId,
25958
+ physicalId
25959
+ );
24913
25960
  this.logger.debug(`CodeBuild Project ${physicalId} does not exist, skipping deletion`);
24914
25961
  return;
24915
25962
  }
@@ -24975,10 +26022,10 @@ var S3VectorsProvider = class {
24975
26022
  );
24976
26023
  }
24977
26024
  }
24978
- async delete(logicalId, physicalId, resourceType, _properties) {
26025
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
24979
26026
  switch (resourceType) {
24980
26027
  case "AWS::S3Vectors::VectorBucket":
24981
- return this.deleteVectorBucket(logicalId, physicalId, resourceType);
26028
+ return this.deleteVectorBucket(logicalId, physicalId, resourceType, context);
24982
26029
  default:
24983
26030
  throw new ProvisioningError(
24984
26031
  `Unsupported resource type: ${resourceType}`,
@@ -25029,7 +26076,7 @@ var S3VectorsProvider = class {
25029
26076
  );
25030
26077
  }
25031
26078
  }
25032
- async deleteVectorBucket(logicalId, physicalId, resourceType) {
26079
+ async deleteVectorBucket(logicalId, physicalId, resourceType, context) {
25033
26080
  this.logger.debug(`Deleting S3 VectorBucket ${logicalId}: ${physicalId}`);
25034
26081
  try {
25035
26082
  await this.emptyVectorBucket(logicalId, physicalId);
@@ -25041,6 +26088,14 @@ var S3VectorsProvider = class {
25041
26088
  this.logger.debug(`Successfully deleted S3 VectorBucket ${logicalId}`);
25042
26089
  } catch (error) {
25043
26090
  if (this.isNotFoundError(error)) {
26091
+ const clientRegion = await this.getClient().config.region();
26092
+ assertRegionMatch(
26093
+ clientRegion,
26094
+ context?.expectedRegion,
26095
+ resourceType,
26096
+ logicalId,
26097
+ physicalId
26098
+ );
25044
26099
  this.logger.debug(`S3 VectorBucket ${physicalId} does not exist, skipping deletion`);
25045
26100
  return;
25046
26101
  }
@@ -25237,7 +26292,7 @@ var S3DirectoryBucketProvider = class {
25237
26292
  * Must empty the bucket before deletion. Directory buckets do not support
25238
26293
  * versioning, so only current objects need to be deleted.
25239
26294
  */
25240
- async delete(logicalId, physicalId, resourceType, _properties) {
26295
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
25241
26296
  this.logger.debug(`Deleting S3 Express Directory Bucket ${logicalId}: ${physicalId}`);
25242
26297
  try {
25243
26298
  await this.emptyBucket(physicalId);
@@ -25249,6 +26304,14 @@ var S3DirectoryBucketProvider = class {
25249
26304
  this.logger.debug(`Successfully deleted S3 Express Directory Bucket ${logicalId}`);
25250
26305
  } catch (error) {
25251
26306
  if (error instanceof Error && (error.name === "NoSuchBucket" || error.name === "BucketNotFound")) {
26307
+ const clientRegion = await this.s3Client.config.region();
26308
+ assertRegionMatch(
26309
+ clientRegion,
26310
+ context?.expectedRegion,
26311
+ resourceType,
26312
+ logicalId,
26313
+ physicalId
26314
+ );
25252
26315
  this.logger.debug(`Bucket ${physicalId} does not exist, skipping deletion`);
25253
26316
  return;
25254
26317
  }
@@ -25343,14 +26406,14 @@ var S3TablesProvider = class {
25343
26406
  this.logger.debug(`Update is no-op for ${resourceType} ${logicalId}`);
25344
26407
  return Promise.resolve({ physicalId, wasReplaced: false });
25345
26408
  }
25346
- async delete(logicalId, physicalId, resourceType, _properties) {
26409
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
25347
26410
  switch (resourceType) {
25348
26411
  case "AWS::S3Tables::TableBucket":
25349
- return this.deleteTableBucket(logicalId, physicalId, resourceType);
26412
+ return this.deleteTableBucket(logicalId, physicalId, resourceType, context);
25350
26413
  case "AWS::S3Tables::Namespace":
25351
- return this.deleteNamespace(logicalId, physicalId, resourceType);
26414
+ return this.deleteNamespace(logicalId, physicalId, resourceType, context);
25352
26415
  case "AWS::S3Tables::Table":
25353
- return this.deleteTable(logicalId, physicalId, resourceType);
26416
+ return this.deleteTable(logicalId, physicalId, resourceType, context);
25354
26417
  default:
25355
26418
  throw new ProvisioningError(
25356
26419
  `Unsupported resource type: ${resourceType}`,
@@ -25396,7 +26459,7 @@ var S3TablesProvider = class {
25396
26459
  );
25397
26460
  }
25398
26461
  }
25399
- async deleteTableBucket(logicalId, physicalId, resourceType) {
26462
+ async deleteTableBucket(logicalId, physicalId, resourceType, context) {
25400
26463
  this.logger.debug(`Deleting S3 Table Bucket ${logicalId}: ${physicalId}`);
25401
26464
  try {
25402
26465
  await this.emptyTableBucket(physicalId);
@@ -25408,6 +26471,14 @@ var S3TablesProvider = class {
25408
26471
  this.logger.debug(`Successfully deleted S3 Table Bucket ${logicalId}`);
25409
26472
  } catch (error) {
25410
26473
  if (error instanceof NotFoundException6) {
26474
+ const clientRegion = await this.getClient().config.region();
26475
+ assertRegionMatch(
26476
+ clientRegion,
26477
+ context?.expectedRegion,
26478
+ resourceType,
26479
+ logicalId,
26480
+ physicalId
26481
+ );
25411
26482
  this.logger.debug(`S3 Table Bucket ${physicalId} does not exist, skipping deletion`);
25412
26483
  return;
25413
26484
  }
@@ -25531,7 +26602,7 @@ var S3TablesProvider = class {
25531
26602
  );
25532
26603
  }
25533
26604
  }
25534
- async deleteNamespace(logicalId, physicalId, resourceType) {
26605
+ async deleteNamespace(logicalId, physicalId, resourceType, context) {
25535
26606
  this.logger.debug(`Deleting S3 Tables Namespace ${logicalId}: ${physicalId}`);
25536
26607
  const [tableBucketARN, namespaceName] = physicalId.split("|");
25537
26608
  if (!tableBucketARN || !namespaceName) {
@@ -25552,6 +26623,14 @@ var S3TablesProvider = class {
25552
26623
  this.logger.debug(`Successfully deleted S3 Tables Namespace ${logicalId}`);
25553
26624
  } catch (error) {
25554
26625
  if (error instanceof NotFoundException6) {
26626
+ const clientRegion = await this.getClient().config.region();
26627
+ assertRegionMatch(
26628
+ clientRegion,
26629
+ context?.expectedRegion,
26630
+ resourceType,
26631
+ logicalId,
26632
+ physicalId
26633
+ );
25555
26634
  this.logger.debug(`S3 Tables Namespace ${physicalId} does not exist, skipping deletion`);
25556
26635
  return;
25557
26636
  }
@@ -25626,7 +26705,7 @@ var S3TablesProvider = class {
25626
26705
  );
25627
26706
  }
25628
26707
  }
25629
- async deleteTable(logicalId, physicalId, resourceType) {
26708
+ async deleteTable(logicalId, physicalId, resourceType, context) {
25630
26709
  this.logger.debug(`Deleting S3 Tables Table ${logicalId}: ${physicalId}`);
25631
26710
  const parts = physicalId.split("|");
25632
26711
  if (parts.length < 3) {
@@ -25651,6 +26730,14 @@ var S3TablesProvider = class {
25651
26730
  this.logger.debug(`Successfully deleted S3 Tables Table ${logicalId}`);
25652
26731
  } catch (error) {
25653
26732
  if (error instanceof NotFoundException6) {
26733
+ const clientRegion = await this.getClient().config.region();
26734
+ assertRegionMatch(
26735
+ clientRegion,
26736
+ context?.expectedRegion,
26737
+ resourceType,
26738
+ logicalId,
26739
+ physicalId
26740
+ );
25654
26741
  this.logger.debug(`S3 Tables Table ${physicalId} does not exist, skipping deletion`);
25655
26742
  return;
25656
26743
  }
@@ -25877,7 +26964,7 @@ var ECRProvider = class {
25877
26964
  * Uses `force: true` to delete the repository even if it contains images.
25878
26965
  * This supports CDK's `emptyOnDelete: true` / `removalPolicy: DESTROY` pattern.
25879
26966
  */
25880
- async delete(logicalId, physicalId, resourceType, _properties) {
26967
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
25881
26968
  this.logger.debug(`Deleting ECR Repository ${logicalId}: ${physicalId}`);
25882
26969
  try {
25883
26970
  await this.getClient().send(
@@ -25889,6 +26976,14 @@ var ECRProvider = class {
25889
26976
  this.logger.debug(`Successfully deleted ECR Repository ${logicalId}`);
25890
26977
  } catch (error) {
25891
26978
  if (error instanceof RepositoryNotFoundException) {
26979
+ const clientRegion = await this.getClient().config.region();
26980
+ assertRegionMatch(
26981
+ clientRegion,
26982
+ context?.expectedRegion,
26983
+ resourceType,
26984
+ logicalId,
26985
+ physicalId
26986
+ );
25892
26987
  this.logger.debug(`ECR Repository ${physicalId} does not exist, skipping deletion`);
25893
26988
  return;
25894
26989
  }
@@ -26256,7 +27351,11 @@ var DeployEngine = class {
26256
27351
  logger = getLogger().child("DeployEngine");
26257
27352
  resolver;
26258
27353
  interrupted = false;
26259
- /** Target region for this stack (saved in state for cross-region destroy) */
27354
+ /**
27355
+ * Target region for this stack. Required — load-bearing for the
27356
+ * region-prefixed S3 state key and recorded in state.json for
27357
+ * cross-region destroy.
27358
+ */
26260
27359
  stackRegion;
26261
27360
  /**
26262
27361
  * Deploy a CloudFormation template
@@ -26265,7 +27364,7 @@ var DeployEngine = class {
26265
27364
  const startTime = Date.now();
26266
27365
  this.logger.debug(`Starting deployment for stack: ${stackName}`);
26267
27366
  setCurrentStackName(stackName);
26268
- await this.lockManager.acquireLockWithRetry(stackName, void 0, "deploy");
27367
+ await this.lockManager.acquireLockWithRetry(stackName, this.stackRegion, void 0, "deploy");
26269
27368
  const renderer = getLiveRenderer();
26270
27369
  renderer.start();
26271
27370
  this.interrupted = false;
@@ -26279,16 +27378,17 @@ var DeployEngine = class {
26279
27378
  };
26280
27379
  process.on("SIGINT", sigintHandler);
26281
27380
  try {
26282
- const currentStateData = await this.stateBackend.getState(stackName);
27381
+ const currentStateData = await this.stateBackend.getState(stackName, this.stackRegion);
26283
27382
  const currentState = currentStateData?.state ?? {
26284
- version: 1,
26285
- ...this.stackRegion && { region: this.stackRegion },
27383
+ version: STATE_SCHEMA_VERSION_CURRENT,
27384
+ region: this.stackRegion,
26286
27385
  stackName,
26287
27386
  resources: {},
26288
27387
  outputs: {},
26289
27388
  lastModified: Date.now()
26290
27389
  };
26291
27390
  const currentEtag = currentStateData?.etag;
27391
+ const migrationPending = currentStateData?.migrationPending ?? false;
26292
27392
  this.logger.debug(
26293
27393
  `Loaded current state: ${Object.keys(currentState.resources).length} resources`
26294
27394
  );
@@ -26374,9 +27474,10 @@ var DeployEngine = class {
26374
27474
  parameterValues,
26375
27475
  conditions,
26376
27476
  currentEtag,
26377
- progress
27477
+ progress,
27478
+ migrationPending
26378
27479
  );
26379
- const newEtag = await this.stateBackend.saveState(stackName, newState);
27480
+ const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, newState);
26380
27481
  this.logger.debug(`State saved (ETag: ${newEtag})`);
26381
27482
  const durationMs = Date.now() - startTime;
26382
27483
  const unchangedCount = this.diffCalculator.filterByType(changes, "NO_CHANGE").length + actualCounts.skipped;
@@ -26392,7 +27493,7 @@ var DeployEngine = class {
26392
27493
  renderer.stop();
26393
27494
  process.removeListener("SIGINT", sigintHandler);
26394
27495
  try {
26395
- await this.lockManager.releaseLock(stackName);
27496
+ await this.lockManager.releaseLock(stackName, this.stackRegion);
26396
27497
  this.logger.debug("Lock released");
26397
27498
  } catch (lockError) {
26398
27499
  this.logger.warn(
@@ -26410,11 +27511,12 @@ var DeployEngine = class {
26410
27511
  * - DELETE follows reverse dependency order (a node starts as soon as all
26411
27512
  * resources that depend ON it have finished deleting)
26412
27513
  */
26413
- async executeDeployment(template, currentState, changes, dag, executionLevels, stackName, parameterValues, conditions, currentEtag, progress) {
27514
+ async executeDeployment(template, currentState, changes, dag, executionLevels, stackName, parameterValues, conditions, currentEtag, progress, migrationPending = false) {
26414
27515
  const concurrency = this.options.concurrency;
26415
27516
  const newResources = { ...currentState.resources };
26416
27517
  const actualCounts = { created: 0, updated: 0, deleted: 0, skipped: 0 };
26417
27518
  const completedOperations = [];
27519
+ let pendingMigration = migrationPending;
26418
27520
  let saveChain = Promise.resolve();
26419
27521
  const saveStateAfterResource = (logicalId) => {
26420
27522
  if (currentEtag === void 0)
@@ -26422,14 +27524,23 @@ var DeployEngine = class {
26422
27524
  saveChain = saveChain.then(async () => {
26423
27525
  try {
26424
27526
  const partialState = {
26425
- version: 1,
26426
- ...this.stackRegion && { region: this.stackRegion },
27527
+ version: STATE_SCHEMA_VERSION_CURRENT,
27528
+ region: this.stackRegion,
26427
27529
  stackName: currentState.stackName,
26428
27530
  resources: newResources,
26429
27531
  outputs: currentState.outputs,
26430
27532
  lastModified: Date.now()
26431
27533
  };
26432
- currentEtag = await this.stateBackend.saveState(stackName, partialState, currentEtag);
27534
+ const migrate = pendingMigration;
27535
+ const expectedEtag = migrate ? void 0 : currentEtag;
27536
+ currentEtag = await this.stateBackend.saveState(
27537
+ stackName,
27538
+ this.stackRegion,
27539
+ partialState,
27540
+ { ...expectedEtag !== void 0 && { expectedEtag }, migrateLegacy: migrate }
27541
+ );
27542
+ if (migrate)
27543
+ pendingMigration = false;
26433
27544
  this.logger.debug(`State saved after ${logicalId}`);
26434
27545
  } catch (error) {
26435
27546
  this.logger.warn(
@@ -26563,14 +27674,23 @@ var DeployEngine = class {
26563
27674
  } catch (error) {
26564
27675
  try {
26565
27676
  const preRollbackState = {
26566
- version: 1,
26567
- ...this.stackRegion && { region: this.stackRegion },
27677
+ version: STATE_SCHEMA_VERSION_CURRENT,
27678
+ region: this.stackRegion,
26568
27679
  stackName: currentState.stackName,
26569
27680
  resources: newResources,
26570
27681
  outputs: currentState.outputs,
26571
27682
  lastModified: Date.now()
26572
27683
  };
26573
- currentEtag = await this.stateBackend.saveState(stackName, preRollbackState, currentEtag);
27684
+ const migrate = pendingMigration;
27685
+ const expectedEtag = migrate ? void 0 : currentEtag;
27686
+ currentEtag = await this.stateBackend.saveState(
27687
+ stackName,
27688
+ this.stackRegion,
27689
+ preRollbackState,
27690
+ { ...expectedEtag !== void 0 && { expectedEtag }, migrateLegacy: migrate }
27691
+ );
27692
+ if (migrate)
27693
+ pendingMigration = false;
26574
27694
  this.logger.debug("Partial state saved before rollback (orphaned resource tracking)");
26575
27695
  } catch (saveError) {
26576
27696
  this.logger.warn(
@@ -26591,31 +27711,35 @@ var DeployEngine = class {
26591
27711
  }
26592
27712
  try {
26593
27713
  const postRollbackState = {
26594
- version: 1,
26595
- ...this.stackRegion && { region: this.stackRegion },
27714
+ version: STATE_SCHEMA_VERSION_CURRENT,
27715
+ region: this.stackRegion,
26596
27716
  stackName: currentState.stackName,
26597
27717
  resources: newResources,
26598
27718
  outputs: currentState.outputs,
26599
27719
  lastModified: Date.now()
26600
27720
  };
26601
- await this.stateBackend.saveState(stackName, postRollbackState, currentEtag);
27721
+ await this.stateBackend.saveState(stackName, this.stackRegion, postRollbackState, {
27722
+ ...currentEtag !== void 0 && { expectedEtag: currentEtag }
27723
+ });
26602
27724
  this.logger.debug("State saved after deployment failure");
26603
27725
  } catch (saveError) {
26604
27726
  this.logger.debug(
26605
27727
  `Retrying state save after rollback (ETag mismatch): ${saveError instanceof Error ? saveError.message : String(saveError)}`
26606
27728
  );
26607
27729
  try {
26608
- const freshState = await this.stateBackend.getState(stackName);
27730
+ const freshState = await this.stateBackend.getState(stackName, this.stackRegion);
26609
27731
  const freshEtag = freshState?.etag;
26610
27732
  const postRollbackState = {
26611
- version: 1,
26612
- ...this.stackRegion && { region: this.stackRegion },
27733
+ version: STATE_SCHEMA_VERSION_CURRENT,
27734
+ region: this.stackRegion,
26613
27735
  stackName: currentState.stackName,
26614
27736
  resources: newResources,
26615
27737
  outputs: currentState.outputs,
26616
27738
  lastModified: Date.now()
26617
27739
  };
26618
- await this.stateBackend.saveState(stackName, postRollbackState, freshEtag);
27740
+ await this.stateBackend.saveState(stackName, this.stackRegion, postRollbackState, {
27741
+ ...freshEtag !== void 0 && { expectedEtag: freshEtag }
27742
+ });
26619
27743
  this.logger.debug("State saved after deployment failure (retry succeeded)");
26620
27744
  } catch (retryError) {
26621
27745
  this.logger.warn(
@@ -26634,8 +27758,8 @@ var DeployEngine = class {
26634
27758
  );
26635
27759
  return {
26636
27760
  state: {
26637
- version: 1,
26638
- ...this.stackRegion && { region: this.stackRegion },
27761
+ version: STATE_SCHEMA_VERSION_CURRENT,
27762
+ region: this.stackRegion,
26639
27763
  stackName: currentState.stackName,
26640
27764
  resources: newResources,
26641
27765
  outputs,
@@ -26764,7 +27888,9 @@ var DeployEngine = class {
26764
27888
  ` Rollback: Deleting created resource ${op.logicalId} (${op.resourceType})`
26765
27889
  );
26766
27890
  const provider = this.providerRegistry.getProvider(op.resourceType);
26767
- await provider.delete(op.logicalId, op.physicalId, op.resourceType, op.properties);
27891
+ await provider.delete(op.logicalId, op.physicalId, op.resourceType, op.properties, {
27892
+ expectedRegion: this.stackRegion
27893
+ });
26768
27894
  delete stateResources[op.logicalId];
26769
27895
  this.logger.info(` Rollback: ${op.logicalId} deleted successfully`);
26770
27896
  break;
@@ -26906,7 +28032,8 @@ var DeployEngine = class {
26906
28032
  logicalId,
26907
28033
  currentResource.physicalId,
26908
28034
  resourceType,
26909
- currentResource.properties
28035
+ currentResource.properties,
28036
+ { expectedRegion: this.stackRegion }
26910
28037
  );
26911
28038
  this.logger.info(` \u2713 Old resource deleted`);
26912
28039
  } catch (deleteError) {
@@ -26955,7 +28082,8 @@ var DeployEngine = class {
26955
28082
  logicalId,
26956
28083
  currentResource.physicalId,
26957
28084
  resourceType,
26958
- currentProps
28085
+ currentProps,
28086
+ { expectedRegion: this.stackRegion }
26959
28087
  );
26960
28088
  } catch (deleteError) {
26961
28089
  const deleteMsg = deleteError instanceof Error ? deleteError.message : String(deleteError);
@@ -27026,7 +28154,8 @@ var DeployEngine = class {
27026
28154
  logicalId,
27027
28155
  currentResource.physicalId,
27028
28156
  resourceType,
27029
- currentResource.properties
28157
+ currentResource.properties,
28158
+ { expectedRegion: this.stackRegion }
27030
28159
  ),
27031
28160
  logicalId,
27032
28161
  3,
@@ -27642,19 +28771,18 @@ async function diffCommand(stacks, options) {
27642
28771
  logger.info(`
27643
28772
  Calculating diff for stack: ${stackInfo.stackName}`);
27644
28773
  const template = stackInfo.template;
27645
- let currentState;
27646
- const stateResult = await stateBackend.getState(stackInfo.stackName);
27647
- if (stateResult) {
27648
- currentState = stateResult.state;
27649
- } else {
27650
- logger.debug(`No existing state for ${stackInfo.stackName}`);
27651
- currentState = {
27652
- stackName: stackInfo.stackName,
27653
- resources: {},
27654
- outputs: {},
27655
- version: 1,
27656
- lastModified: Date.now()
27657
- };
28774
+ const stackRegion = stackInfo.region || region;
28775
+ const stateResult = await stateBackend.getState(stackInfo.stackName, stackRegion);
28776
+ const currentState = stateResult ? stateResult.state : {
28777
+ stackName: stackInfo.stackName,
28778
+ region: stackRegion,
28779
+ resources: {},
28780
+ outputs: {},
28781
+ version: STATE_SCHEMA_VERSION_CURRENT,
28782
+ lastModified: Date.now()
28783
+ };
28784
+ if (!stateResult) {
28785
+ logger.debug(`No existing state for ${stackInfo.stackName} (${stackRegion})`);
27658
28786
  }
27659
28787
  const diffResolveFn = (value) => intrinsicResolver.resolve(value, {
27660
28788
  template,
@@ -27776,19 +28904,27 @@ async function destroyCommand(stackArgs, options) {
27776
28904
  });
27777
28905
  appStacks = result.stacks.map((s) => ({
27778
28906
  stackName: s.stackName,
27779
- displayName: s.displayName
28907
+ displayName: s.displayName,
28908
+ ...s.region && { region: s.region }
27780
28909
  }));
27781
28910
  } catch {
27782
28911
  logger.debug("Could not synthesize app, falling back to state-based stack list");
27783
28912
  }
27784
28913
  }
27785
- const allStateStacks = await stateBackend.listStacks();
28914
+ const allStateRefs = await stateBackend.listStacks();
27786
28915
  let candidateStacks;
27787
28916
  if (appStacks.length > 0) {
27788
- const stateSet = new Set(allStateStacks);
27789
- candidateStacks = appStacks.filter((s) => stateSet.has(s.stackName));
28917
+ const stateNames = new Set(allStateRefs.map((r) => r.stackName));
28918
+ candidateStacks = appStacks.filter((s) => stateNames.has(s.stackName));
27790
28919
  } else if (stackArgs.length > 0 || options.stack || options.all) {
27791
- candidateStacks = allStateStacks.map((name) => ({ stackName: name }));
28920
+ const seen = /* @__PURE__ */ new Set();
28921
+ candidateStacks = [];
28922
+ for (const ref of allStateRefs) {
28923
+ if (seen.has(ref.stackName))
28924
+ continue;
28925
+ seen.add(ref.stackName);
28926
+ candidateStacks.push({ stackName: ref.stackName });
28927
+ }
27792
28928
  } else {
27793
28929
  throw new Error(
27794
28930
  "Could not determine which stacks belong to this app. Specify stack names explicitly, use --all, or ensure --app / cdk.json is configured."
@@ -27815,10 +28951,38 @@ async function destroyCommand(stackArgs, options) {
27815
28951
  return;
27816
28952
  }
27817
28953
  logger.info(`Found ${stackNames.length} stack(s) to destroy: ${stackNames.join(", ")}`);
28954
+ const stateRefsByName = /* @__PURE__ */ new Map();
28955
+ for (const ref of allStateRefs) {
28956
+ const arr = stateRefsByName.get(ref.stackName) ?? [];
28957
+ arr.push(ref);
28958
+ stateRefsByName.set(ref.stackName, arr);
28959
+ }
27818
28960
  for (const stackName of stackNames) {
27819
28961
  logger.info(`
27820
28962
  Preparing to destroy stack: ${stackName}`);
27821
- const stateResult = await stateBackend.getState(stackName);
28963
+ const refs = stateRefsByName.get(stackName) ?? [];
28964
+ const synthStack = appStacks.find((s) => s.stackName === stackName);
28965
+ const synthRegion = synthStack?.region;
28966
+ let stackTargetRegion;
28967
+ if (refs.length === 0) {
28968
+ logger.warn(`No state found for stack ${stackName}, skipping`);
28969
+ continue;
28970
+ } else if (refs.length === 1) {
28971
+ const onlyRegion = refs[0]?.region;
28972
+ if (!onlyRegion) {
28973
+ stackTargetRegion = region;
28974
+ } else {
28975
+ stackTargetRegion = onlyRegion;
28976
+ }
28977
+ } else if (synthRegion && refs.some((r) => r.region === synthRegion)) {
28978
+ stackTargetRegion = synthRegion;
28979
+ } else {
28980
+ const regions = refs.map((r) => r.region ?? "(legacy)").join(", ");
28981
+ throw new Error(
28982
+ `Stack '${stackName}' has state in multiple regions: ${regions}. Use 'cdkd state rm ${stackName} --region <region>' to remove cdkd's record for one region, or run destroy from a CDK app whose env.region matches one of them.`
28983
+ );
28984
+ }
28985
+ const stateResult = await stateBackend.getState(stackName, stackTargetRegion);
27822
28986
  if (!stateResult) {
27823
28987
  logger.warn(`No state found for stack ${stackName}, skipping`);
27824
28988
  continue;
@@ -27827,7 +28991,7 @@ Preparing to destroy stack: ${stackName}`);
27827
28991
  const resourceCount = Object.keys(currentState.resources).length;
27828
28992
  if (resourceCount === 0) {
27829
28993
  logger.info(`Stack ${stackName} has no resources, cleaning up state...`);
27830
- await stateBackend.deleteState(stackName);
28994
+ await stateBackend.deleteState(stackName, stackTargetRegion);
27831
28995
  logger.info("\u2713 State deleted");
27832
28996
  continue;
27833
28997
  }
@@ -27852,7 +29016,7 @@ Are you sure you want to destroy stack "${stackName}" and delete all ${resourceC
27852
29016
  continue;
27853
29017
  }
27854
29018
  }
27855
- const stackRegion = currentState.region;
29019
+ const stackRegion = stackTargetRegion;
27856
29020
  let destroyProviderRegistry = providerRegistry;
27857
29021
  let destroyAwsClients;
27858
29022
  if (stackRegion && stackRegion !== region) {
@@ -27870,7 +29034,7 @@ Are you sure you want to destroy stack "${stackName}" and delete all ${resourceC
27870
29034
  }
27871
29035
  logger.info(`
27872
29036
  Acquiring lock for stack ${stackName}...`);
27873
- await lockManager.acquireLock(stackName, "destroy");
29037
+ await lockManager.acquireLock(stackName, stackRegion, void 0, "destroy");
27874
29038
  const renderer = getLiveRenderer();
27875
29039
  renderer.start();
27876
29040
  try {
@@ -27946,7 +29110,8 @@ Acquiring lock for stack ${stackName}...`);
27946
29110
  logicalId,
27947
29111
  resource.physicalId,
27948
29112
  resource.resourceType,
27949
- resource.properties
29113
+ resource.properties,
29114
+ { expectedRegion: currentState.region }
27950
29115
  );
27951
29116
  lastDeleteError = null;
27952
29117
  break;
@@ -27985,7 +29150,7 @@ Acquiring lock for stack ${stackName}...`);
27985
29150
  await Promise.all(deletePromises);
27986
29151
  }
27987
29152
  if (errorCount === 0) {
27988
- await stateBackend.deleteState(stackName);
29153
+ await stateBackend.deleteState(stackName, stackRegion);
27989
29154
  logger.debug("State deleted");
27990
29155
  } else {
27991
29156
  logger.warn(`${errorCount} resource(s) failed to delete. State preserved.`);
@@ -27997,7 +29162,7 @@ Acquiring lock for stack ${stackName}...`);
27997
29162
  } finally {
27998
29163
  renderer.stop();
27999
29164
  logger.debug("Releasing lock...");
28000
- await lockManager.releaseLock(stackName);
29165
+ await lockManager.releaseLock(stackName, stackRegion);
28001
29166
  if (destroyAwsClients) {
28002
29167
  destroyAwsClients.destroy();
28003
29168
  process.env["AWS_REGION"] = region;
@@ -28058,7 +29223,7 @@ function createPublishAssetsCommand() {
28058
29223
  }
28059
29224
 
28060
29225
  // src/cli/commands/force-unlock.ts
28061
- import { Command as Command8 } from "commander";
29226
+ import { Command as Command8, Option as Option3 } from "commander";
28062
29227
  init_aws_clients();
28063
29228
  async function forceUnlockCommand(stackArgs, options) {
28064
29229
  const logger = getLogger();
@@ -28082,17 +29247,33 @@ async function forceUnlockCommand(stackArgs, options) {
28082
29247
  prefix: options.statePrefix
28083
29248
  };
28084
29249
  const lockManager = new LockManager(awsClients.s3, stateConfig);
29250
+ const stateBackend = new S3StateBackend(awsClients.s3, stateConfig);
28085
29251
  for (const stackName of stackPatterns) {
28086
- logger.info(`Force-unlocking stack: ${stackName}`);
28087
- try {
28088
- await lockManager.forceReleaseLock(stackName);
28089
- logger.info(`\u2713 Lock released for stack: ${stackName}`);
28090
- } catch (error) {
28091
- const message = error instanceof Error ? error.message : String(error);
28092
- if (message.includes("No lock found") || message.includes("NoSuchKey")) {
28093
- logger.info(`No lock found for stack: ${stackName}`);
29252
+ let regionsToTry;
29253
+ if (options.stackRegion) {
29254
+ regionsToTry = [options.stackRegion];
29255
+ } else {
29256
+ const refs = await stateBackend.listStacks();
29257
+ const matched = refs.filter((r) => r.stackName === stackName);
29258
+ if (matched.length === 0) {
29259
+ regionsToTry = [region];
28094
29260
  } else {
28095
- logger.error(`Failed to unlock stack ${stackName}: ${message}`);
29261
+ regionsToTry = matched.map((r) => r.region);
29262
+ }
29263
+ }
29264
+ for (const r of regionsToTry) {
29265
+ const where = r ? `${stackName} (${r})` : `${stackName} (legacy lock key)`;
29266
+ logger.info(`Force-unlocking stack: ${where}`);
29267
+ try {
29268
+ await lockManager.forceReleaseLock(stackName, r);
29269
+ logger.info(`\u2713 Lock released for stack: ${where}`);
29270
+ } catch (error) {
29271
+ const message = error instanceof Error ? error.message : String(error);
29272
+ if (message.includes("No lock found") || message.includes("NoSuchKey")) {
29273
+ logger.info(`No lock found for stack: ${where}`);
29274
+ } else {
29275
+ logger.error(`Failed to unlock stack ${where}: ${message}`);
29276
+ }
28096
29277
  }
28097
29278
  }
28098
29279
  }
@@ -28101,15 +29282,47 @@ async function forceUnlockCommand(stackArgs, options) {
28101
29282
  }
28102
29283
  }
28103
29284
  function createForceUnlockCommand() {
28104
- const cmd = new Command8("force-unlock").description("Force-release a stale lock on a stack").argument("[stacks...]", "Stack name(s) to unlock").action(withErrorHandling(forceUnlockCommand));
29285
+ const cmd = new Command8("force-unlock").description("Force-release a stale lock on a stack").argument("[stacks...]", "Stack name(s) to unlock").addOption(
29286
+ new Option3(
29287
+ "--stack-region <region>",
29288
+ "Stack region whose lock to release (use when the same stack name has locks in multiple regions). Defaults to all regions where the stack has state."
29289
+ )
29290
+ ).action(withErrorHandling(forceUnlockCommand));
28105
29291
  [...commonOptions, ...stateOptions, ...stackOptions].forEach((opt) => cmd.addOption(opt));
28106
29292
  return cmd;
28107
29293
  }
28108
29294
 
28109
29295
  // src/cli/commands/state.ts
28110
29296
  import * as readline2 from "node:readline/promises";
28111
- import { Command as Command9 } from "commander";
29297
+ import { Command as Command9, Option as Option4 } from "commander";
28112
29298
  init_aws_clients();
29299
+ function formatStackRef(ref) {
29300
+ return ref.region ? `${ref.stackName} (${ref.region})` : ref.stackName;
29301
+ }
29302
+ function resolveSingleRegion(stackName, refs, requestedRegion) {
29303
+ const matches = refs.filter((r) => r.stackName === stackName);
29304
+ if (matches.length === 0) {
29305
+ throw new Error(
29306
+ `No state found for stack '${stackName}'. Run 'cdkd state list' to see available stacks.`
29307
+ );
29308
+ }
29309
+ if (requestedRegion) {
29310
+ const ref = matches.find((r) => r.region === requestedRegion);
29311
+ if (!ref) {
29312
+ const seen = matches.map((r) => r.region ?? "(legacy)").join(", ");
29313
+ throw new Error(
29314
+ `No state found for stack '${stackName}' in region '${requestedRegion}'. Available regions: ${seen}.`
29315
+ );
29316
+ }
29317
+ return ref;
29318
+ }
29319
+ if (matches.length === 1)
29320
+ return matches[0];
29321
+ const regions = matches.map((r) => r.region ?? "(legacy)").join(", ");
29322
+ throw new Error(
29323
+ `Stack '${stackName}' has state in multiple regions: ${regions}. Re-run with --region <region> to disambiguate.`
29324
+ );
29325
+ }
28113
29326
  async function setupStateBackend(options) {
28114
29327
  const awsClients = new AwsClients({
28115
29328
  ...options.region && { region: options.region },
@@ -28131,34 +29344,52 @@ async function setupStateBackend(options) {
28131
29344
  dispose: () => awsClients.destroy()
28132
29345
  };
28133
29346
  }
29347
+ function sortRefs(refs) {
29348
+ return refs.slice().sort((a, b) => {
29349
+ if (a.stackName < b.stackName)
29350
+ return -1;
29351
+ if (a.stackName > b.stackName)
29352
+ return 1;
29353
+ const ar = a.region ?? "\uFFFF";
29354
+ const br = b.region ?? "\uFFFF";
29355
+ if (ar < br)
29356
+ return -1;
29357
+ if (ar > br)
29358
+ return 1;
29359
+ return 0;
29360
+ });
29361
+ }
28134
29362
  async function stateListCommand(options) {
28135
29363
  const logger = getLogger();
28136
29364
  if (options.verbose)
28137
29365
  logger.setLevel("debug");
28138
29366
  const setup = await setupStateBackend(options);
28139
29367
  try {
28140
- const stackNames = (await setup.stateBackend.listStacks()).slice().sort();
29368
+ const refs = sortRefs(await setup.stateBackend.listStacks());
28141
29369
  if (!options.long && !options.json) {
28142
- for (const name of stackNames) {
28143
- process.stdout.write(`${name}
29370
+ for (const ref of refs) {
29371
+ process.stdout.write(`${formatStackRef(ref)}
28144
29372
  `);
28145
29373
  }
28146
29374
  return;
28147
29375
  }
28148
29376
  if (options.json && !options.long) {
28149
- process.stdout.write(`${JSON.stringify(stackNames, null, 2)}
29377
+ const payload = refs.map((r) => ({ stackName: r.stackName, region: r.region ?? null }));
29378
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}
28150
29379
  `);
28151
29380
  return;
28152
29381
  }
28153
29382
  const details = await Promise.all(
28154
- stackNames.map(async (stackName) => {
29383
+ refs.map(async (ref) => {
29384
+ const lookupRegion = ref.region ?? "";
28155
29385
  const [stateResult, locked] = await Promise.all([
28156
- setup.stateBackend.getState(stackName),
28157
- setup.lockManager.isLocked(stackName)
29386
+ lookupRegion ? setup.stateBackend.getState(ref.stackName, lookupRegion) : Promise.resolve(null),
29387
+ setup.lockManager.isLocked(ref.stackName, ref.region)
28158
29388
  ]);
28159
29389
  const state = stateResult?.state;
28160
29390
  return {
28161
- stackName,
29391
+ stackName: ref.stackName,
29392
+ region: ref.region ?? null,
28162
29393
  resourceCount: state ? Object.keys(state.resources).length : 0,
28163
29394
  lastModified: state && typeof state.lastModified === "number" ? new Date(state.lastModified).toISOString() : null,
28164
29395
  locked
@@ -28172,7 +29403,13 @@ async function stateListCommand(options) {
28172
29403
  }
28173
29404
  const lines = [];
28174
29405
  for (const detail of details) {
28175
- lines.push(detail.stackName);
29406
+ lines.push(
29407
+ formatStackRef({
29408
+ stackName: detail.stackName,
29409
+ ...detail.region ? { region: detail.region } : {}
29410
+ })
29411
+ );
29412
+ lines.push(` Region: ${detail.region ?? "(legacy)"}`);
28176
29413
  lines.push(` Resources: ${detail.resourceCount}`);
28177
29414
  lines.push(` Last Modified: ${detail.lastModified ?? "unknown"}`);
28178
29415
  lines.push(` Lock: ${detail.locked ? "locked" : "unlocked"}`);
@@ -28200,10 +29437,17 @@ async function stateResourcesCommand(stackName, options) {
28200
29437
  logger.setLevel("debug");
28201
29438
  const setup = await setupStateBackend(options);
28202
29439
  try {
28203
- const stateResult = await setup.stateBackend.getState(stackName);
29440
+ const refs = await setup.stateBackend.listStacks();
29441
+ const ref = resolveSingleRegion(stackName, refs, options.stackRegion);
29442
+ if (!ref.region) {
29443
+ throw new Error(
29444
+ `Stack '${stackName}' has only a legacy state record without a region. Run 'cdkd deploy ${stackName}' (or any cdkd write) to migrate it to the region-scoped layout, then re-run this command.`
29445
+ );
29446
+ }
29447
+ const stateResult = await setup.stateBackend.getState(stackName, ref.region);
28204
29448
  if (!stateResult) {
28205
29449
  throw new Error(
28206
- `No state found for stack '${stackName}' in s3://${setup.bucket}/${setup.prefix}/. Run 'cdkd state list' to see available stacks.`
29450
+ `No state found for stack '${stackName}' (${ref.region}) in s3://${setup.bucket}/${setup.prefix}/. Run 'cdkd state list' to see available stacks.`
28207
29451
  );
28208
29452
  }
28209
29453
  const resources = stateResult.state.resources ?? {};
@@ -28286,7 +29530,7 @@ function formatLockSummary(lockInfo) {
28286
29530
  return `locked by ${lockInfo.owner}${opStr}, ${expiresStr}`;
28287
29531
  }
28288
29532
  function createStateResourcesCommand() {
28289
- const cmd = new Command9("resources").description("List resources recorded in a stack's state").argument("<stack>", "Stack name (physical CloudFormation name)").option("-l, --long", "Include dependencies and attributes per resource", false).option("--json", "Output as JSON", false).action(withErrorHandling(stateResourcesCommand));
29533
+ const cmd = new Command9("resources").description("List resources recorded in a stack's state").argument("<stack>", "Stack name (physical CloudFormation name)").option("-l, --long", "Include dependencies and attributes per resource", false).option("--json", "Output as JSON", false).addOption(stackRegionOption()).action(withErrorHandling(stateResourcesCommand));
28290
29534
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
28291
29535
  return cmd;
28292
29536
  }
@@ -28296,13 +29540,20 @@ async function stateShowCommand(stackName, options) {
28296
29540
  logger.setLevel("debug");
28297
29541
  const setup = await setupStateBackend(options);
28298
29542
  try {
29543
+ const refs = await setup.stateBackend.listStacks();
29544
+ const ref = resolveSingleRegion(stackName, refs, options.stackRegion);
29545
+ if (!ref.region) {
29546
+ throw new Error(
29547
+ `Stack '${stackName}' has only a legacy state record without a region. Run 'cdkd deploy ${stackName}' (or any cdkd write) to migrate it to the region-scoped layout, then re-run this command.`
29548
+ );
29549
+ }
28299
29550
  const [stateResult, lockInfo] = await Promise.all([
28300
- setup.stateBackend.getState(stackName),
28301
- setup.lockManager.getLockInfo(stackName)
29551
+ setup.stateBackend.getState(stackName, ref.region),
29552
+ setup.lockManager.getLockInfo(stackName, ref.region)
28302
29553
  ]);
28303
29554
  if (!stateResult) {
28304
29555
  throw new Error(
28305
- `No state found for stack '${stackName}' in s3://${setup.bucket}/${setup.prefix}/. Run 'cdkd state list' to see available stacks.`
29556
+ `No state found for stack '${stackName}' (${ref.region}) in s3://${setup.bucket}/${setup.prefix}/. Run 'cdkd state list' to see available stacks.`
28306
29557
  );
28307
29558
  }
28308
29559
  if (options.json) {
@@ -28366,7 +29617,7 @@ async function stateShowCommand(stackName, options) {
28366
29617
  }
28367
29618
  }
28368
29619
  function createStateShowCommand() {
28369
- const cmd = new Command9("show").description("Show the full cdkd state record for a stack (metadata, outputs, resources)").argument("<stack>", "Stack name (physical CloudFormation name)").option("--json", "Output the raw state and lock as JSON", false).action(withErrorHandling(stateShowCommand));
29620
+ const cmd = new Command9("show").description("Show the full cdkd state record for a stack (metadata, outputs, resources)").argument("<stack>", "Stack name (physical CloudFormation name)").option("--json", "Output the raw state and lock as JSON", false).addOption(stackRegionOption()).action(withErrorHandling(stateShowCommand));
28370
29621
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
28371
29622
  return cmd;
28372
29623
  }
@@ -28379,24 +29630,36 @@ async function stateRmCommand(stackArgs, options) {
28379
29630
  }
28380
29631
  const setup = await setupStateBackend(options);
28381
29632
  try {
29633
+ const refs = await setup.stateBackend.listStacks();
28382
29634
  for (const stackName of stackArgs) {
28383
- const exists = await setup.stateBackend.stateExists(stackName);
28384
- if (!exists) {
29635
+ const stackRefs = refs.filter((r) => r.stackName === stackName);
29636
+ if (stackRefs.length === 0) {
28385
29637
  logger.info(`No state found for stack: ${stackName}, skipping`);
28386
29638
  continue;
28387
29639
  }
29640
+ const targets = options.stackRegion ? stackRefs.filter((r) => r.region === options.stackRegion) : stackRefs;
29641
+ if (targets.length === 0) {
29642
+ const seen = stackRefs.map((r) => r.region ?? "(legacy)").join(", ");
29643
+ throw new Error(
29644
+ `No state found for stack '${stackName}' in region '${options.stackRegion}'. Available regions: ${seen}.`
29645
+ );
29646
+ }
28388
29647
  if (!options.force) {
28389
- const locked = await setup.lockManager.isLocked(stackName);
28390
- if (locked) {
28391
- throw new Error(
28392
- `Stack '${stackName}' is locked. Run 'cdkd force-unlock ${stackName}' first, or pass --force to remove anyway.`
28393
- );
29648
+ for (const target of targets) {
29649
+ const locked = await setup.lockManager.isLocked(stackName, target.region);
29650
+ if (locked) {
29651
+ const where = target.region ?? "(legacy)";
29652
+ throw new Error(
29653
+ `Stack '${stackName}' (${where}) is locked. Run 'cdkd force-unlock ${stackName}${target.region ? ` --stack-region ${target.region}` : ""}' first, or pass --force to remove anyway.`
29654
+ );
29655
+ }
28394
29656
  }
28395
29657
  }
28396
29658
  if (!options.yes && !options.force) {
29659
+ const targetList = targets.map((t) => formatStackRef(t)).join(", ");
28397
29660
  process.stdout.write(
28398
29661
  `
28399
- WARNING: This removes cdkd's state record for '${stackName}' only. AWS resources will NOT be deleted.
29662
+ WARNING: This removes cdkd's state record for [${targetList}] only. AWS resources will NOT be deleted.
28400
29663
  Use 'cdkd destroy ${stackName}' if you want to delete the actual resources.
28401
29664
 
28402
29665
  `
@@ -28406,7 +29669,7 @@ Use 'cdkd destroy ${stackName}' if you want to delete the actual resources.
28406
29669
  output: process.stdout
28407
29670
  });
28408
29671
  const answer = await rl.question(
28409
- `Remove state for stack '${stackName}' from s3://${setup.bucket}/${setup.prefix}/? (y/N): `
29672
+ `Remove state for ${targetList} from s3://${setup.bucket}/${setup.prefix}/? (y/N): `
28410
29673
  );
28411
29674
  rl.close();
28412
29675
  const trimmed = answer.trim().toLowerCase();
@@ -28415,16 +29678,28 @@ Use 'cdkd destroy ${stackName}' if you want to delete the actual resources.
28415
29678
  continue;
28416
29679
  }
28417
29680
  }
28418
- await setup.stateBackend.deleteState(stackName);
28419
- await setup.lockManager.forceReleaseLock(stackName);
28420
- logger.info(`\u2713 Removed state for stack: ${stackName}`);
29681
+ for (const target of targets) {
29682
+ if (target.region) {
29683
+ await setup.stateBackend.deleteState(stackName, target.region);
29684
+ await setup.lockManager.forceReleaseLock(stackName, target.region);
29685
+ } else {
29686
+ await setup.lockManager.forceReleaseLock(stackName, void 0);
29687
+ }
29688
+ logger.info(`\u2713 Removed state for stack: ${formatStackRef(target)}`);
29689
+ }
28421
29690
  }
28422
29691
  } finally {
28423
29692
  setup.dispose();
28424
29693
  }
28425
29694
  }
29695
+ function stackRegionOption() {
29696
+ return new Option4(
29697
+ "--stack-region <region>",
29698
+ "Region of the stack record to operate on. Required when the same stack name has state in multiple regions."
29699
+ );
29700
+ }
28426
29701
  function createStateRmCommand() {
28427
- const cmd = new Command9("rm").description("Remove cdkd state for one or more stacks (does NOT delete AWS resources)").argument("<stacks...>", "Stack name(s) to remove from state").option("-f, --force", "Skip confirmation and remove even if the stack is locked", false).action(withErrorHandling(stateRmCommand));
29702
+ const cmd = new Command9("rm").description("Remove cdkd state for one or more stacks (does NOT delete AWS resources)").argument("<stacks...>", "Stack name(s) to remove from state").option("-f, --force", "Skip confirmation and remove even if the stack is locked", false).addOption(stackRegionOption()).action(withErrorHandling(stateRmCommand));
28428
29703
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
28429
29704
  return cmd;
28430
29705
  }
@@ -28462,7 +29737,7 @@ function reorderArgs(argv) {
28462
29737
  }
28463
29738
  async function main() {
28464
29739
  const program = new Command10();
28465
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.7.0");
29740
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.9.0");
28466
29741
  program.addCommand(createBootstrapCommand());
28467
29742
  program.addCommand(createSynthCommand());
28468
29743
  program.addCommand(createListCommand());