@geekmidas/cli 10.0.0-alpha.95 → 10.0.0-alpha.96

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.
Files changed (123) hide show
  1. package/dist/{GoDaddyProvider-BwW51hPm.mjs → GoDaddyProvider-BsUKjGcg.mjs} +2 -2
  2. package/dist/{GoDaddyProvider-BwW51hPm.mjs.map → GoDaddyProvider-BsUKjGcg.mjs.map} +1 -1
  3. package/dist/{GoDaddyProvider-C_DgIkMv.cjs → GoDaddyProvider-DyHXHDHe.cjs} +2 -2
  4. package/dist/{GoDaddyProvider-C_DgIkMv.cjs.map → GoDaddyProvider-DyHXHDHe.cjs.map} +1 -1
  5. package/dist/{LegacyStateStore-D5DarNA2.mjs → LegacyStateStore-B6SOqRFi.mjs} +3 -3
  6. package/dist/{LegacyStateStore-D5DarNA2.mjs.map → LegacyStateStore-B6SOqRFi.mjs.map} +1 -1
  7. package/dist/{LegacyStateStore-BawjE_5n.cjs → LegacyStateStore-BnMcONE0.cjs} +3 -3
  8. package/dist/{LegacyStateStore-BawjE_5n.cjs.map → LegacyStateStore-BnMcONE0.cjs.map} +1 -1
  9. package/dist/{LocalStateStore-YjIqlgAz.mjs → LocalStateStore-6CrsZzqI.mjs} +2 -2
  10. package/dist/{LocalStateStore-YjIqlgAz.mjs.map → LocalStateStore-6CrsZzqI.mjs.map} +1 -1
  11. package/dist/{LocalStateStore-D8fu5WiF.cjs → LocalStateStore-pd4bbdJJ.cjs} +2 -2
  12. package/dist/{LocalStateStore-D8fu5WiF.cjs.map → LocalStateStore-pd4bbdJJ.cjs.map} +1 -1
  13. package/dist/{S3StateStore-Cm-v2IPI.cjs → S3StateStore-CoGk6ZKU.cjs} +2 -2
  14. package/dist/{S3StateStore-Cm-v2IPI.cjs.map → S3StateStore-CoGk6ZKU.cjs.map} +1 -1
  15. package/dist/{S3StateStore-CjTAVsWh.mjs → S3StateStore-DP_7Q4yb.mjs} +2 -2
  16. package/dist/{S3StateStore-CjTAVsWh.mjs.map → S3StateStore-DP_7Q4yb.mjs.map} +1 -1
  17. package/dist/{SSMStateStore-BhtM6jOh.mjs → SSMStateStore-Bo6_KDYY.mjs} +2 -2
  18. package/dist/{SSMStateStore-BhtM6jOh.mjs.map → SSMStateStore-Bo6_KDYY.mjs.map} +1 -1
  19. package/dist/{SSMStateStore-BS4BiZCN.cjs → SSMStateStore-D9s4_Do3.cjs} +2 -2
  20. package/dist/{SSMStateStore-BS4BiZCN.cjs.map → SSMStateStore-D9s4_Do3.cjs.map} +1 -1
  21. package/dist/{add-ylx8dejw.mjs → add-BQS2QZHp.mjs} +4 -4
  22. package/dist/{add-ylx8dejw.mjs.map → add-BQS2QZHp.mjs.map} +1 -1
  23. package/dist/{add-D-3IKT7W.cjs → add-CjURJ-vI.cjs} +4 -4
  24. package/dist/{add-D-3IKT7W.cjs.map → add-CjURJ-vI.cjs.map} +1 -1
  25. package/dist/{bundler-ywR3C1B_.mjs → bundler-BMM3UsGm.mjs} +5 -5
  26. package/dist/{bundler-ywR3C1B_.mjs.map → bundler-BMM3UsGm.mjs.map} +1 -1
  27. package/dist/{bundler-CI3elfZS.cjs → bundler-DmLJQ7AY.cjs} +5 -5
  28. package/dist/{bundler-CI3elfZS.cjs.map → bundler-DmLJQ7AY.cjs.map} +1 -1
  29. package/dist/{cli-Dewt2T21.cjs → cli-C2c-jOVK.cjs} +2 -2
  30. package/dist/{cli-Dewt2T21.cjs.map → cli-C2c-jOVK.cjs.map} +1 -1
  31. package/dist/{cli-BCSuCfQk.mjs → cli-Ce6NgaQF.mjs} +2 -2
  32. package/dist/{cli-BCSuCfQk.mjs.map → cli-Ce6NgaQF.mjs.map} +1 -1
  33. package/dist/{compose-C4js1JDv.mjs → compose-BJDPFBAN.mjs} +7 -7
  34. package/dist/{compose-C4js1JDv.mjs.map → compose-BJDPFBAN.mjs.map} +1 -1
  35. package/dist/{compose-Dd0XphvF.cjs → compose-ETFKqkTu.cjs} +7 -7
  36. package/dist/{compose-Dd0XphvF.cjs.map → compose-ETFKqkTu.cjs.map} +1 -1
  37. package/dist/config.cjs +1 -1
  38. package/dist/config.mjs +1 -1
  39. package/dist/deploy/public.cjs +7 -7
  40. package/dist/deploy/public.mjs +7 -7
  41. package/dist/{deploy-Bw1FjX_x.cjs → deploy-BdN0ym2i.cjs} +200 -46
  42. package/dist/deploy-BdN0ym2i.cjs.map +1 -0
  43. package/dist/{deploy-B0drCD6s.mjs → deploy-DktQl7ti.mjs} +200 -46
  44. package/dist/deploy-DktQl7ti.mjs.map +1 -0
  45. package/dist/{dns-X3oqEV4C.mjs → dns-BjHECDO7.mjs} +5 -5
  46. package/dist/{dns-X3oqEV4C.mjs.map → dns-BjHECDO7.mjs.map} +1 -1
  47. package/dist/{dns-Bmk7P9c2.cjs → dns-C-X6PLkV.cjs} +3 -3
  48. package/dist/{dns-Bmk7P9c2.cjs.map → dns-C-X6PLkV.cjs.map} +1 -1
  49. package/dist/{dns-idXfyCay.mjs → dns-C2EeR6n2.mjs} +3 -3
  50. package/dist/{dns-idXfyCay.mjs.map → dns-C2EeR6n2.mjs.map} +1 -1
  51. package/dist/{dns-CyMIq6to.cjs → dns-ClI7-d5g.cjs} +5 -5
  52. package/dist/{dns-CyMIq6to.cjs.map → dns-ClI7-d5g.cjs.map} +1 -1
  53. package/dist/{dokploy-DaT0mgLU.cjs → dokploy-CD_Bc4EV.cjs} +2 -2
  54. package/dist/{dokploy-DaT0mgLU.cjs.map → dokploy-CD_Bc4EV.cjs.map} +1 -1
  55. package/dist/{dokploy-CZYxXeMa.mjs → dokploy-CxtkS5hl.mjs} +2 -2
  56. package/dist/{dokploy-CZYxXeMa.mjs.map → dokploy-CxtkS5hl.mjs.map} +1 -1
  57. package/dist/{encryption-D0ucY4O7.mjs → encryption-BgQd4f2S.mjs} +3 -3
  58. package/dist/{encryption-D0ucY4O7.mjs.map → encryption-BgQd4f2S.mjs.map} +1 -1
  59. package/dist/{encryption-Dp0IvLVX.cjs → encryption-CqVfz-m7.cjs} +3 -3
  60. package/dist/{encryption-Dp0IvLVX.cjs.map → encryption-CqVfz-m7.cjs.map} +1 -1
  61. package/dist/{engine-WY6j_kvA.cjs → engine-OVVKtPmW.cjs} +4 -4
  62. package/dist/{engine-WY6j_kvA.cjs.map → engine-OVVKtPmW.cjs.map} +1 -1
  63. package/dist/{engine-BcuhnyQO.mjs → engine-YlXzlTUl.mjs} +4 -4
  64. package/dist/{engine-BcuhnyQO.mjs.map → engine-YlXzlTUl.mjs.map} +1 -1
  65. package/dist/{github-DgmW83OP.cjs → github-CsMloeSL.cjs} +5 -5
  66. package/dist/{github-DgmW83OP.cjs.map → github-CsMloeSL.cjs.map} +1 -1
  67. package/dist/{github-DUGA57du.mjs → github-DuuvqI3Z.mjs} +5 -5
  68. package/dist/{github-DUGA57du.mjs.map → github-DuuvqI3Z.mjs.map} +1 -1
  69. package/dist/index.cjs +48 -48
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.mjs +48 -48
  72. package/dist/index.mjs.map +1 -1
  73. package/dist/{migrate-afgRL3Hg.cjs → migrate-BAmJeVq1.cjs} +3 -3
  74. package/dist/{migrate-afgRL3Hg.cjs.map → migrate-BAmJeVq1.cjs.map} +1 -1
  75. package/dist/{migrate-BBcG1dhS.cjs → migrate-BdkoNdIu.cjs} +3 -3
  76. package/dist/{migrate-BBcG1dhS.cjs.map → migrate-BdkoNdIu.cjs.map} +1 -1
  77. package/dist/{migrate-CZ-v5WV0.mjs → migrate-CB8B6W9y.mjs} +3 -3
  78. package/dist/{migrate-CZ-v5WV0.mjs.map → migrate-CB8B6W9y.mjs.map} +1 -1
  79. package/dist/{migrate-D6PlNv2o.mjs → migrate-CsPFHgTs.mjs} +3 -3
  80. package/dist/{migrate-D6PlNv2o.mjs.map → migrate-CsPFHgTs.mjs.map} +1 -1
  81. package/dist/openapi.cjs +1 -1
  82. package/dist/openapi.mjs +1 -1
  83. package/dist/{resolve-CFb4Navi.cjs → resolve-BDatNOoG.cjs} +3 -3
  84. package/dist/{resolve-CFb4Navi.cjs.map → resolve-BDatNOoG.cjs.map} +1 -1
  85. package/dist/{resolve-DakDhr6Z.mjs → resolve-CUne0qOm.mjs} +3 -3
  86. package/dist/{resolve-DakDhr6Z.mjs.map → resolve-CUne0qOm.mjs.map} +1 -1
  87. package/dist/{rollback-B1TIpjdE.mjs → rollback-CCdEEEcF.mjs} +5 -5
  88. package/dist/{rollback-B1TIpjdE.mjs.map → rollback-CCdEEEcF.mjs.map} +1 -1
  89. package/dist/{rollback-B-1dPP0m.cjs → rollback-CuG3DkTY.cjs} +5 -5
  90. package/dist/{rollback-B-1dPP0m.cjs.map → rollback-CuG3DkTY.cjs.map} +1 -1
  91. package/dist/{secrets-CPM0Qffe.cjs → secrets-C5qROlbp.cjs} +3 -3
  92. package/dist/{secrets-CPM0Qffe.cjs.map → secrets-C5qROlbp.cjs.map} +1 -1
  93. package/dist/{secrets-oI9ClAPR.mjs → secrets-ZikTIxEm.mjs} +3 -3
  94. package/dist/{secrets-oI9ClAPR.mjs.map → secrets-ZikTIxEm.mjs.map} +1 -1
  95. package/dist/{seeds-CnXkgKUv.cjs → seeds-BjXW-Krf.cjs} +4 -4
  96. package/dist/{seeds-CnXkgKUv.cjs.map → seeds-BjXW-Krf.cjs.map} +1 -1
  97. package/dist/{seeds-CoKkrSny.mjs → seeds-CbJSne1A.mjs} +4 -4
  98. package/dist/{seeds-CoKkrSny.mjs.map → seeds-CbJSne1A.mjs.map} +1 -1
  99. package/dist/{sniffer-CFRzIrby.mjs → sniffer-DQGBYY8i.mjs} +7 -7
  100. package/dist/{sniffer-CFRzIrby.mjs.map → sniffer-DQGBYY8i.mjs.map} +1 -1
  101. package/dist/{sniffer-vbiQ2Isn.cjs → sniffer-NPD1dyab.cjs} +7 -7
  102. package/dist/{sniffer-vbiQ2Isn.cjs.map → sniffer-NPD1dyab.cjs.map} +1 -1
  103. package/dist/{stack-B7O975TR.mjs → stack-BFq3K-_z.mjs} +3 -3
  104. package/dist/{stack-B7O975TR.mjs.map → stack-BFq3K-_z.mjs.map} +1 -1
  105. package/dist/{stack-1g57OfOw.cjs → stack-DOVvEd1T.cjs} +3 -3
  106. package/dist/{stack-1g57OfOw.cjs.map → stack-DOVvEd1T.cjs.map} +1 -1
  107. package/dist/{stages-Bt4OggMm.mjs → stages-BEn2ArJZ.mjs} +4 -4
  108. package/dist/{stages-Bt4OggMm.mjs.map → stages-BEn2ArJZ.mjs.map} +1 -1
  109. package/dist/{stages-BujUB4we.cjs → stages-Cw6urZyJ.cjs} +4 -4
  110. package/dist/{stages-BujUB4we.cjs.map → stages-Cw6urZyJ.cjs.map} +1 -1
  111. package/dist/target/public.cjs +1 -1
  112. package/dist/target/public.mjs +1 -1
  113. package/dist/workspace/index.cjs +1 -1
  114. package/dist/workspace/index.mjs +1 -1
  115. package/dist/{workspace-DWK1PTXP.mjs → workspace-CqFUjObk.mjs} +54 -2
  116. package/dist/workspace-CqFUjObk.mjs.map +1 -0
  117. package/dist/{workspace-cSyi5nb1.cjs → workspace-DDkm4y6D.cjs} +71 -1
  118. package/dist/workspace-DDkm4y6D.cjs.map +1 -0
  119. package/package.json +15 -15
  120. package/dist/deploy-B0drCD6s.mjs.map +0 -1
  121. package/dist/deploy-Bw1FjX_x.cjs.map +0 -1
  122. package/dist/workspace-DWK1PTXP.mjs.map +0 -1
  123. package/dist/workspace-cSyi5nb1.cjs.map +0 -1
@@ -4,15 +4,15 @@ import { r as storedCredentials } from "./credentials-B5N5yKk6.mjs";
4
4
  import { f as withOutput, r as discover } from "./discover-qjFapetL.mjs";
5
5
  import { c as withSandbox } from "./sandbox-CbVB7uOD.mjs";
6
6
  import { n as derivedApps, t as appKey } from "./derive-B4vDpzgP.mjs";
7
- import { $ as RotationInProgress, G as checkS3Config, J as BucketNameUnavailable, K as AccessKeyLimit, L as resolveStageTelemetry, N as assertDeployedStage, Q as RecordedBucketUnreachable, U as stageProvider, W as S3_PROVISIONING, X as IamUserNotOwned, Y as BucketOwnedByAnotherProject, Z as ProvisionedBucketUnreachable, et as S3RegionRequired, q as BucketKeySetElsewhere, u as getAppBuildOrder } from "./workspace-DWK1PTXP.mjs";
7
+ import { $ as IamUserNotOwned, G as checkS3Config, J as BucketNameUnavailable, K as AccessKeyLimit, L as resolveStageTelemetry, N as assertDeployedStage, Q as BucketRegionUnknown, U as stageProvider, W as S3_PROVISIONING, X as BucketProbeFailed, Y as BucketOwnedByAnotherProject, Z as BucketRegionMismatch, et as ProvisionedBucketUnreachable, nt as RotationInProgress, q as BucketKeySetElsewhere, rt as S3RegionRequired, tt as RecordedBucketUnreachable, u as getAppBuildOrder } from "./workspace-CqFUjObk.mjs";
8
8
  import { o as builtinTarget } from "./define-6poNqm5O.mjs";
9
9
  import { findWorkspaceRoot, loadWorkspaceConfig } from "./config.mjs";
10
- import { E as reportDevServices, F as DeployJournal, H as createComposeState, In as applicationName, It as appEnvKeys, Ln as deployIdentity, N as stageProviderNotes, O as stageServiceDeclarations, On as secretsStoreFor, P as PROVIDER_KINDS, T as externalServices, U as createEmptyState, Wn as initStageSecrets, b as DevServicesNeedServerTarget, ct as constructGlobs, fn as usesTelemetry, j as assertStageProvidersEnabled, nr as isMainFrontendApp, rr as resolveHost, tr as NoDomainForStage, w as devServicesUsed, x as ExternalServicesNotConfigured } from "./seeds-CoKkrSny.mjs";
11
- import { a as parseTargetOptions, o as resolveTarget } from "./resolve-DakDhr6Z.mjs";
10
+ import { E as reportDevServices, F as DeployJournal, H as createComposeState, In as applicationName, It as appEnvKeys, Ln as deployIdentity, N as stageProviderNotes, O as stageServiceDeclarations, On as secretsStoreFor, P as PROVIDER_KINDS, T as externalServices, U as createEmptyState, Wn as initStageSecrets, b as DevServicesNeedServerTarget, ct as constructGlobs, fn as usesTelemetry, j as assertStageProvidersEnabled, nr as isMainFrontendApp, rr as resolveHost, tr as NoDomainForStage, w as devServicesUsed, x as ExternalServicesNotConfigured } from "./seeds-CbJSne1A.mjs";
11
+ import { a as parseTargetOptions, o as resolveTarget } from "./resolve-CUne0qOm.mjs";
12
12
  import { t as LocalSandbox } from "./local-BwC2v2Y8.mjs";
13
- import { S as eventError, _ as assertStateOutlivesRun, y as createStateStore } from "./sniffer-CFRzIrby.mjs";
14
- import { c as stageDnsDomains, n as dnsCredentials } from "./dns-idXfyCay.mjs";
15
- import { t as deploysWithCompose } from "./dns-X3oqEV4C.mjs";
13
+ import { S as eventError, _ as assertStateOutlivesRun, y as createStateStore } from "./sniffer-DQGBYY8i.mjs";
14
+ import { c as stageDnsDomains, n as dnsCredentials } from "./dns-C2EeR6n2.mjs";
15
+ import { t as deploysWithCompose } from "./dns-BjHECDO7.mjs";
16
16
  import { n as assertStageCredentials } from "./credentialSchemas-BJFvfkaz.mjs";
17
17
  import { resolve } from "node:path";
18
18
  import { dependentsOf, kebabCase, provideKey, providedKeyFor } from "@geekmidas/manifest";
@@ -447,6 +447,74 @@ function errorName(error) {
447
447
  const e = error;
448
448
  return e?.Code ?? e?.name ?? "";
449
449
  }
450
+ /** The answer an SDK call failed with. */
451
+ function s3Answer(error) {
452
+ const e = error;
453
+ const headers = Object.fromEntries(Object.entries(e?.$response?.headers ?? {}).map(([k, v]) => [k.toLowerCase(), v]));
454
+ const status = e?.$metadata?.httpStatusCode ?? e?.$response?.statusCode;
455
+ const region = headers["x-amz-bucket-region"];
456
+ const requestId = e?.$metadata?.requestId ?? headers["x-amz-request-id"];
457
+ const id2 = e?.$metadata?.extendedRequestId ?? headers["x-amz-id-2"];
458
+ return {
459
+ ...status !== void 0 ? { status } : {},
460
+ name: errorName(error) || "Error",
461
+ message: e?.message ?? String(error),
462
+ ...region ? { region } : {},
463
+ ...requestId ? { requestId } : {},
464
+ ...id2 ? { id2 } : {}
465
+ };
466
+ }
467
+ /**
468
+ * `HTTP 403 Unknown: UnknownError [x-amz-bucket-region: eu-west-1,
469
+ * x-amz-request-id: …, x-amz-id-2: …]` — everything S3 said, for a message.
470
+ */
471
+ function describeAnswer(answer) {
472
+ const said = answer.message && answer.message !== answer.name ? `${answer.name}: ${answer.message}` : answer.name;
473
+ const headers = [
474
+ answer.region && `x-amz-bucket-region: ${answer.region}`,
475
+ answer.requestId && `x-amz-request-id: ${answer.requestId}`,
476
+ answer.id2 && `x-amz-id-2: ${answer.id2}`
477
+ ].filter(Boolean);
478
+ return `${answer.status === void 0 ? "no response" : `HTTP ${answer.status}`} ${said}` + (headers.length > 0 ? ` [${headers.join(", ")}]` : "");
479
+ }
480
+ /** `HeadBucket`: the bucket's region when it answers, else what S3 said. */
481
+ async function headBucket(S3, client, bucket) {
482
+ try {
483
+ const out = await client.send(new S3.HeadBucketCommand({ Bucket: bucket }));
484
+ return {
485
+ ok: true,
486
+ ...out.BucketRegion ? { region: out.BucketRegion } : {}
487
+ };
488
+ } catch (error) {
489
+ return {
490
+ ok: false,
491
+ ...s3Answer(error)
492
+ };
493
+ }
494
+ }
495
+ /** `GetBucketLocation`'s answer as a region: none is us-east-1, `EU` eu-west-1. */
496
+ async function bucketLocation(S3, client, bucket) {
497
+ try {
498
+ const location = (await client.send(new S3.GetBucketLocationCommand({ Bucket: bucket }))).LocationConstraint;
499
+ return { region: !location ? "us-east-1" : location === "EU" ? "eu-west-1" : location };
500
+ } catch (error) {
501
+ return { failed: s3Answer(error) };
502
+ }
503
+ }
504
+ /**
505
+ * Where `bucket` is: `HeadBucket`'s `x-amz-bucket-region` — on a 200, a 301
506
+ * or a 400 alike — else `GetBucketLocation`.
507
+ *
508
+ * @throws {BucketRegionUnknown}
509
+ */
510
+ async function bucketRegion(clients, bucket) {
511
+ const { S3, s3 } = clients;
512
+ const head = await headBucket(S3, s3, bucket);
513
+ if (head.region) return head.region;
514
+ const location = await bucketLocation(S3, s3, bucket);
515
+ if ("region" in location) return location.region;
516
+ throw new BucketRegionUnknown(bucket, head.ok ? "HTTP 200 without x-amz-bucket-region" : describeAnswer(head), describeAnswer(location.failed));
517
+ }
450
518
  /** A read whose "there is none" answer is one of `missing`. */
451
519
  async function readOr(read, missing) {
452
520
  try {
@@ -500,7 +568,7 @@ function identityTags(ctx, id) {
500
568
  }
501
569
  ];
502
570
  }
503
- async function ensureBucket(ctx, clients, construct) {
571
+ async function ensureBucket(ctx, stageClients, construct) {
504
572
  const { id } = construct;
505
573
  const nameInput = {
506
574
  scope: ctx.identity.scope,
@@ -509,7 +577,13 @@ async function ensureBucket(ctx, clients, construct) {
509
577
  };
510
578
  const versioning = ctx.config.versioning ?? construct.versioned;
511
579
  const open = [...new Set(construct.servers.flatMap((s) => s.open))].sort();
512
- const { bucket, exists } = await ensureBucketName(ctx, clients, id, bucketName(nameInput));
580
+ const located = await ensureBucketName(ctx, stageClients, id, bucketName(nameInput));
581
+ const { bucket, exists } = located;
582
+ let clients = stageClients;
583
+ if (located.region !== stageClients.region) {
584
+ clients = await makeClients(ctx.credential, located.region);
585
+ ctx.log(` '${bucket}' is in ${located.region}, not ${stageClients.region}: '${id}' is reached and addressed there`);
586
+ }
513
587
  await ensureBucketSettings(ctx, clients, {
514
588
  id,
515
589
  bucket,
@@ -517,10 +591,11 @@ async function ensureBucket(ctx, clients, construct) {
517
591
  versioning,
518
592
  open
519
593
  });
594
+ const user = await ensureUser(ctx, clients, id, iamUserName(nameInput), userPolicy(bucket, { versioning }));
520
595
  await ensureAccessKey(ctx, clients, {
521
596
  id,
522
597
  bucket,
523
- ...await ensureUser(ctx, clients, id, iamUserName(nameInput), userPolicy(bucket, { versioning }))
598
+ ...user
524
599
  });
525
600
  const publicUrl = bucketPublicUrl(bucket, clients.region, clients.endpoint);
526
601
  for (const server of construct.servers) {
@@ -557,26 +632,32 @@ async function ensureBucketName(ctx, clients, id, good) {
557
632
  const { S3, s3, region } = clients;
558
633
  const key = `s3-bucket:${id}`;
559
634
  const record = ctx.state.record(key);
560
- const entry = (name) => ({
635
+ const entry = (name, where) => ({
561
636
  key,
562
637
  type: "s3-bucket",
563
638
  data: {
564
639
  name,
565
- region
640
+ region: where
566
641
  }
567
642
  });
568
- /** Whether this account holds `name`: yes, no, or someone else does. */
643
+ /**
644
+ * Whether this account holds `name`: yes, no, or someone else does. Read
645
+ * by status — a `HEAD` has no body, so its error is named `Unknown` — and,
646
+ * when S3 says the bucket is in another region, asked again there.
647
+ */
569
648
  const probe = async (name) => {
570
- try {
571
- await s3.send(new S3.HeadBucketCommand({ Bucket: name }));
572
- return "ours";
573
- } catch (error) {
574
- const name = errorName(error);
575
- if (name === "NotFound" || name === "NoSuchBucket") return "none";
576
- if (name === "Forbidden" || name === "AccessDenied") return "taken";
577
- throw error;
649
+ let head = await headBucket(S3, s3, name);
650
+ if (!head.ok && head.region && head.region !== region && (head.status === 301 || head.status === 400)) {
651
+ const there = await makeClients(ctx.credential, head.region);
652
+ head = await headBucket(there.S3, there.s3, name);
578
653
  }
654
+ if (head.ok) return "ours";
655
+ if (head.status === 404) return "none";
656
+ if (head.status === 403) return "taken";
657
+ throw new BucketProbeFailed(name, describeAnswer(head));
579
658
  };
659
+ /** Where the bucket is now — read, not assumed: it may be adopted. */
660
+ const located = async (name, made) => ctx.dryRun && made === "created" ? region : bucketRegion(clients, name);
580
661
  const create = async (name) => {
581
662
  if (ctx.dryRun) {
582
663
  const found = await probe(name);
@@ -590,7 +671,7 @@ async function ensureBucketName(ctx, clients, id, good) {
590
671
  }
591
672
  return found;
592
673
  }
593
- await ctx.state.pending(entry(name));
674
+ await ctx.state.pending(entry(name, region));
594
675
  try {
595
676
  await s3.send(new S3.CreateBucketCommand({
596
677
  Bucket: name,
@@ -609,23 +690,22 @@ async function ensureBucketName(ctx, clients, id, good) {
609
690
  }, async () => {});
610
691
  return "created";
611
692
  };
612
- const adopt = async (name) => {
613
- const owner = (await readOr(() => s3.send(new S3.GetBucketTaggingCommand({ Bucket: name })), ["NoSuchTagSet", "NoSuchTagSetError"]))?.TagSet?.find((t) => t.Key === TAG_PROJECT)?.Value;
693
+ const adopt = async (name, where) => {
694
+ const there = where === region ? clients : await makeClients(ctx.credential, where);
695
+ const owner = (await readOr(() => there.s3.send(new there.S3.GetBucketTaggingCommand({ Bucket: name })), ["NoSuchTagSet", "NoSuchTagSetError"]))?.TagSet?.find((t) => t.Key === TAG_PROJECT)?.Value;
614
696
  if (owner && owner !== ctx.identity.key) throw new BucketOwnedByAnotherProject(name, owner, ctx.identity.key);
615
697
  };
616
698
  if (record?.status === "ready" && record.id) {
617
699
  const found = await probe(record.id);
618
- if (found === "ours") return {
619
- bucket: record.id,
620
- exists: true
621
- };
622
700
  if (found === "taken") throw new RecordedBucketUnreachable(id, record.id);
623
- const made = await create(record.id);
701
+ const made = found === "ours" ? "ours" : await create(record.id);
624
702
  if (made === "taken") throw new RecordedBucketUnreachable(id, record.id);
625
- if (!ctx.dryRun) await ctx.state.ready(entry(record.id), record.id);
703
+ const where = await located(record.id, made);
704
+ if (!ctx.dryRun) await ctx.state.ready(entry(record.id, where), record.id);
626
705
  return {
627
706
  bucket: record.id,
628
- exists: made === "ours"
707
+ exists: made === "ours",
708
+ region: where
629
709
  };
630
710
  }
631
711
  const pending = record?.data?.name;
@@ -639,12 +719,14 @@ async function ensureBucketName(ctx, clients, id, good) {
639
719
  tried.push(name);
640
720
  const made = await create(name);
641
721
  if (made === "taken") continue;
642
- if (made === "ours") await adopt(name);
643
- if (!ctx.dryRun) await ctx.state.ready(entry(name), name);
722
+ const where = await located(name, made);
723
+ if (made === "ours") await adopt(name, where);
724
+ if (!ctx.dryRun) await ctx.state.ready(entry(name, where), name);
644
725
  if (name !== good) ctx.log(` '${good}' is taken by another account; '${id}' is '${name}', recorded in the stage's state`);
645
726
  return {
646
727
  bucket: name,
647
- exists: made === "ours"
728
+ exists: made === "ours",
729
+ region: where
648
730
  };
649
731
  }
650
732
  throw new BucketNameUnavailable(id, tried.filter((n) => n !== pending));
@@ -808,8 +890,8 @@ async function ensureAccessKey(ctx, clients, input) {
808
890
  const record = ctx.state.record(entryKey);
809
891
  const given = ctx.secrets[urlKey];
810
892
  let inUrl;
893
+ let parsed;
811
894
  if (given !== void 0) {
812
- let parsed;
813
895
  try {
814
896
  parsed = s3Url.parse(given);
815
897
  } catch {
@@ -853,7 +935,20 @@ async function ensureAccessKey(ctx, clients, input) {
853
935
  await ctx.state.ready(entry(), inUrl);
854
936
  current = inUrl;
855
937
  }
856
- if (current !== void 0 && listed.includes(current) && inUrl === current && !ctx.rotateKeys) return;
938
+ if (current !== void 0 && listed.includes(current) && inUrl === current && !ctx.rotateKeys) {
939
+ if (parsed && parsed.region !== clients.region) {
940
+ const address = parsed;
941
+ await ctx.change({
942
+ construct: id,
943
+ resource: urlKey,
944
+ change: `name ${clients.region}, the bucket's region (it said ${address.region ?? "none"})`
945
+ }, () => ctx.writeSecrets({ [urlKey]: s3Url.build({
946
+ ...address,
947
+ region: clients.region
948
+ }) }));
949
+ }
950
+ return;
951
+ }
857
952
  if (ctx.rotateKeys && previous) throw new RotationInProgress(id, previous, ctx.stage);
858
953
  const outgoing = current && listed.includes(current) ? current : void 0;
859
954
  if (listed.length >= 2) throw new AccessKeyLimit(user, listed);
@@ -875,28 +970,58 @@ async function ensureAccessKey(ctx, clients, input) {
875
970
  accessKeyId: key.AccessKeyId,
876
971
  secretAccessKey: key.SecretAccessKey
877
972
  }) });
973
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
878
974
  await ctx.state.ready(entry(outgoing ? {
879
975
  previous: outgoing,
880
- rotatedAt: (/* @__PURE__ */ new Date()).toISOString()
881
- } : {}), key.AccessKeyId);
976
+ rotatedAt: issuedAt,
977
+ issuedAt
978
+ } : { issuedAt }), key.AccessKeyId);
882
979
  });
883
980
  if (outgoing) ctx.log(` ${urlKey} holds the new key. This deploy moves '${ctx.stage}' onto it; the next deploy after it deletes the old key ${outgoing}`);
884
981
  }
982
+ /** How long a key issued this recently counts as new to IAM. */
983
+ const NEW_KEY_WINDOW_MS = 6e5;
984
+ /** The waits between asks while a new key becomes usable: about a minute. */
985
+ let newKeyWaits = [
986
+ 2e3,
987
+ 4e3,
988
+ 8e3,
989
+ 15e3,
990
+ 15e3,
991
+ 15e3
992
+ ];
993
+ /** When the key in the URL was issued, if the stage's state says it was just now. */
994
+ function issuedJustNow(ctx, id, accessKeyId) {
995
+ const record = ctx.resources?.[`iam-access-key:${id}`];
996
+ const issuedAt = record?.data?.issuedAt;
997
+ if (record?.id !== accessKeyId || typeof issuedAt !== "string") return;
998
+ const age = Date.now() - Date.parse(issuedAt);
999
+ return age >= 0 && age < NEW_KEY_WINDOW_MS ? issuedAt : void 0;
1000
+ }
885
1001
  /**
886
1002
  * Each provisioned bucket answers `HeadBucket` to the key in the stage's
887
1003
  * secrets — the one the apps will use. A bucket whose URL carries no key is
888
1004
  * not one this provider wrote, and the deploy's own check covers a URL that
889
1005
  * is missing.
1006
+ *
1007
+ * A failure is read by its status, never its name — a `HEAD` has no body,
1008
+ * so SDK v3 calls a 301, 400 or 403 `Unknown` — and the message carries
1009
+ * everything S3 said: status, error, region, request ids.
1010
+ *
1011
+ * @throws {BucketRegionMismatch} the bucket is in another region than its URL says
1012
+ * @throws {ProvisionedBucketUnreachable}
890
1013
  */
891
1014
  async function verifyS3(ctx) {
892
1015
  const S3 = await import("@aws-sdk/client-s3");
893
1016
  for (const { id } of bucketConstructs(ctx.manifest)) {
894
- const url = ctx.secrets[provideKey(id, "url")];
1017
+ const urlKey = provideKey(id, "url");
1018
+ const url = ctx.secrets[urlKey];
895
1019
  if (url === void 0) continue;
896
1020
  const address = s3Url.parse(url);
897
1021
  if (!address.accessKeyId || !address.secretAccessKey) continue;
1022
+ const asked = address.region ?? ctx.config.region ?? "us-east-1";
898
1023
  const client = new S3.S3Client({
899
- region: address.region ?? ctx.config.region ?? "us-east-1",
1024
+ region: asked,
900
1025
  credentials: {
901
1026
  accessKeyId: address.accessKeyId,
902
1027
  secretAccessKey: address.secretAccessKey
@@ -905,10 +1030,35 @@ async function verifyS3(ctx) {
905
1030
  ...address.forcePathStyle ? { forcePathStyle: true } : {}
906
1031
  });
907
1032
  try {
908
- await client.send(new S3.HeadBucketCommand({ Bucket: address.bucket }));
909
- } catch (error) {
910
- const name = errorName(error);
911
- throw new ProvisionedBucketUnreachable(id, address.bucket, ctx.stage, name === "NotFound" ? "it does not exist" : name === "Forbidden" ? "the key is refused" : name || String(error));
1033
+ let head = await headBucket(S3, client, address.bucket);
1034
+ const issuedAt = issuedJustNow(ctx, id, address.accessKeyId);
1035
+ let waited = 0;
1036
+ if (!head.ok && head.status === 403 && issuedAt) {
1037
+ ctx.log?.(` ${id}: waiting for the new key ${address.accessKeyId} to become active…`);
1038
+ for (const wait of newKeyWaits) {
1039
+ await new Promise((resolve) => setTimeout(resolve, wait));
1040
+ waited += wait;
1041
+ head = await headBucket(S3, client, address.bucket);
1042
+ if (head.ok || head.status !== 403) break;
1043
+ }
1044
+ }
1045
+ if (head.ok) continue;
1046
+ const fail = (reason) => new ProvisionedBucketUnreachable(id, address.bucket, ctx.stage, reason);
1047
+ const said = describeAnswer(head);
1048
+ if (head.status === 301 || head.status === 400 && head.region && head.region !== asked) {
1049
+ let where = head.region;
1050
+ let located = "";
1051
+ if (!where) {
1052
+ const location = await bucketLocation(S3, client, address.bucket);
1053
+ if ("region" in location) where = location.region;
1054
+ else located = `; GetBucketLocation answered ${describeAnswer(location.failed)}`;
1055
+ }
1056
+ if (where && where !== asked) throw new BucketRegionMismatch(id, address.bucket, ctx.stage, urlKey, where, address.region, asked, said);
1057
+ throw fail(`S3 redirected the request (${said}${located})`);
1058
+ }
1059
+ if (head.status === 404) throw fail(`it does not exist (${said})`);
1060
+ if (head.status === 403) throw fail(`the key is refused — AccessDenied, or an invalid or not-yet-active key (${said})` + (issuedAt ? `. The key was issued at ${issuedAt} by this deploy, and IAM keys take seconds to become usable: it was still refused after waiting ${Math.round(waited / 1e3)}s` : ""));
1061
+ throw fail(said);
912
1062
  } finally {
913
1063
  client.destroy();
914
1064
  }
@@ -961,7 +1111,9 @@ async function verifyStageProviders(input) {
961
1111
  manifest: input.manifest,
962
1112
  stage: input.stage,
963
1113
  config: choice.config,
964
- secrets: input.secrets
1114
+ secrets: input.secrets,
1115
+ ...input.resources ? { resources: input.resources } : {},
1116
+ ...input.log ? { log: input.log } : {}
965
1117
  });
966
1118
  verified.push(`${kind}: ${provider.name}`);
967
1119
  }
@@ -1255,7 +1407,9 @@ async function assertStageReady(ctx) {
1255
1407
  workspace,
1256
1408
  manifest,
1257
1409
  stage,
1258
- secrets: supplied
1410
+ secrets: supplied,
1411
+ resources: (await ctx.state.read(stage))?.resources ?? {},
1412
+ log: (line) => ctx.logger.info(line)
1259
1413
  });
1260
1414
  for (const line of verified) ctx.logger.info(`✓ ${line} verified`);
1261
1415
  }
@@ -1851,4 +2005,4 @@ async function load(input, sandbox) {
1851
2005
  //#endregion
1852
2006
  export { UnknownDeployApps as a, iamUserName as c, TargetBuildsNothing as i, NoDeployableApps as n, IAM_USER_PATH as o, RollbackFailed as r, bucketName as s, deploy as t };
1853
2007
 
1854
- //# sourceMappingURL=deploy-B0drCD6s.mjs.map
2008
+ //# sourceMappingURL=deploy-DktQl7ti.mjs.map