@hot-updater/aws 1.2.0-rc.1 → 1.2.0-rc.10

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/index.mjs CHANGED
@@ -1,11 +1,10 @@
1
1
  import { CreateTableCommand, DescribeTableCommand, DescribeTimeToLiveCommand, DynamoDBClient, UpdateTimeToLiveCommand, waitUntilTableExists } from "@aws-sdk/client-dynamodb";
2
- import { BUNDLE_STORAGE_PREFIX, CONTENT_ADDRESSED_ASSET_PREFIX, createEngineDatabase, createKvAdapter, createStorageAdapter, createStorageKeyBuilder, createStorageUri, migrateCoreSchema, parseStorageUri } from "@hot-updater/plugin-core";
2
+ import { BUNDLE_STORAGE_PREFIX, CONTENT_ADDRESSED_ASSET_PREFIX, createEngineDatabase, createKvAdapter, migrateCoreSchema, resolveLazy } from "@hot-updater/plugin-core";
3
3
  import { CloudFrontClient, CreateInvalidationCommand, GetInvalidationCommand } from "@aws-sdk/client-cloudfront";
4
4
  import { BatchGetCommand, BatchWriteCommand, DynamoDBDocumentClient, QueryCommand, TransactWriteCommand } from "@aws-sdk/lib-dynamodb";
5
5
  import { SSM } from "@aws-sdk/client-ssm";
6
6
  import { getSignedUrl } from "@aws-sdk/cloudfront-signer";
7
- import { DeleteObjectCommand, DeleteObjectsCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, S3Client } from "@aws-sdk/client-s3";
8
- import { Upload } from "@aws-sdk/lib-storage";
7
+ import { createS3Storage } from "@hot-updater/storage-s3";
9
8
  import { randomUUID } from "node:crypto";
10
9
  //#region src/cloudFrontInvalidation.ts
11
10
  const DEFAULT_POLL_INTERVAL_MS = 2e3;
@@ -314,10 +313,30 @@ async apply() {
314
313
  };
315
314
  };
316
315
  //#endregion
316
+ //#region src/lazyCredentials.ts
317
+ /**
318
+ * `credentials` as the AWS SDK takes them: access keys with a lazy field
319
+ * become a provider, which the SDK calls once, on its first request.
320
+ */
321
+ const toSdkCredentials = (credentials) => {
322
+ if (credentials === void 0 || typeof credentials === "function") return credentials;
323
+ const { accessKeyId, secretAccessKey, sessionToken, ...rest } = credentials;
324
+ if (typeof accessKeyId !== "function" && typeof secretAccessKey !== "function" && typeof sessionToken !== "function") return credentials;
325
+ return async () => ({
326
+ ...rest,
327
+ accessKeyId: await resolveLazy(accessKeyId),
328
+ secretAccessKey: await resolveLazy(secretAccessKey),
329
+ ...sessionToken === void 0 ? {} : { sessionToken: await resolveLazy(sessionToken) }
330
+ });
331
+ };
332
+ //#endregion
317
333
  //#region src/dynamoDB.ts
318
334
  /** The DynamoDB client ignores the CloudFront and batching settings. */
319
- const adapterOf = ({ tableName, ...clientConfig }) => {
320
- const client = new DynamoDBClient(clientConfig);
335
+ const adapterOf = ({ tableName, credentials, ...clientConfig }) => {
336
+ const client = new DynamoDBClient({
337
+ ...clientConfig,
338
+ credentials: toSdkCredentials(credentials)
339
+ });
321
340
  const store = createDynamoDBStore({
322
341
  client,
323
342
  tableName
@@ -352,7 +371,10 @@ const marked = (target) => ({ [DATABASE_TARGET]: target });
352
371
  */
353
372
  const dynamoDB = (config) => {
354
373
  const { client, adapter } = adapterOf(config);
355
- const cloudFront = createUpdateRouteInvalidation(config);
374
+ const cloudFront = createUpdateRouteInvalidation({
375
+ ...config,
376
+ credentials: toSdkCredentials(config.credentials)
377
+ });
356
378
  return {
357
379
  ...createEngineDatabase({
358
380
  name: "dynamoDB",
@@ -495,153 +517,17 @@ const cloudFrontDownloadUrl = (config) => {
495
517
  };
496
518
  //#endregion
497
519
  //#region src/s3Storage.ts
498
- /** Devices download right after the update check that returns the URL. */
499
- const PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
500
- const isObjectNotFoundError = (error) => error instanceof Error && (error.name === "NotFound" || error.name === "NoSuchKey");
501
520
  function s3Storage(config) {
502
- const { bucketName, basePath, getDownloadUrl, ...s3Config } = config;
503
- const client = new S3Client(applyS3RuntimeAwsConfig(s3Config));
504
- const normalizedBasePath = basePath?.replace(/^\/+|\/+$/g, "") ?? "";
505
- const getStorageKey = createStorageKeyBuilder(normalizedBasePath);
506
- const getListPrefix = (prefix = "") => {
507
- const normalizedPrefix = prefix.replace(/^\/+|\/+$/g, "");
508
- const value = [normalizedBasePath, normalizedPrefix].filter(Boolean).join("/");
509
- return value ? `${value}/` : "";
510
- };
511
- /**
512
- * The URI `put` returns for the key, or null for a key no `put` writes,
513
- * such as a folder marker that ends in "/".
514
- */
515
- const getListedStorageUri = (key) => {
516
- try {
517
- return createStorageUri({
518
- bucket: bucketName,
519
- key,
520
- protocol: "s3"
521
- });
522
- } catch {
523
- return null;
524
- }
525
- };
526
- const getRelativeKey = (key) => {
527
- if (!normalizedBasePath) return key;
528
- const basePrefix = `${normalizedBasePath}/`;
529
- return key.startsWith(basePrefix) ? key.slice(basePrefix.length) : key;
530
- };
531
- const parseAndValidate = (storageUri) => {
532
- const parsed = parseStorageUri(storageUri, "s3");
533
- if (parsed.bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${parsed.bucket}".`);
534
- return parsed;
535
- };
536
- return createStorageAdapter({
521
+ const { bucketName, basePath, getDownloadUrl, credentials, ...s3Config } = config;
522
+ return createS3Storage({
537
523
  name: "s3Storage",
538
- protocol: "s3",
539
- async listObjects(prefix) {
540
- const objects = [];
541
- let continuationToken;
542
- do {
543
- const response = await client.send(new ListObjectsV2Command({
544
- Bucket: bucketName,
545
- ContinuationToken: continuationToken,
546
- Prefix: getListPrefix(prefix)
547
- }));
548
- for (const object of response.Contents ?? []) {
549
- if (!object.Key) continue;
550
- const storageUri = getListedStorageUri(object.Key);
551
- if (storageUri === null) continue;
552
- objects.push({
553
- key: getRelativeKey(object.Key),
554
- lastModifiedAt: object.LastModified,
555
- size: object.Size ?? 0,
556
- storageUri
557
- });
558
- }
559
- continuationToken = response.NextContinuationToken;
560
- } while (continuationToken);
561
- return objects;
562
- },
563
- async deleteObjects(relativeKeys) {
564
- const keys = relativeKeys.map((key) => getStorageKey(key));
565
- for (let offset = 0; offset < keys.length; offset += 1e3) {
566
- const batch = keys.slice(offset, offset + 1e3);
567
- if (batch.length === 0) continue;
568
- const response = await client.send(new DeleteObjectsCommand({
569
- Bucket: bucketName,
570
- Delete: {
571
- Objects: batch.map((Key) => ({ Key })),
572
- Quiet: true
573
- }
574
- }));
575
- if (response.Errors && response.Errors.length > 0) throw new Error(`Failed to delete ${response.Errors.length} S3 object(s).`);
576
- }
577
- },
578
- async put({ key, body, contentLength, contentType }) {
579
- const storageKey = getStorageKey(key);
580
- const response = await new Upload({
581
- client,
582
- params: {
583
- Body: body,
584
- Bucket: bucketName,
585
- CacheControl: "max-age=31536000",
586
- ...contentLength === void 0 ? {} : { ContentLength: contentLength },
587
- ContentType: contentType,
588
- Key: storageKey
589
- }
590
- }).done();
591
- if (!response.Bucket || !response.Key) throw new Error("Upload failed");
592
- return { storageUri: createStorageUri({
593
- bucket: bucketName,
594
- key: storageKey,
595
- protocol: "s3"
596
- }) };
597
- },
598
- async get({ storageUri }) {
599
- const { key } = parseAndValidate(storageUri);
600
- try {
601
- const response = await client.send(new GetObjectCommand({
602
- Bucket: bucketName,
603
- Key: key
604
- }));
605
- if (!response.Body) return { response: null };
606
- const headers = new Headers();
607
- if (response.ContentType) headers.set("content-type", response.ContentType);
608
- if (response.ContentLength !== void 0) headers.set("content-length", String(response.ContentLength));
609
- return { response: new Response(response.Body.transformToWebStream(), { headers }) };
610
- } catch (error) {
611
- if (isObjectNotFoundError(error)) return { response: null };
612
- throw error;
613
- }
614
- },
615
- async getDownloadUrl({ storageUri }) {
616
- const { key } = parseAndValidate(storageUri);
617
- if (getDownloadUrl) return getDownloadUrl({ storageUri });
618
- const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
619
- return { url: await getSignedUrl(client, new GetObjectCommand({
620
- Bucket: bucketName,
621
- Key: key
622
- }), { expiresIn: PRESIGNED_URL_EXPIRES_IN_SECONDS }) };
623
- },
624
- async exists({ storageUri }) {
625
- const { key } = parseAndValidate(storageUri);
626
- try {
627
- await client.send(new HeadObjectCommand({
628
- Bucket: bucketName,
629
- Key: key
630
- }));
631
- return { exists: true };
632
- } catch (error) {
633
- if (isObjectNotFoundError(error)) return { exists: false };
634
- throw error;
635
- }
636
- },
637
- async delete({ storageUri }) {
638
- const { key } = parseAndValidate(storageUri);
639
- await client.send(new DeleteObjectCommand({
640
- Bucket: bucketName,
641
- Key: key
642
- }));
643
- return { deleted: true };
644
- }
524
+ protocol: "s3"
525
+ }, {
526
+ ...applyS3RuntimeAwsConfig(s3Config),
527
+ credentials,
528
+ bucketName,
529
+ basePath,
530
+ getDownloadUrl
645
531
  });
646
532
  }
647
533
  //#endregion
@@ -666,7 +552,22 @@ const EXPIRED_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set([
666
552
  ]);
667
553
  /** Errors of the SDK finding no credentials, or an expired SSO session. */
668
554
  const NO_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set(["CredentialsProviderError", "TokenProviderError"]);
669
- const SIGN_IN_HINT = "In CI, set them with aws-actions/configure-aws-credentials; on your machine, run aws sso login --profile <profile> and set AWS_PROFILE=<profile>";
555
+ const SIGN_IN_HINT = ["In CI, set them with aws-actions/configure-aws-credentials.", "On your machine: aws sso login --profile <profile>, then set AWS_PROFILE."];
556
+ /** The widest line a message has: the CLI prints no wider. */
557
+ const LINE_WIDTH = 76;
558
+ /** `text`, such as AWS's own message, in lines that fit, broken at spaces. */
559
+ const wrap = (text) => {
560
+ const lines = [];
561
+ let line = "";
562
+ for (const word of text.split(/\s+/u).filter(Boolean)) if (line && line.length + 1 + word.length > LINE_WIDTH) {
563
+ lines.push(line);
564
+ line = word;
565
+ } else line = line ? `${line} ${word}` : word;
566
+ if (line) lines.push(line);
567
+ return lines;
568
+ };
569
+ /** An error of one line each, for a terminal. */
570
+ const failure = (lines, cause) => new Error(lines.join("\n"), cause === void 0 ? void 0 : { cause });
670
571
  const errorName = (error) => error instanceof Error ? error.name : "";
671
572
  const errorMessage = (error) => (error instanceof Error ? error.message : String(error)).replace(/\.+$/u, "");
672
573
  const resolveEndpoint = (endpoint) => {
@@ -681,7 +582,11 @@ const resolveEndpoint = (endpoint) => {
681
582
  "127.0.0.1",
682
583
  "[::1]"
683
584
  ])).has(url.hostname);
684
- if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback) || url.username || url.password || url.search || url.hash) throw new Error(`${NAME} endpoint must use HTTPS, or HTTP on a loopback address, without credentials, a query, or a fragment.`);
585
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback) || url.username || url.password || url.search || url.hash) throw failure([
586
+ `${NAME} can't use its endpoint.`,
587
+ "Use HTTPS, or HTTP on a loopback address.",
588
+ "Leave out credentials, a query, and a fragment."
589
+ ]);
685
590
  return url.toString();
686
591
  };
687
592
  /** Masks a value in the CLI's output, which loads `@hot-updater/cli-tools`. */
@@ -708,20 +613,20 @@ const checkCredentials = async (credentials) => {
708
613
  */
709
614
  const secretsManager = ({ credentials, endpoint, prefix = "hot-updater/", region }) => {
710
615
  const checkSettings = () => {
711
- if (typeof region !== "string" || !REGION_PATTERN.test(region)) throw new Error(`${NAME} needs an AWS region, such as "us-east-1", and ${JSON.stringify(region)} is not one.`);
616
+ if (typeof region !== "string" || !REGION_PATTERN.test(region)) throw failure([`${NAME} can't use the region ${JSON.stringify(region)}.`, "Set an AWS region, such as \"us-east-1\"."]);
712
617
  if (prefix !== "" && !SECRET_ID_PATTERN.test(prefix)) throw new Error(`${NAME}'s prefix takes letters, digits, and /_+=.@- only.`);
713
618
  };
714
619
  const secretId = (name) => {
715
620
  checkSettings();
716
621
  const id = `${prefix}${name}`;
717
- if (!SECRET_ID_PATTERN.test(name) || id.length > MAX_SECRET_ID_LENGTH) throw new Error(`${NAME} cannot name a secret ${JSON.stringify(id)}: AWS takes up to ${MAX_SECRET_ID_LENGTH} letters, digits, and /_+=.@- characters.`);
622
+ if (!SECRET_ID_PATTERN.test(name) || id.length > MAX_SECRET_ID_LENGTH) throw failure([`${NAME} can't name a secret ${JSON.stringify(id)}.`, `AWS takes up to ${MAX_SECRET_ID_LENGTH} letters, digits, and /_+=.@- characters.`]);
718
623
  return id;
719
624
  };
720
625
  const resolveCredentials = () => {
721
626
  if (credentials !== void 0) return async () => checkCredentials(typeof credentials === "function" ? await credentials() : credentials);
722
627
  const accessKeyId = process.env["AWS_ACCESS_KEY_ID"];
723
628
  const secretAccessKey = process.env["AWS_SECRET_ACCESS_KEY"];
724
- if (Boolean(accessKeyId) !== Boolean(secretAccessKey)) throw new Error(`${NAME} found ${accessKeyId ? "AWS_ACCESS_KEY_ID" : "AWS_SECRET_ACCESS_KEY"} without ${accessKeyId ? "AWS_SECRET_ACCESS_KEY" : "AWS_ACCESS_KEY_ID"} in the environment: set both, or neither to use a profile.`);
629
+ if (Boolean(accessKeyId) !== Boolean(secretAccessKey)) throw failure([`${NAME} found ${accessKeyId ? "AWS_ACCESS_KEY_ID" : "AWS_SECRET_ACCESS_KEY"} without ${accessKeyId ? "AWS_SECRET_ACCESS_KEY" : "AWS_ACCESS_KEY_ID"}.`, "Set both, or neither to use a profile."]);
725
630
  };
726
631
  let opened;
727
632
  const open = () => {
@@ -744,12 +649,40 @@ const secretsManager = ({ credentials, endpoint, prefix = "hot-updater/", region
744
649
  const describeFailure = (verb, id, error, permissions) => {
745
650
  const name = errorName(error);
746
651
  const status = error?.$metadata?.httpStatusCode;
747
- const detail = `${name || "Error"}${status ? ` (${status})` : ""}: ${errorMessage(error)}`;
748
- if (NO_CREDENTIAL_ERRORS.has(name)) return new Error(`${NAME} found no usable AWS credentials to ${verb} ${JSON.stringify(id)} (${errorMessage(error)}). ${SIGN_IN_HINT}.`, { cause: error });
749
- if (EXPIRED_CREDENTIAL_ERRORS.has(name)) return new Error(`AWS Secrets Manager refused the credentials to ${verb} ${JSON.stringify(id)}, ${detail}. They are expired or invalid: sign in again. ${SIGN_IN_HINT}.`, { cause: error });
750
- if (name === "AccessDeniedException") return new Error(`AWS Secrets Manager refused to ${verb} ${JSON.stringify(id)}, ${detail}. The AWS identity needs ${permissions} on it, and on a secret encrypted with a customer managed KMS key, kms:Decrypt to read and kms:GenerateDataKey to store.`, { cause: error });
751
- if (/marked for deletion|scheduled for deletion/iu.test(errorMessage(error))) return new Error(`AWS Secrets Manager cannot ${verb} ${JSON.stringify(id)}: it is scheduled for deletion. Restore it with aws secretsmanager restore-secret --secret-id ${id}, or wait until it is deleted.`, { cause: error });
752
- return new Error(`AWS Secrets Manager failed to ${verb} ${JSON.stringify(id)}, ${detail}.`, { cause: error });
652
+ const detail = wrap(`${name || "Error"}${status ? ` (${status})` : ""}: ${errorMessage(error)}.`);
653
+ const secret = `Secret: ${id}`;
654
+ if (NO_CREDENTIAL_ERRORS.has(name)) return failure([
655
+ `${NAME} found no AWS credentials to ${verb} a secret.`,
656
+ secret,
657
+ ...wrap(`${errorMessage(error)}.`),
658
+ ...SIGN_IN_HINT
659
+ ], error);
660
+ if (EXPIRED_CREDENTIAL_ERRORS.has(name)) return failure([
661
+ `AWS Secrets Manager refused the sign-in to ${verb} a secret.`,
662
+ secret,
663
+ ...detail,
664
+ "They are expired or invalid: sign in again.",
665
+ ...SIGN_IN_HINT
666
+ ], error);
667
+ if (name === "AccessDeniedException") return failure([
668
+ `AWS Secrets Manager refused to ${verb} a secret.`,
669
+ secret,
670
+ ...detail,
671
+ `The AWS identity needs ${permissions} on it.`,
672
+ `With a customer managed KMS key, it needs ${verb === "read" ? "kms:Decrypt" : "kms:GenerateDataKey"} too.`
673
+ ], error);
674
+ if (/marked for deletion|scheduled for deletion/iu.test(errorMessage(error))) return failure([
675
+ `AWS Secrets Manager can't ${verb} a secret.`,
676
+ secret,
677
+ "It is scheduled for deletion. Restore it, or wait until it is gone:",
678
+ "aws secretsmanager restore-secret \\",
679
+ ` --secret-id ${id}`
680
+ ], error);
681
+ return failure([
682
+ `AWS Secrets Manager failed to ${verb} a secret.`,
683
+ secret,
684
+ ...detail
685
+ ], error);
753
686
  };
754
687
  const read = async ({ sdk, client }, id) => {
755
688
  let output;
@@ -779,8 +712,8 @@ const secretsManager = ({ credentials, endpoint, prefix = "hot-updater/", region
779
712
  },
780
713
  set: async (name, value) => {
781
714
  const id = secretId(name);
782
- if (typeof value !== "string" || value === "") throw new Error(`${NAME} cannot store an empty ${id}.`);
783
- if (Buffer.byteLength(value, "utf8") > MAX_SECRET_BYTES) throw new Error(`${NAME} cannot store ${id}: AWS takes up to ${MAX_SECRET_BYTES} bytes per secret.`);
715
+ if (typeof value !== "string" || value === "") throw new Error(`${NAME} can't store an empty ${id}.`);
716
+ if (Buffer.byteLength(value, "utf8") > MAX_SECRET_BYTES) throw failure([`${NAME} can't store ${id}.`, `AWS takes up to ${MAX_SECRET_BYTES} bytes per secret.`]);
784
717
  await mask([value]);
785
718
  const { sdk, client } = await open();
786
719
  const token = randomUUID();
@@ -6579,10 +6579,30 @@ async apply() {
6579
6579
  };
6580
6580
  };
6581
6581
  //#endregion
6582
+ //#region src/lazyCredentials.ts
6583
+ /**
6584
+ * `credentials` as the AWS SDK takes them: access keys with a lazy field
6585
+ * become a provider, which the SDK calls once, on its first request.
6586
+ */
6587
+ const toSdkCredentials = (credentials) => {
6588
+ if (credentials === void 0 || typeof credentials === "function") return credentials;
6589
+ const { accessKeyId, secretAccessKey, sessionToken, ...rest } = credentials;
6590
+ if (typeof accessKeyId !== "function" && typeof secretAccessKey !== "function" && typeof sessionToken !== "function") return credentials;
6591
+ return async () => ({
6592
+ ...rest,
6593
+ accessKeyId: await (0, _hot_updater_plugin_core.resolveLazy)(accessKeyId),
6594
+ secretAccessKey: await (0, _hot_updater_plugin_core.resolveLazy)(secretAccessKey),
6595
+ ...sessionToken === void 0 ? {} : { sessionToken: await (0, _hot_updater_plugin_core.resolveLazy)(sessionToken) }
6596
+ });
6597
+ };
6598
+ //#endregion
6582
6599
  //#region src/dynamoDB.ts
6583
6600
  /** The DynamoDB client ignores the CloudFront and batching settings. */
6584
- const adapterOf = ({ tableName, ...clientConfig }) => {
6585
- const client = new _aws_sdk_client_dynamodb.DynamoDBClient(clientConfig);
6601
+ const adapterOf = ({ tableName, credentials, ...clientConfig }) => {
6602
+ const client = new _aws_sdk_client_dynamodb.DynamoDBClient({
6603
+ ...clientConfig,
6604
+ credentials: toSdkCredentials(credentials)
6605
+ });
6586
6606
  const store = createDynamoDBStore({
6587
6607
  client,
6588
6608
  tableName
@@ -6617,7 +6637,10 @@ const marked = (target) => ({ [DATABASE_TARGET]: target });
6617
6637
  */
6618
6638
  const dynamoDB = (config) => {
6619
6639
  const { client, adapter } = adapterOf(config);
6620
- const cloudFront = createUpdateRouteInvalidation(config);
6640
+ const cloudFront = createUpdateRouteInvalidation({
6641
+ ...config,
6642
+ credentials: toSdkCredentials(config.credentials)
6643
+ });
6621
6644
  return {
6622
6645
  ...(0, _hot_updater_plugin_core.createEngineDatabase)({
6623
6646
  name: "dynamoDB",
@@ -6774,7 +6797,8 @@ const initProvider = {
6774
6797
  help: "Existing CloudFront distribution ID",
6775
6798
  optional: true
6776
6799
  }
6777
- }
6800
+ },
6801
+ secrets: ["HOT_UPDATER_S3_ACCESS_KEY_ID", "HOT_UPDATER_S3_SECRET_ACCESS_KEY"]
6778
6802
  };
6779
6803
  //#endregion
6780
6804
  //#region iac/regionLocationMap.ts
@@ -8416,7 +8440,17 @@ var SSMKeyPairManager = class {
8416
8440
  };
8417
8441
  //#endregion
8418
8442
  //#region iac/templates.ts
8419
- const getConfigScaffold = (build, authMode) => {
8443
+ /**
8444
+ * The config init writes. With the `resources` init set up, it names them,
8445
+ * and an account's access keys are read with `secret()`, from the
8446
+ * environment and `.env.hotupdater`, then the config's `secrets` adapter.
8447
+ * Without them, as the agent scaffold writes it before the agent creates the
8448
+ * resources, it reads their names from the environment, where the agent
8449
+ * records them. A profile or SSO sign-in is each person's own.
8450
+ */
8451
+ const getConfigScaffold = (build, authMode, resources) => {
8452
+ const value = (envKey, resolved) => resolved === void 0 ? `process.env.${envKey}!` : JSON.stringify(resolved);
8453
+ const region = value("HOT_UPDATER_S3_REGION", resources?.region);
8420
8454
  const storageConfig = {
8421
8455
  imports: [{
8422
8456
  pkg: "@hot-updater/aws",
@@ -8424,7 +8458,7 @@ const getConfigScaffold = (build, authMode) => {
8424
8458
  }],
8425
8459
  configString: `s3Storage({
8426
8460
  ...awsOptions,
8427
- bucketName: process.env.HOT_UPDATER_S3_BUCKET_NAME!,
8461
+ bucketName: ${value("HOT_UPDATER_S3_BUCKET_NAME", resources?.bucketName)},
8428
8462
  })`
8429
8463
  };
8430
8464
  const databaseConfig = {
@@ -8434,8 +8468,8 @@ const getConfigScaffold = (build, authMode) => {
8434
8468
  }],
8435
8469
  configString: `dynamoDB({
8436
8470
  ...awsOptions,
8437
- tableName: process.env.HOT_UPDATER_DYNAMODB_TABLE_NAME!,
8438
- cloudfrontDistributionId: process.env.HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID!,
8471
+ tableName: ${value("HOT_UPDATER_DYNAMODB_TABLE_NAME", resources?.tableName)},
8472
+ cloudfrontDistributionId: ${value("HOT_UPDATER_CLOUDFRONT_DISTRIBUTION_ID", resources?.cloudfrontDistributionId)},
8439
8473
  })`
8440
8474
  };
8441
8475
  let helperStatements;
@@ -8447,7 +8481,7 @@ const getConfigScaffold = (build, authMode) => {
8447
8481
  replaceIncompatibleProperties: ["credentials"],
8448
8482
  code: `
8449
8483
  const awsOptions = {
8450
- region: process.env.HOT_UPDATER_S3_REGION!,
8484
+ region: ${region},
8451
8485
  credentials: fromSSO({ profile: process.env.HOT_UPDATER_AWS_PROFILE! }),
8452
8486
  };`.trim()
8453
8487
  }];
@@ -8459,11 +8493,11 @@ const awsOptions = {
8459
8493
  replaceIncompatibleProperties: ["credentials"],
8460
8494
  code: authMode.profile ? `
8461
8495
  const awsOptions = {
8462
- region: process.env.HOT_UPDATER_S3_REGION!,
8496
+ region: ${region},
8463
8497
  credentials: fromIni({ profile: process.env.HOT_UPDATER_AWS_PROFILE! }),
8464
8498
  };`.trim() : `
8465
8499
  const awsOptions = {
8466
- region: process.env.HOT_UPDATER_S3_REGION!,
8500
+ region: ${region},
8467
8501
  credentials: fromNodeProviderChain(),
8468
8502
  };`.trim()
8469
8503
  }];
@@ -8474,10 +8508,10 @@ const awsOptions = {
8474
8508
  replaceIncompatibleProperties: ["credentials"],
8475
8509
  code: `
8476
8510
  const awsOptions = {
8477
- region: process.env.HOT_UPDATER_S3_REGION!,
8511
+ region: ${region},
8478
8512
  credentials: {
8479
- accessKeyId: process.env.HOT_UPDATER_S3_ACCESS_KEY_ID!,
8480
- secretAccessKey: process.env.HOT_UPDATER_S3_SECRET_ACCESS_KEY!,
8513
+ accessKeyId: secret("HOT_UPDATER_S3_ACCESS_KEY_ID"),
8514
+ secretAccessKey: secret("HOT_UPDATER_S3_SECRET_ACCESS_KEY"),
8481
8515
  },
8482
8516
  };`.trim()
8483
8517
  }];
@@ -8500,9 +8534,15 @@ const awsOptions = {
8500
8534
  named: ["fromSSO"]
8501
8535
  });
8502
8536
  break;
8503
- case "local": builder.addImport({
8504
- pkg: "@aws-sdk/credential-providers",
8505
- named: [authMode.profile ? "fromIni" : "fromNodeProviderChain"]
8537
+ case "local":
8538
+ builder.addImport({
8539
+ pkg: "@aws-sdk/credential-providers",
8540
+ named: [authMode.profile ? "fromIni" : "fromNodeProviderChain"]
8541
+ });
8542
+ break;
8543
+ case "account": builder.addImport({
8544
+ pkg: "hot-updater",
8545
+ named: ["secret"]
8506
8546
  });
8507
8547
  }
8508
8548
  return (0, _hot_updater_cli_tools.createHotUpdaterConfigScaffoldFromBuilder)(builder, { helperStatements });
@@ -8528,9 +8568,9 @@ const prepareDynamoDBDeployment = async (input) => {
8528
8568
  (0, _hot_updater_server_plugins.remoteConfig)()
8529
8569
  ]);
8530
8570
  };
8531
- const runInit = async ({ build, envFile }) => {
8571
+ const runInit = async ({ build, envFile, secretsEnv }) => {
8532
8572
  const nonInteractive = envFile !== void 0;
8533
- const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(process.cwd(), envFile);
8573
+ const initEnvSources = await (0, _hot_updater_cli_tools.readHotUpdaterInitEnv)(process.cwd(), envFile, secretsEnv);
8534
8574
  const { managedEnv } = initEnvSources;
8535
8575
  const providerEnv = (0, _hot_updater_cli_tools.getHotUpdaterInitInputEnv)(initEnvSources, nonInteractive);
8536
8576
  const savedInputs = resolveAwsInitInputs(providerEnv);
@@ -8739,7 +8779,12 @@ const runInit = async ({ build, envFile }) => {
8739
8779
  if (mode === "sso") await (0, _hot_updater_cli_tools.ensureInstallPackages)({ devDependencies: ["@aws-sdk/credential-provider-sso"] });
8740
8780
  else if (mode === "local-session" || mode === "shared-profile") await (0, _hot_updater_cli_tools.ensureInstallPackages)({ devDependencies: ["@aws-sdk/credential-providers"] });
8741
8781
  _hot_updater_cli_tools.p.log.success("Generated '.env.hotupdater' file with AWS settings.");
8742
- await (0, _hot_updater_cli_tools.writeHotUpdaterFiles)(getConfigScaffold(build, configAuthMode), {
8782
+ await (0, _hot_updater_cli_tools.writeHotUpdaterFiles)(getConfigScaffold(build, configAuthMode, {
8783
+ region: bucketRegion,
8784
+ bucketName,
8785
+ tableName: resolvedDynamoDBTableName,
8786
+ cloudfrontDistributionId: distributionId
8787
+ }), {
8743
8788
  cwd: process.cwd(),
8744
8789
  settings: "AWS"
8745
8790
  });
@@ -206,6 +206,7 @@ declare const initProvider: {
206
206
  readonly optional: true;
207
207
  };
208
208
  };
209
+ readonly secrets: readonly ["HOT_UPDATER_S3_ACCESS_KEY_ID", "HOT_UPDATER_S3_SECRET_ACCESS_KEY"];
209
210
  };
210
211
  //#endregion
211
212
  //#region iac/index.d.ts
@@ -218,6 +219,6 @@ declare const prepareDynamoDBDeployment: (input: {
218
219
  readonly region: string;
219
220
  readonly tableName: string;
220
221
  }) => Promise<void>;
221
- declare const runInit: ({ build, envFile }: RunInitOptions) => Promise<void>;
222
+ declare const runInit: ({ build, envFile, secretsEnv }: RunInitOptions) => Promise<void>;
222
223
  //#endregion
223
224
  export { LAMBDA_EDGE_TRUST_POLICY, buildDynamoDBBackupInput, buildDynamoDBCreateTableInput, buildDynamoDBPolicy, buildDynamoDBSchemaSettingsInput, buildDynamoDBTimeToLiveInput, buildS3Policy, buildSsmPolicy, initProvider, prepareDynamoDBDeployment, runInit };
@@ -206,6 +206,7 @@ declare const initProvider: {
206
206
  readonly optional: true;
207
207
  };
208
208
  };
209
+ readonly secrets: readonly ["HOT_UPDATER_S3_ACCESS_KEY_ID", "HOT_UPDATER_S3_SECRET_ACCESS_KEY"];
209
210
  };
210
211
  //#endregion
211
212
  //#region iac/index.d.ts
@@ -218,6 +219,6 @@ declare const prepareDynamoDBDeployment: (input: {
218
219
  readonly region: string;
219
220
  readonly tableName: string;
220
221
  }) => Promise<void>;
221
- declare const runInit: ({ build, envFile }: RunInitOptions) => Promise<void>;
222
+ declare const runInit: ({ build, envFile, secretsEnv }: RunInitOptions) => Promise<void>;
222
223
  //#endregion
223
224
  export { LAMBDA_EDGE_TRUST_POLICY, buildDynamoDBBackupInput, buildDynamoDBCreateTableInput, buildDynamoDBPolicy, buildDynamoDBSchemaSettingsInput, buildDynamoDBTimeToLiveInput, buildS3Policy, buildSsmPolicy, initProvider, prepareDynamoDBDeployment, runInit };