@hot-updater/aws 1.2.0-rc.0 → 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.cjs
CHANGED
|
@@ -5,8 +5,7 @@ let _aws_sdk_client_cloudfront = require("@aws-sdk/client-cloudfront");
|
|
|
5
5
|
let _aws_sdk_lib_dynamodb = require("@aws-sdk/lib-dynamodb");
|
|
6
6
|
let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
|
|
7
7
|
let _aws_sdk_cloudfront_signer = require("@aws-sdk/cloudfront-signer");
|
|
8
|
-
let
|
|
9
|
-
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
|
8
|
+
let _hot_updater_storage_s3 = require("@hot-updater/storage-s3");
|
|
10
9
|
let node_crypto = require("node:crypto");
|
|
11
10
|
//#region src/cloudFrontInvalidation.ts
|
|
12
11
|
const DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
@@ -315,10 +314,30 @@ async apply() {
|
|
|
315
314
|
};
|
|
316
315
|
};
|
|
317
316
|
//#endregion
|
|
317
|
+
//#region src/lazyCredentials.ts
|
|
318
|
+
/**
|
|
319
|
+
* `credentials` as the AWS SDK takes them: access keys with a lazy field
|
|
320
|
+
* become a provider, which the SDK calls once, on its first request.
|
|
321
|
+
*/
|
|
322
|
+
const toSdkCredentials = (credentials) => {
|
|
323
|
+
if (credentials === void 0 || typeof credentials === "function") return credentials;
|
|
324
|
+
const { accessKeyId, secretAccessKey, sessionToken, ...rest } = credentials;
|
|
325
|
+
if (typeof accessKeyId !== "function" && typeof secretAccessKey !== "function" && typeof sessionToken !== "function") return credentials;
|
|
326
|
+
return async () => ({
|
|
327
|
+
...rest,
|
|
328
|
+
accessKeyId: await (0, _hot_updater_plugin_core.resolveLazy)(accessKeyId),
|
|
329
|
+
secretAccessKey: await (0, _hot_updater_plugin_core.resolveLazy)(secretAccessKey),
|
|
330
|
+
...sessionToken === void 0 ? {} : { sessionToken: await (0, _hot_updater_plugin_core.resolveLazy)(sessionToken) }
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
//#endregion
|
|
318
334
|
//#region src/dynamoDB.ts
|
|
319
335
|
/** The DynamoDB client ignores the CloudFront and batching settings. */
|
|
320
|
-
const adapterOf = ({ tableName, ...clientConfig }) => {
|
|
321
|
-
const client = new _aws_sdk_client_dynamodb.DynamoDBClient(
|
|
336
|
+
const adapterOf = ({ tableName, credentials, ...clientConfig }) => {
|
|
337
|
+
const client = new _aws_sdk_client_dynamodb.DynamoDBClient({
|
|
338
|
+
...clientConfig,
|
|
339
|
+
credentials: toSdkCredentials(credentials)
|
|
340
|
+
});
|
|
322
341
|
const store = createDynamoDBStore({
|
|
323
342
|
client,
|
|
324
343
|
tableName
|
|
@@ -353,7 +372,10 @@ const marked = (target) => ({ [DATABASE_TARGET]: target });
|
|
|
353
372
|
*/
|
|
354
373
|
const dynamoDB = (config) => {
|
|
355
374
|
const { client, adapter } = adapterOf(config);
|
|
356
|
-
const cloudFront = createUpdateRouteInvalidation(
|
|
375
|
+
const cloudFront = createUpdateRouteInvalidation({
|
|
376
|
+
...config,
|
|
377
|
+
credentials: toSdkCredentials(config.credentials)
|
|
378
|
+
});
|
|
357
379
|
return {
|
|
358
380
|
...(0, _hot_updater_plugin_core.createEngineDatabase)({
|
|
359
381
|
name: "dynamoDB",
|
|
@@ -496,153 +518,17 @@ const cloudFrontDownloadUrl = (config) => {
|
|
|
496
518
|
};
|
|
497
519
|
//#endregion
|
|
498
520
|
//#region src/s3Storage.ts
|
|
499
|
-
/** Devices download right after the update check that returns the URL. */
|
|
500
|
-
const PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
|
|
501
|
-
const isObjectNotFoundError = (error) => error instanceof Error && (error.name === "NotFound" || error.name === "NoSuchKey");
|
|
502
521
|
function s3Storage(config) {
|
|
503
|
-
const { bucketName, basePath, getDownloadUrl, ...s3Config } = config;
|
|
504
|
-
|
|
505
|
-
const normalizedBasePath = basePath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
506
|
-
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(normalizedBasePath);
|
|
507
|
-
const getListPrefix = (prefix = "") => {
|
|
508
|
-
const normalizedPrefix = prefix.replace(/^\/+|\/+$/g, "");
|
|
509
|
-
const value = [normalizedBasePath, normalizedPrefix].filter(Boolean).join("/");
|
|
510
|
-
return value ? `${value}/` : "";
|
|
511
|
-
};
|
|
512
|
-
/**
|
|
513
|
-
* The URI `put` returns for the key, or null for a key no `put` writes,
|
|
514
|
-
* such as a folder marker that ends in "/".
|
|
515
|
-
*/
|
|
516
|
-
const getListedStorageUri = (key) => {
|
|
517
|
-
try {
|
|
518
|
-
return (0, _hot_updater_plugin_core.createStorageUri)({
|
|
519
|
-
bucket: bucketName,
|
|
520
|
-
key,
|
|
521
|
-
protocol: "s3"
|
|
522
|
-
});
|
|
523
|
-
} catch {
|
|
524
|
-
return null;
|
|
525
|
-
}
|
|
526
|
-
};
|
|
527
|
-
const getRelativeKey = (key) => {
|
|
528
|
-
if (!normalizedBasePath) return key;
|
|
529
|
-
const basePrefix = `${normalizedBasePath}/`;
|
|
530
|
-
return key.startsWith(basePrefix) ? key.slice(basePrefix.length) : key;
|
|
531
|
-
};
|
|
532
|
-
const parseAndValidate = (storageUri) => {
|
|
533
|
-
const parsed = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "s3");
|
|
534
|
-
if (parsed.bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${parsed.bucket}".`);
|
|
535
|
-
return parsed;
|
|
536
|
-
};
|
|
537
|
-
return (0, _hot_updater_plugin_core.createStorageAdapter)({
|
|
522
|
+
const { bucketName, basePath, getDownloadUrl, credentials, ...s3Config } = config;
|
|
523
|
+
return (0, _hot_updater_storage_s3.createS3Storage)({
|
|
538
524
|
name: "s3Storage",
|
|
539
|
-
protocol: "s3"
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
ContinuationToken: continuationToken,
|
|
547
|
-
Prefix: getListPrefix(prefix)
|
|
548
|
-
}));
|
|
549
|
-
for (const object of response.Contents ?? []) {
|
|
550
|
-
if (!object.Key) continue;
|
|
551
|
-
const storageUri = getListedStorageUri(object.Key);
|
|
552
|
-
if (storageUri === null) continue;
|
|
553
|
-
objects.push({
|
|
554
|
-
key: getRelativeKey(object.Key),
|
|
555
|
-
lastModifiedAt: object.LastModified,
|
|
556
|
-
size: object.Size ?? 0,
|
|
557
|
-
storageUri
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
continuationToken = response.NextContinuationToken;
|
|
561
|
-
} while (continuationToken);
|
|
562
|
-
return objects;
|
|
563
|
-
},
|
|
564
|
-
async deleteObjects(relativeKeys) {
|
|
565
|
-
const keys = relativeKeys.map((key) => getStorageKey(key));
|
|
566
|
-
for (let offset = 0; offset < keys.length; offset += 1e3) {
|
|
567
|
-
const batch = keys.slice(offset, offset + 1e3);
|
|
568
|
-
if (batch.length === 0) continue;
|
|
569
|
-
const response = await client.send(new _aws_sdk_client_s3.DeleteObjectsCommand({
|
|
570
|
-
Bucket: bucketName,
|
|
571
|
-
Delete: {
|
|
572
|
-
Objects: batch.map((Key) => ({ Key })),
|
|
573
|
-
Quiet: true
|
|
574
|
-
}
|
|
575
|
-
}));
|
|
576
|
-
if (response.Errors && response.Errors.length > 0) throw new Error(`Failed to delete ${response.Errors.length} S3 object(s).`);
|
|
577
|
-
}
|
|
578
|
-
},
|
|
579
|
-
async put({ key, body, contentLength, contentType }) {
|
|
580
|
-
const storageKey = getStorageKey(key);
|
|
581
|
-
const response = await new _aws_sdk_lib_storage.Upload({
|
|
582
|
-
client,
|
|
583
|
-
params: {
|
|
584
|
-
Body: body,
|
|
585
|
-
Bucket: bucketName,
|
|
586
|
-
CacheControl: "max-age=31536000",
|
|
587
|
-
...contentLength === void 0 ? {} : { ContentLength: contentLength },
|
|
588
|
-
ContentType: contentType,
|
|
589
|
-
Key: storageKey
|
|
590
|
-
}
|
|
591
|
-
}).done();
|
|
592
|
-
if (!response.Bucket || !response.Key) throw new Error("Upload failed");
|
|
593
|
-
return { storageUri: (0, _hot_updater_plugin_core.createStorageUri)({
|
|
594
|
-
bucket: bucketName,
|
|
595
|
-
key: storageKey,
|
|
596
|
-
protocol: "s3"
|
|
597
|
-
}) };
|
|
598
|
-
},
|
|
599
|
-
async get({ storageUri }) {
|
|
600
|
-
const { key } = parseAndValidate(storageUri);
|
|
601
|
-
try {
|
|
602
|
-
const response = await client.send(new _aws_sdk_client_s3.GetObjectCommand({
|
|
603
|
-
Bucket: bucketName,
|
|
604
|
-
Key: key
|
|
605
|
-
}));
|
|
606
|
-
if (!response.Body) return { response: null };
|
|
607
|
-
const headers = new Headers();
|
|
608
|
-
if (response.ContentType) headers.set("content-type", response.ContentType);
|
|
609
|
-
if (response.ContentLength !== void 0) headers.set("content-length", String(response.ContentLength));
|
|
610
|
-
return { response: new Response(response.Body.transformToWebStream(), { headers }) };
|
|
611
|
-
} catch (error) {
|
|
612
|
-
if (isObjectNotFoundError(error)) return { response: null };
|
|
613
|
-
throw error;
|
|
614
|
-
}
|
|
615
|
-
},
|
|
616
|
-
async getDownloadUrl({ storageUri }) {
|
|
617
|
-
const { key } = parseAndValidate(storageUri);
|
|
618
|
-
if (getDownloadUrl) return getDownloadUrl({ storageUri });
|
|
619
|
-
const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
|
|
620
|
-
return { url: await getSignedUrl(client, new _aws_sdk_client_s3.GetObjectCommand({
|
|
621
|
-
Bucket: bucketName,
|
|
622
|
-
Key: key
|
|
623
|
-
}), { expiresIn: PRESIGNED_URL_EXPIRES_IN_SECONDS }) };
|
|
624
|
-
},
|
|
625
|
-
async exists({ storageUri }) {
|
|
626
|
-
const { key } = parseAndValidate(storageUri);
|
|
627
|
-
try {
|
|
628
|
-
await client.send(new _aws_sdk_client_s3.HeadObjectCommand({
|
|
629
|
-
Bucket: bucketName,
|
|
630
|
-
Key: key
|
|
631
|
-
}));
|
|
632
|
-
return { exists: true };
|
|
633
|
-
} catch (error) {
|
|
634
|
-
if (isObjectNotFoundError(error)) return { exists: false };
|
|
635
|
-
throw error;
|
|
636
|
-
}
|
|
637
|
-
},
|
|
638
|
-
async delete({ storageUri }) {
|
|
639
|
-
const { key } = parseAndValidate(storageUri);
|
|
640
|
-
await client.send(new _aws_sdk_client_s3.DeleteObjectCommand({
|
|
641
|
-
Bucket: bucketName,
|
|
642
|
-
Key: key
|
|
643
|
-
}));
|
|
644
|
-
return { deleted: true };
|
|
645
|
-
}
|
|
525
|
+
protocol: "s3"
|
|
526
|
+
}, {
|
|
527
|
+
...applyS3RuntimeAwsConfig(s3Config),
|
|
528
|
+
credentials,
|
|
529
|
+
bucketName,
|
|
530
|
+
basePath,
|
|
531
|
+
getDownloadUrl
|
|
646
532
|
});
|
|
647
533
|
}
|
|
648
534
|
//#endregion
|
|
@@ -667,7 +553,22 @@ const EXPIRED_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set([
|
|
|
667
553
|
]);
|
|
668
554
|
/** Errors of the SDK finding no credentials, or an expired SSO session. */
|
|
669
555
|
const NO_CREDENTIAL_ERRORS = /* @__PURE__ */ new Set(["CredentialsProviderError", "TokenProviderError"]);
|
|
670
|
-
const SIGN_IN_HINT = "In CI, set them with aws-actions/configure-aws-credentials
|
|
556
|
+
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."];
|
|
557
|
+
/** The widest line a message has: the CLI prints no wider. */
|
|
558
|
+
const LINE_WIDTH = 76;
|
|
559
|
+
/** `text`, such as AWS's own message, in lines that fit, broken at spaces. */
|
|
560
|
+
const wrap = (text) => {
|
|
561
|
+
const lines = [];
|
|
562
|
+
let line = "";
|
|
563
|
+
for (const word of text.split(/\s+/u).filter(Boolean)) if (line && line.length + 1 + word.length > LINE_WIDTH) {
|
|
564
|
+
lines.push(line);
|
|
565
|
+
line = word;
|
|
566
|
+
} else line = line ? `${line} ${word}` : word;
|
|
567
|
+
if (line) lines.push(line);
|
|
568
|
+
return lines;
|
|
569
|
+
};
|
|
570
|
+
/** An error of one line each, for a terminal. */
|
|
571
|
+
const failure = (lines, cause) => new Error(lines.join("\n"), cause === void 0 ? void 0 : { cause });
|
|
671
572
|
const errorName = (error) => error instanceof Error ? error.name : "";
|
|
672
573
|
const errorMessage = (error) => (error instanceof Error ? error.message : String(error)).replace(/\.+$/u, "");
|
|
673
574
|
const resolveEndpoint = (endpoint) => {
|
|
@@ -682,7 +583,11 @@ const resolveEndpoint = (endpoint) => {
|
|
|
682
583
|
"127.0.0.1",
|
|
683
584
|
"[::1]"
|
|
684
585
|
])).has(url.hostname);
|
|
685
|
-
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback) || url.username || url.password || url.search || url.hash) throw
|
|
586
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback) || url.username || url.password || url.search || url.hash) throw failure([
|
|
587
|
+
`${NAME} can't use its endpoint.`,
|
|
588
|
+
"Use HTTPS, or HTTP on a loopback address.",
|
|
589
|
+
"Leave out credentials, a query, and a fragment."
|
|
590
|
+
]);
|
|
686
591
|
return url.toString();
|
|
687
592
|
};
|
|
688
593
|
/** Masks a value in the CLI's output, which loads `@hot-updater/cli-tools`. */
|
|
@@ -709,20 +614,20 @@ const checkCredentials = async (credentials) => {
|
|
|
709
614
|
*/
|
|
710
615
|
const secretsManager = ({ credentials, endpoint, prefix = "hot-updater/", region }) => {
|
|
711
616
|
const checkSettings = () => {
|
|
712
|
-
if (typeof region !== "string" || !REGION_PATTERN.test(region)) throw
|
|
617
|
+
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\"."]);
|
|
713
618
|
if (prefix !== "" && !SECRET_ID_PATTERN.test(prefix)) throw new Error(`${NAME}'s prefix takes letters, digits, and /_+=.@- only.`);
|
|
714
619
|
};
|
|
715
620
|
const secretId = (name) => {
|
|
716
621
|
checkSettings();
|
|
717
622
|
const id = `${prefix}${name}`;
|
|
718
|
-
if (!SECRET_ID_PATTERN.test(name) || id.length > MAX_SECRET_ID_LENGTH) throw
|
|
623
|
+
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.`]);
|
|
719
624
|
return id;
|
|
720
625
|
};
|
|
721
626
|
const resolveCredentials = () => {
|
|
722
627
|
if (credentials !== void 0) return async () => checkCredentials(typeof credentials === "function" ? await credentials() : credentials);
|
|
723
628
|
const accessKeyId = process.env["AWS_ACCESS_KEY_ID"];
|
|
724
629
|
const secretAccessKey = process.env["AWS_SECRET_ACCESS_KEY"];
|
|
725
|
-
if (Boolean(accessKeyId) !== Boolean(secretAccessKey)) throw
|
|
630
|
+
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."]);
|
|
726
631
|
};
|
|
727
632
|
let opened;
|
|
728
633
|
const open = () => {
|
|
@@ -745,12 +650,40 @@ const secretsManager = ({ credentials, endpoint, prefix = "hot-updater/", region
|
|
|
745
650
|
const describeFailure = (verb, id, error, permissions) => {
|
|
746
651
|
const name = errorName(error);
|
|
747
652
|
const status = error?.$metadata?.httpStatusCode;
|
|
748
|
-
const detail = `${name || "Error"}${status ? ` (${status})` : ""}: ${errorMessage(error)}
|
|
749
|
-
|
|
750
|
-
if (
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
653
|
+
const detail = wrap(`${name || "Error"}${status ? ` (${status})` : ""}: ${errorMessage(error)}.`);
|
|
654
|
+
const secret = `Secret: ${id}`;
|
|
655
|
+
if (NO_CREDENTIAL_ERRORS.has(name)) return failure([
|
|
656
|
+
`${NAME} found no AWS credentials to ${verb} a secret.`,
|
|
657
|
+
secret,
|
|
658
|
+
...wrap(`${errorMessage(error)}.`),
|
|
659
|
+
...SIGN_IN_HINT
|
|
660
|
+
], error);
|
|
661
|
+
if (EXPIRED_CREDENTIAL_ERRORS.has(name)) return failure([
|
|
662
|
+
`AWS Secrets Manager refused the sign-in to ${verb} a secret.`,
|
|
663
|
+
secret,
|
|
664
|
+
...detail,
|
|
665
|
+
"They are expired or invalid: sign in again.",
|
|
666
|
+
...SIGN_IN_HINT
|
|
667
|
+
], error);
|
|
668
|
+
if (name === "AccessDeniedException") return failure([
|
|
669
|
+
`AWS Secrets Manager refused to ${verb} a secret.`,
|
|
670
|
+
secret,
|
|
671
|
+
...detail,
|
|
672
|
+
`The AWS identity needs ${permissions} on it.`,
|
|
673
|
+
`With a customer managed KMS key, it needs ${verb === "read" ? "kms:Decrypt" : "kms:GenerateDataKey"} too.`
|
|
674
|
+
], error);
|
|
675
|
+
if (/marked for deletion|scheduled for deletion/iu.test(errorMessage(error))) return failure([
|
|
676
|
+
`AWS Secrets Manager can't ${verb} a secret.`,
|
|
677
|
+
secret,
|
|
678
|
+
"It is scheduled for deletion. Restore it, or wait until it is gone:",
|
|
679
|
+
"aws secretsmanager restore-secret \\",
|
|
680
|
+
` --secret-id ${id}`
|
|
681
|
+
], error);
|
|
682
|
+
return failure([
|
|
683
|
+
`AWS Secrets Manager failed to ${verb} a secret.`,
|
|
684
|
+
secret,
|
|
685
|
+
...detail
|
|
686
|
+
], error);
|
|
754
687
|
};
|
|
755
688
|
const read = async ({ sdk, client }, id) => {
|
|
756
689
|
let output;
|
|
@@ -780,8 +713,8 @@ const secretsManager = ({ credentials, endpoint, prefix = "hot-updater/", region
|
|
|
780
713
|
},
|
|
781
714
|
set: async (name, value) => {
|
|
782
715
|
const id = secretId(name);
|
|
783
|
-
if (typeof value !== "string" || value === "") throw new Error(`${NAME}
|
|
784
|
-
if (Buffer.byteLength(value, "utf8") > MAX_SECRET_BYTES) throw
|
|
716
|
+
if (typeof value !== "string" || value === "") throw new Error(`${NAME} can't store an empty ${id}.`);
|
|
717
|
+
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.`]);
|
|
785
718
|
await mask([value]);
|
|
786
719
|
const { sdk, client } = await open();
|
|
787
720
|
const token = (0, node_crypto.randomUUID)();
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import { DynamoDBClientConfig } from "@aws-sdk/client-dynamodb";
|
|
2
|
-
import { EngineDatabase, PluginTables, SecretsAdapter, StorageAdapter, StorageAdapterWith } from "@hot-updater/plugin-core";
|
|
2
|
+
import { EngineDatabase, Lazy, PluginTables, SecretsAdapter, StorageAdapter, StorageAdapterWith } from "@hot-updater/plugin-core";
|
|
3
3
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
4
|
+
import { S3StorageOperations } from "@hot-updater/storage-s3";
|
|
5
|
+
//#region src/lazyCredentials.d.ts
|
|
6
|
+
type SdkCredentials = S3ClientConfig["credentials"];
|
|
7
|
+
/**
|
|
8
|
+
* Access keys whose fields may each be a function that returns them, such
|
|
9
|
+
* as `secret("<name>")` in `hot-updater.config.ts`, called on first use.
|
|
10
|
+
*/
|
|
11
|
+
interface LazyAwsCredentials {
|
|
12
|
+
readonly accessKeyId: Lazy<string>;
|
|
13
|
+
readonly secretAccessKey: Lazy<string>;
|
|
14
|
+
readonly sessionToken?: Lazy<string>;
|
|
15
|
+
}
|
|
16
|
+
/** The credentials the AWS SDK takes, or access keys with lazy fields. */
|
|
17
|
+
type AwsCredentialsOption = SdkCredentials | LazyAwsCredentials;
|
|
18
|
+
//#endregion
|
|
4
19
|
//#region src/dynamoDB.d.ts
|
|
5
|
-
interface DynamoDBConfig extends DynamoDBClientConfig {
|
|
20
|
+
interface DynamoDBConfig extends Omit<DynamoDBClientConfig, "credentials"> {
|
|
21
|
+
/** The SDK's credentials, or access keys whose fields are lazy. */
|
|
22
|
+
readonly credentials?: AwsCredentialsOption;
|
|
6
23
|
readonly apiBasePath?: string;
|
|
7
24
|
readonly cloudfrontDistributionId?: string;
|
|
8
25
|
readonly shouldWaitForInvalidation?: boolean;
|
|
@@ -59,7 +76,9 @@ declare const cloudFrontDownloadUrl: (config: CloudFrontDownloadUrlOptions) => (
|
|
|
59
76
|
}>;
|
|
60
77
|
//#endregion
|
|
61
78
|
//#region src/s3Storage.d.ts
|
|
62
|
-
interface S3StorageConfig extends S3ClientConfig {
|
|
79
|
+
interface S3StorageConfig extends Omit<S3ClientConfig, "credentials"> {
|
|
80
|
+
/** The SDK's credentials, or access keys whose fields are lazy. */
|
|
81
|
+
credentials?: AwsCredentialsOption;
|
|
63
82
|
bucketName: string;
|
|
64
83
|
/** Base path where bundles will be stored in the bucket. */
|
|
65
84
|
basePath?: string;
|
|
@@ -70,7 +89,6 @@ interface S3StorageConfig extends S3ClientConfig {
|
|
|
70
89
|
*/
|
|
71
90
|
getDownloadUrl?: StorageAdapter["getDownloadUrl"];
|
|
72
91
|
}
|
|
73
|
-
type S3StorageOperations = "put" | "get" | "getDownloadUrl" | "exists" | "delete" | "listObjects" | "deleteObjects";
|
|
74
92
|
declare function s3Storage(config: S3StorageConfig): StorageAdapterWith<S3StorageOperations>;
|
|
75
93
|
//#endregion
|
|
76
94
|
//#region src/secretsManager.d.ts
|
|
@@ -85,7 +103,7 @@ interface SecretsManagerOptions {
|
|
|
85
103
|
/** The AWS region the secrets are in, such as `"us-east-1"`. */
|
|
86
104
|
readonly region: string;
|
|
87
105
|
/**
|
|
88
|
-
* Put before every name to make its secret's name, so `secret
|
|
106
|
+
* Put before every name to make its secret's name, so `secret("asc-api-key")`
|
|
89
107
|
* reads the secret `hot-updater/asc-api-key` and one IAM resource,
|
|
90
108
|
* `secret:hot-updater/*`, covers them all. Letters, digits, and
|
|
91
109
|
* `/_+=.@-`.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import { DynamoDBClientConfig } from "@aws-sdk/client-dynamodb";
|
|
2
|
-
import { EngineDatabase, PluginTables, SecretsAdapter, StorageAdapter, StorageAdapterWith } from "@hot-updater/plugin-core";
|
|
2
|
+
import { EngineDatabase, Lazy, PluginTables, SecretsAdapter, StorageAdapter, StorageAdapterWith } from "@hot-updater/plugin-core";
|
|
3
|
+
import { S3StorageOperations } from "@hot-updater/storage-s3";
|
|
3
4
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
5
|
+
//#region src/lazyCredentials.d.ts
|
|
6
|
+
type SdkCredentials = S3ClientConfig["credentials"];
|
|
7
|
+
/**
|
|
8
|
+
* Access keys whose fields may each be a function that returns them, such
|
|
9
|
+
* as `secret("<name>")` in `hot-updater.config.ts`, called on first use.
|
|
10
|
+
*/
|
|
11
|
+
interface LazyAwsCredentials {
|
|
12
|
+
readonly accessKeyId: Lazy<string>;
|
|
13
|
+
readonly secretAccessKey: Lazy<string>;
|
|
14
|
+
readonly sessionToken?: Lazy<string>;
|
|
15
|
+
}
|
|
16
|
+
/** The credentials the AWS SDK takes, or access keys with lazy fields. */
|
|
17
|
+
type AwsCredentialsOption = SdkCredentials | LazyAwsCredentials;
|
|
18
|
+
//#endregion
|
|
4
19
|
//#region src/dynamoDB.d.ts
|
|
5
|
-
interface DynamoDBConfig extends DynamoDBClientConfig {
|
|
20
|
+
interface DynamoDBConfig extends Omit<DynamoDBClientConfig, "credentials"> {
|
|
21
|
+
/** The SDK's credentials, or access keys whose fields are lazy. */
|
|
22
|
+
readonly credentials?: AwsCredentialsOption;
|
|
6
23
|
readonly apiBasePath?: string;
|
|
7
24
|
readonly cloudfrontDistributionId?: string;
|
|
8
25
|
readonly shouldWaitForInvalidation?: boolean;
|
|
@@ -59,7 +76,9 @@ declare const cloudFrontDownloadUrl: (config: CloudFrontDownloadUrlOptions) => (
|
|
|
59
76
|
}>;
|
|
60
77
|
//#endregion
|
|
61
78
|
//#region src/s3Storage.d.ts
|
|
62
|
-
interface S3StorageConfig extends S3ClientConfig {
|
|
79
|
+
interface S3StorageConfig extends Omit<S3ClientConfig, "credentials"> {
|
|
80
|
+
/** The SDK's credentials, or access keys whose fields are lazy. */
|
|
81
|
+
credentials?: AwsCredentialsOption;
|
|
63
82
|
bucketName: string;
|
|
64
83
|
/** Base path where bundles will be stored in the bucket. */
|
|
65
84
|
basePath?: string;
|
|
@@ -70,7 +89,6 @@ interface S3StorageConfig extends S3ClientConfig {
|
|
|
70
89
|
*/
|
|
71
90
|
getDownloadUrl?: StorageAdapter["getDownloadUrl"];
|
|
72
91
|
}
|
|
73
|
-
type S3StorageOperations = "put" | "get" | "getDownloadUrl" | "exists" | "delete" | "listObjects" | "deleteObjects";
|
|
74
92
|
declare function s3Storage(config: S3StorageConfig): StorageAdapterWith<S3StorageOperations>;
|
|
75
93
|
//#endregion
|
|
76
94
|
//#region src/secretsManager.d.ts
|
|
@@ -85,7 +103,7 @@ interface SecretsManagerOptions {
|
|
|
85
103
|
/** The AWS region the secrets are in, such as `"us-east-1"`. */
|
|
86
104
|
readonly region: string;
|
|
87
105
|
/**
|
|
88
|
-
* Put before every name to make its secret's name, so `secret
|
|
106
|
+
* Put before every name to make its secret's name, so `secret("asc-api-key")`
|
|
89
107
|
* reads the secret `hot-updater/asc-api-key` and one IAM resource,
|
|
90
108
|
* `secret:hot-updater/*`, covers them all. Letters, digits, and
|
|
91
109
|
* `/_+=.@-`.
|