@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.cjs +96 -163
- package/dist/index.d.cts +23 -5
- package/dist/index.d.mts +23 -5
- package/dist/index.mjs +97 -164
- package/dist/init/index.cjs +65 -20
- package/dist/init/index.d.cts +2 -1
- package/dist/init/index.d.mts +2 -1
- package/dist/init/index.mjs +66 -21
- package/dist/lambda/index.cjs +133 -62
- package/package.json +8 -7
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,
|
|
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 {
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
749
|
-
if (
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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}
|
|
783
|
-
if (Buffer.byteLength(value, "utf8") > MAX_SECRET_BYTES) throw
|
|
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();
|
package/dist/init/index.cjs
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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:
|
|
8438
|
-
cloudfrontDistributionId:
|
|
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:
|
|
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:
|
|
8496
|
+
region: ${region},
|
|
8463
8497
|
credentials: fromIni({ profile: process.env.HOT_UPDATER_AWS_PROFILE! }),
|
|
8464
8498
|
};`.trim() : `
|
|
8465
8499
|
const awsOptions = {
|
|
8466
|
-
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:
|
|
8511
|
+
region: ${region},
|
|
8478
8512
|
credentials: {
|
|
8479
|
-
accessKeyId:
|
|
8480
|
-
secretAccessKey:
|
|
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":
|
|
8504
|
-
|
|
8505
|
-
|
|
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
|
});
|
package/dist/init/index.d.cts
CHANGED
|
@@ -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 };
|
package/dist/init/index.d.mts
CHANGED
|
@@ -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 };
|