@hot-updater/aws 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/iac/index.cjs +426 -690
- package/dist/iac/{index.js → index.mjs} +304 -548
- package/dist/index.cjs +179 -59
- package/dist/index.d.cts +37 -4
- package/dist/index.d.mts +62 -0
- package/dist/{index.js → index.mjs} +155 -33
- package/dist/lambda/index.cjs +5819 -6848
- package/dist/lambda/index.d.cts +4 -1
- package/package.json +15 -13
- package/dist/index.d.ts +0 -29
- package/dist/lambda/dist-cjs-Mm7FDWb8.cjs +0 -128
- package/dist/lambda/event-streams-0D7SB645.cjs +0 -204
- /package/dist/iac/{index.d.ts → index.d.mts} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -19,23 +20,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
19
20
|
value: mod,
|
|
20
21
|
enumerable: true
|
|
21
22
|
}) : target, mod));
|
|
22
|
-
|
|
23
23
|
//#endregion
|
|
24
|
-
let
|
|
25
|
-
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
let
|
|
29
|
-
__aws_sdk_lib_storage = __toESM(__aws_sdk_lib_storage);
|
|
30
|
-
let __hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
31
|
-
__hot_updater_plugin_core = __toESM(__hot_updater_plugin_core);
|
|
32
|
-
let __aws_sdk_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
33
|
-
__aws_sdk_s3_request_presigner = __toESM(__aws_sdk_s3_request_presigner);
|
|
24
|
+
let _aws_sdk_client_cloudfront = require("@aws-sdk/client-cloudfront");
|
|
25
|
+
let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
26
|
+
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
|
27
|
+
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
28
|
+
let _aws_sdk_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
34
29
|
let fs_promises = require("fs/promises");
|
|
35
30
|
fs_promises = __toESM(fs_promises);
|
|
36
31
|
let path = require("path");
|
|
37
32
|
path = __toESM(path);
|
|
38
|
-
|
|
33
|
+
let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
|
|
34
|
+
let _aws_sdk_cloudfront_signer = require("@aws-sdk/cloudfront-signer");
|
|
39
35
|
//#region ../../node_modules/.pnpm/mime@4.0.4/node_modules/mime/dist/types/other.js
|
|
40
36
|
const types$1 = {
|
|
41
37
|
"application/prs.cww": ["cww"],
|
|
@@ -849,8 +845,6 @@ const types$1 = {
|
|
|
849
845
|
"x-conference/x-cooltalk": ["ice"]
|
|
850
846
|
};
|
|
851
847
|
Object.freeze(types$1);
|
|
852
|
-
var other_default = types$1;
|
|
853
|
-
|
|
854
848
|
//#endregion
|
|
855
849
|
//#region ../../node_modules/.pnpm/mime@4.0.4/node_modules/mime/dist/types/standard.js
|
|
856
850
|
const types = {
|
|
@@ -1296,11 +1290,9 @@ const types = {
|
|
|
1296
1290
|
"video/webm": ["webm"]
|
|
1297
1291
|
};
|
|
1298
1292
|
Object.freeze(types);
|
|
1299
|
-
var standard_default = types;
|
|
1300
|
-
|
|
1301
1293
|
//#endregion
|
|
1302
1294
|
//#region ../../node_modules/.pnpm/mime@4.0.4/node_modules/mime/dist/src/Mime.js
|
|
1303
|
-
var __classPrivateFieldGet =
|
|
1295
|
+
var __classPrivateFieldGet = function(receiver, state, kind, f) {
|
|
1304
1296
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
1305
1297
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
1306
1298
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
@@ -1334,11 +1326,11 @@ var Mime = class {
|
|
|
1334
1326
|
}
|
|
1335
1327
|
return this;
|
|
1336
1328
|
}
|
|
1337
|
-
getType(path
|
|
1338
|
-
if (typeof path
|
|
1339
|
-
const last = path
|
|
1329
|
+
getType(path) {
|
|
1330
|
+
if (typeof path !== "string") return null;
|
|
1331
|
+
const last = path.replace(/^.*[/\\]/, "").toLowerCase();
|
|
1340
1332
|
const ext = last.replace(/^.*\./, "").toLowerCase();
|
|
1341
|
-
const hasPath = last.length < path
|
|
1333
|
+
const hasPath = last.length < path.length;
|
|
1342
1334
|
if (!(ext.length < last.length - 1) && hasPath) return null;
|
|
1343
1335
|
return __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(ext) ?? null;
|
|
1344
1336
|
}
|
|
@@ -1367,12 +1359,44 @@ var Mime = class {
|
|
|
1367
1359
|
}
|
|
1368
1360
|
};
|
|
1369
1361
|
_Mime_extensionToType = /* @__PURE__ */ new WeakMap(), _Mime_typeToExtension = /* @__PURE__ */ new WeakMap(), _Mime_typeToExtensions = /* @__PURE__ */ new WeakMap();
|
|
1370
|
-
var Mime_default = Mime;
|
|
1371
|
-
|
|
1372
1362
|
//#endregion
|
|
1373
1363
|
//#region ../../node_modules/.pnpm/mime@4.0.4/node_modules/mime/dist/src/index.js
|
|
1374
|
-
var src_default = new
|
|
1375
|
-
|
|
1364
|
+
var src_default = new Mime(types, types$1)._freeze();
|
|
1365
|
+
//#endregion
|
|
1366
|
+
//#region src/runtimeAwsConfig.ts
|
|
1367
|
+
const truthyValues = new Set([
|
|
1368
|
+
"1",
|
|
1369
|
+
"true",
|
|
1370
|
+
"yes",
|
|
1371
|
+
"on"
|
|
1372
|
+
]);
|
|
1373
|
+
const isTruthy = (value) => {
|
|
1374
|
+
if (!value) return false;
|
|
1375
|
+
return truthyValues.has(value.toLowerCase());
|
|
1376
|
+
};
|
|
1377
|
+
const getAwsEndpointUrl = () => {
|
|
1378
|
+
return process.env.AWS_ENDPOINT_URL?.trim() || void 0;
|
|
1379
|
+
};
|
|
1380
|
+
const shouldForcePathStyle = (forcePathStyle, endpoint) => {
|
|
1381
|
+
if (forcePathStyle !== void 0) return forcePathStyle;
|
|
1382
|
+
if (isTruthy(process.env.AWS_S3_FORCE_PATH_STYLE)) return true;
|
|
1383
|
+
return endpoint !== void 0;
|
|
1384
|
+
};
|
|
1385
|
+
const applyS3RuntimeAwsConfig = (config) => {
|
|
1386
|
+
const endpoint = config.endpoint ?? getAwsEndpointUrl();
|
|
1387
|
+
return {
|
|
1388
|
+
...config,
|
|
1389
|
+
endpoint,
|
|
1390
|
+
forcePathStyle: shouldForcePathStyle(config.forcePathStyle, endpoint)
|
|
1391
|
+
};
|
|
1392
|
+
};
|
|
1393
|
+
const applySsmRuntimeAwsConfig = (config) => {
|
|
1394
|
+
const endpoint = config.endpoint ?? getAwsEndpointUrl();
|
|
1395
|
+
return {
|
|
1396
|
+
...config,
|
|
1397
|
+
endpoint
|
|
1398
|
+
};
|
|
1399
|
+
};
|
|
1376
1400
|
//#endregion
|
|
1377
1401
|
//#region src/utils/streamToString.ts
|
|
1378
1402
|
const streamToString = (stream) => {
|
|
@@ -1383,16 +1407,18 @@ const streamToString = (stream) => {
|
|
|
1383
1407
|
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
1384
1408
|
});
|
|
1385
1409
|
};
|
|
1386
|
-
|
|
1387
1410
|
//#endregion
|
|
1388
1411
|
//#region src/s3Database.ts
|
|
1412
|
+
const DEFAULT_INVALIDATION_POLL_INTERVAL_MS = 2e3;
|
|
1413
|
+
const DEFAULT_INVALIDATION_TIMEOUT_MS = 300 * 1e3;
|
|
1414
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1389
1415
|
/**
|
|
1390
1416
|
* Loads JSON data from S3.
|
|
1391
1417
|
* Returns null if NoSuchKey error occurs.
|
|
1392
1418
|
*/
|
|
1393
1419
|
async function loadJsonFromS3(client, bucket, key) {
|
|
1394
1420
|
try {
|
|
1395
|
-
const { Body } = await client.send(new
|
|
1421
|
+
const { Body } = await client.send(new _aws_sdk_client_s3.GetObjectCommand({
|
|
1396
1422
|
Bucket: bucket,
|
|
1397
1423
|
Key: key
|
|
1398
1424
|
}));
|
|
@@ -1400,7 +1426,7 @@ async function loadJsonFromS3(client, bucket, key) {
|
|
|
1400
1426
|
const bodyContents = await streamToString(Body);
|
|
1401
1427
|
return JSON.parse(bodyContents);
|
|
1402
1428
|
} catch (e) {
|
|
1403
|
-
if (e instanceof
|
|
1429
|
+
if (e instanceof _aws_sdk_client_s3.NoSuchKey) return null;
|
|
1404
1430
|
throw e;
|
|
1405
1431
|
}
|
|
1406
1432
|
}
|
|
@@ -1408,7 +1434,7 @@ async function loadJsonFromS3(client, bucket, key) {
|
|
|
1408
1434
|
* Converts data to JSON string and uploads to S3.
|
|
1409
1435
|
*/
|
|
1410
1436
|
async function uploadJsonToS3(client, bucket, key, data) {
|
|
1411
|
-
await new
|
|
1437
|
+
await new _aws_sdk_lib_storage.Upload({
|
|
1412
1438
|
client,
|
|
1413
1439
|
params: {
|
|
1414
1440
|
Bucket: bucket,
|
|
@@ -1423,7 +1449,7 @@ async function listObjectsInS3(client, bucketName, prefix) {
|
|
|
1423
1449
|
let continuationToken;
|
|
1424
1450
|
const keys = [];
|
|
1425
1451
|
do {
|
|
1426
|
-
const response = await client.send(new
|
|
1452
|
+
const response = await client.send(new _aws_sdk_client_s3.ListObjectsV2Command({
|
|
1427
1453
|
Bucket: bucketName,
|
|
1428
1454
|
Prefix: prefix,
|
|
1429
1455
|
ContinuationToken: continuationToken
|
|
@@ -1435,7 +1461,7 @@ async function listObjectsInS3(client, bucketName, prefix) {
|
|
|
1435
1461
|
return keys;
|
|
1436
1462
|
}
|
|
1437
1463
|
async function deleteObjectInS3(client, bucketName, key) {
|
|
1438
|
-
await client.send(new
|
|
1464
|
+
await client.send(new _aws_sdk_client_s3.DeleteObjectCommand({
|
|
1439
1465
|
Bucket: bucketName,
|
|
1440
1466
|
Key: key
|
|
1441
1467
|
}));
|
|
@@ -1443,26 +1469,50 @@ async function deleteObjectInS3(client, bucketName, key) {
|
|
|
1443
1469
|
/**
|
|
1444
1470
|
* Invalidates CloudFront cache for the given paths.
|
|
1445
1471
|
*/
|
|
1446
|
-
async function invalidateCloudFront(client, distributionId, paths) {
|
|
1472
|
+
async function invalidateCloudFront(client, distributionId, paths, options) {
|
|
1447
1473
|
if (paths.length === 0) return;
|
|
1448
1474
|
const timestamp = Date.now();
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1475
|
+
try {
|
|
1476
|
+
const response = await client.send(new _aws_sdk_client_cloudfront.CreateInvalidationCommand({
|
|
1477
|
+
DistributionId: distributionId,
|
|
1478
|
+
InvalidationBatch: {
|
|
1479
|
+
CallerReference: `invalidation-${timestamp}`,
|
|
1480
|
+
Paths: {
|
|
1481
|
+
Quantity: paths.length,
|
|
1482
|
+
Items: paths
|
|
1483
|
+
}
|
|
1456
1484
|
}
|
|
1485
|
+
}));
|
|
1486
|
+
if (!options?.shouldWaitForInvalidation) return;
|
|
1487
|
+
const invalidationId = response.Invalidation?.Id;
|
|
1488
|
+
if (!invalidationId) throw new Error("CloudFront invalidation response is missing Invalidation.Id");
|
|
1489
|
+
if (response.Invalidation?.Status === "Completed") return;
|
|
1490
|
+
const timeoutMs = DEFAULT_INVALIDATION_TIMEOUT_MS;
|
|
1491
|
+
const pollIntervalMs = DEFAULT_INVALIDATION_POLL_INTERVAL_MS;
|
|
1492
|
+
const deadline = Date.now() + timeoutMs;
|
|
1493
|
+
while (Date.now() < deadline) {
|
|
1494
|
+
await sleep(pollIntervalMs);
|
|
1495
|
+
if ((await client.send(new _aws_sdk_client_cloudfront.GetInvalidationCommand({
|
|
1496
|
+
DistributionId: distributionId,
|
|
1497
|
+
Id: invalidationId
|
|
1498
|
+
}))).Invalidation?.Status === "Completed") return;
|
|
1457
1499
|
}
|
|
1458
|
-
|
|
1500
|
+
throw new Error(`Timed out waiting for CloudFront invalidation ${invalidationId} to complete after ${timeoutMs}ms`);
|
|
1501
|
+
} catch (error) {
|
|
1502
|
+
if (options?.shouldWaitForInvalidation) throw error;
|
|
1503
|
+
const message = error instanceof Error ? error.message : "Unknown invalidation error";
|
|
1504
|
+
console.warn(`[hot-updater/aws] CloudFront invalidation failed for distribution ${distributionId}; continuing without cache invalidation.`, {
|
|
1505
|
+
error: message,
|
|
1506
|
+
paths
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1459
1509
|
}
|
|
1460
|
-
const s3Database = (0,
|
|
1510
|
+
const s3Database = (0, _hot_updater_plugin_core.createBlobDatabasePlugin)({
|
|
1461
1511
|
name: "s3Database",
|
|
1462
1512
|
factory: (config) => {
|
|
1463
|
-
const { bucketName, cloudfrontDistributionId, apiBasePath = "/api/check-update"
|
|
1464
|
-
const client = new
|
|
1465
|
-
const cloudfrontClient = cloudfrontDistributionId ? new
|
|
1513
|
+
const { bucketName, cloudfrontDistributionId, apiBasePath = "/api/check-update", shouldWaitForInvalidation = false, ...s3Config } = config;
|
|
1514
|
+
const client = new _aws_sdk_client_s3.S3Client(applyS3RuntimeAwsConfig(s3Config));
|
|
1515
|
+
const cloudfrontClient = cloudfrontDistributionId ? new _aws_sdk_client_cloudfront.CloudFrontClient({
|
|
1466
1516
|
credentials: s3Config.credentials,
|
|
1467
1517
|
region: s3Config.region
|
|
1468
1518
|
}) : void 0;
|
|
@@ -1473,33 +1523,32 @@ const s3Database = (0, __hot_updater_plugin_core.createBlobDatabasePlugin)({
|
|
|
1473
1523
|
uploadObject: (key, data) => uploadJsonToS3(client, bucketName, key, data),
|
|
1474
1524
|
deleteObject: (key) => deleteObjectInS3(client, bucketName, key),
|
|
1475
1525
|
invalidatePaths: (pathsToInvalidate) => {
|
|
1476
|
-
if (cloudfrontClient && cloudfrontDistributionId && pathsToInvalidate.length > 0) return invalidateCloudFront(cloudfrontClient, cloudfrontDistributionId, pathsToInvalidate);
|
|
1526
|
+
if (cloudfrontClient && cloudfrontDistributionId && pathsToInvalidate.length > 0) return invalidateCloudFront(cloudfrontClient, cloudfrontDistributionId, pathsToInvalidate, { shouldWaitForInvalidation });
|
|
1477
1527
|
return Promise.resolve();
|
|
1478
1528
|
}
|
|
1479
1529
|
};
|
|
1480
1530
|
}
|
|
1481
1531
|
});
|
|
1482
|
-
|
|
1483
1532
|
//#endregion
|
|
1484
1533
|
//#region src/s3Storage.ts
|
|
1485
|
-
const s3Storage = (0,
|
|
1534
|
+
const s3Storage = (0, _hot_updater_plugin_core.createStoragePlugin)({
|
|
1486
1535
|
name: "s3Storage",
|
|
1487
1536
|
supportedProtocol: "s3",
|
|
1488
1537
|
factory: (config) => {
|
|
1489
|
-
const { bucketName
|
|
1490
|
-
const client = new
|
|
1491
|
-
const getStorageKey = (0,
|
|
1538
|
+
const { bucketName, ...s3Config } = config;
|
|
1539
|
+
const client = new _aws_sdk_client_s3.S3Client(applyS3RuntimeAwsConfig(s3Config));
|
|
1540
|
+
const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
|
|
1492
1541
|
return {
|
|
1493
1542
|
async delete(storageUri) {
|
|
1494
|
-
const { bucket, key } = (0,
|
|
1543
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "s3");
|
|
1495
1544
|
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
1496
|
-
const listCommand = new
|
|
1545
|
+
const listCommand = new _aws_sdk_client_s3.ListObjectsV2Command({
|
|
1497
1546
|
Bucket: bucketName,
|
|
1498
1547
|
Prefix: key
|
|
1499
1548
|
});
|
|
1500
1549
|
const listResponse = await client.send(listCommand);
|
|
1501
1550
|
if (listResponse.Contents && listResponse.Contents.length > 0) {
|
|
1502
|
-
const deleteCommand = new
|
|
1551
|
+
const deleteCommand = new _aws_sdk_client_s3.DeleteObjectsCommand({
|
|
1503
1552
|
Bucket: bucketName,
|
|
1504
1553
|
Delete: {
|
|
1505
1554
|
Objects: listResponse.Contents.map((obj) => ({ Key: obj.Key })),
|
|
@@ -1513,9 +1562,9 @@ const s3Storage = (0, __hot_updater_plugin_core.createStoragePlugin)({
|
|
|
1513
1562
|
},
|
|
1514
1563
|
async upload(key, filePath) {
|
|
1515
1564
|
const Body = await fs_promises.default.readFile(filePath);
|
|
1516
|
-
const ContentType = (0,
|
|
1565
|
+
const ContentType = (0, _hot_updater_plugin_core.getContentType)(filePath);
|
|
1517
1566
|
const Key = getStorageKey(key, path.default.basename(filePath));
|
|
1518
|
-
const response = await new
|
|
1567
|
+
const response = await new _aws_sdk_lib_storage.Upload({
|
|
1519
1568
|
client,
|
|
1520
1569
|
params: {
|
|
1521
1570
|
ContentType,
|
|
@@ -1535,7 +1584,7 @@ const s3Storage = (0, __hot_updater_plugin_core.createStoragePlugin)({
|
|
|
1535
1584
|
const key = u.pathname.slice(1);
|
|
1536
1585
|
if (!bucket || !key) throw new Error("Invalid S3 storage URI: missing bucket or key");
|
|
1537
1586
|
try {
|
|
1538
|
-
const signedUrl = await (0,
|
|
1587
|
+
const signedUrl = await (0, _aws_sdk_s3_request_presigner.getSignedUrl)(client, new _aws_sdk_client_s3.GetObjectCommand({
|
|
1539
1588
|
Bucket: bucket,
|
|
1540
1589
|
Key: key
|
|
1541
1590
|
}), { expiresIn: 3600 });
|
|
@@ -1548,7 +1597,78 @@ const s3Storage = (0, __hot_updater_plugin_core.createStoragePlugin)({
|
|
|
1548
1597
|
};
|
|
1549
1598
|
}
|
|
1550
1599
|
});
|
|
1551
|
-
|
|
1552
1600
|
//#endregion
|
|
1601
|
+
//#region src/withCloudFrontSignedUrl.ts
|
|
1602
|
+
const ONE_YEAR_IN_SECONDS = 3600 * 24 * 365;
|
|
1603
|
+
const privateKeyCache = /* @__PURE__ */ new Map();
|
|
1604
|
+
const getPrivateKeyFromSsm = async (region, parameterName) => {
|
|
1605
|
+
if (!region) throw new Error(`Invalid AWS region format: ${region}. Expected format like 'us-east-1' or 'ap-southeast-1'`);
|
|
1606
|
+
const parameter = (await new _aws_sdk_client_ssm.SSM(applySsmRuntimeAwsConfig({ region })).getParameter({
|
|
1607
|
+
Name: parameterName,
|
|
1608
|
+
WithDecryption: true
|
|
1609
|
+
})).Parameter;
|
|
1610
|
+
if (!parameter) throw new Error(`Failed to retrieve private key from SSM parameter: ${parameterName}`);
|
|
1611
|
+
const parameterValue = parameter.Value;
|
|
1612
|
+
if (!parameterValue) throw new Error(`Failed to retrieve private key from SSM parameter: ${parameterName}`);
|
|
1613
|
+
let keyPair;
|
|
1614
|
+
try {
|
|
1615
|
+
keyPair = JSON.parse(parameterValue);
|
|
1616
|
+
} catch (error) {
|
|
1617
|
+
throw new Error(`Invalid JSON format in SSM parameter: ${parameterName}. ${error instanceof Error ? error.message : String(error)}`);
|
|
1618
|
+
}
|
|
1619
|
+
const privateKey = keyPair.privateKey;
|
|
1620
|
+
if (!privateKey || typeof privateKey !== "string") throw new Error(`Invalid private key format in SSM parameter: ${parameterName}`);
|
|
1621
|
+
return privateKey;
|
|
1622
|
+
};
|
|
1623
|
+
const resolvePrivateKey = (config) => {
|
|
1624
|
+
if ("getPrivateKey" in config && typeof config.getPrivateKey === "function") return config.getPrivateKey();
|
|
1625
|
+
const cacheKey = `${config.ssmRegion}:${config.ssmParameterName}`;
|
|
1626
|
+
const cachedPrivateKey = privateKeyCache.get(cacheKey);
|
|
1627
|
+
if (cachedPrivateKey) return cachedPrivateKey;
|
|
1628
|
+
const privateKeyPromise = getPrivateKeyFromSsm(config.ssmRegion, config.ssmParameterName).catch((error) => {
|
|
1629
|
+
privateKeyCache.delete(cacheKey);
|
|
1630
|
+
throw error;
|
|
1631
|
+
});
|
|
1632
|
+
privateKeyCache.set(cacheKey, privateKeyPromise);
|
|
1633
|
+
return privateKeyPromise;
|
|
1634
|
+
};
|
|
1635
|
+
const resolvePublicBaseUrl = async (config, context) => {
|
|
1636
|
+
const publicBaseUrl = typeof config.publicBaseUrl === "function" ? await config.publicBaseUrl(context) : config.publicBaseUrl;
|
|
1637
|
+
if (!publicBaseUrl) throw new Error("CloudFront publicBaseUrl resolver returned an empty URL");
|
|
1638
|
+
return publicBaseUrl;
|
|
1639
|
+
};
|
|
1640
|
+
const withCloudFrontSignedUrl = (storageFactory, config) => {
|
|
1641
|
+
return () => {
|
|
1642
|
+
const baseStorage = storageFactory();
|
|
1643
|
+
return {
|
|
1644
|
+
...baseStorage,
|
|
1645
|
+
name: `${baseStorage.name}WithCloudFrontSignedUrl`,
|
|
1646
|
+
async getDownloadUrl(storageUri, context) {
|
|
1647
|
+
const storageUrl = new URL(storageUri);
|
|
1648
|
+
if (storageUrl.protocol !== "s3:") return baseStorage.getDownloadUrl(storageUri, context);
|
|
1649
|
+
const [privateKey, publicBaseUrl] = await Promise.all([resolvePrivateKey(config), resolvePublicBaseUrl(config, context)]);
|
|
1650
|
+
const url = new URL(publicBaseUrl);
|
|
1651
|
+
url.pathname = storageUrl.pathname;
|
|
1652
|
+
url.search = "";
|
|
1653
|
+
return { fileUrl: (0, _aws_sdk_cloudfront_signer.getSignedUrl)({
|
|
1654
|
+
url: url.toString(),
|
|
1655
|
+
keyPairId: config.keyPairId,
|
|
1656
|
+
privateKey,
|
|
1657
|
+
dateLessThan: new Date(Date.now() + (config.expiresSeconds ?? ONE_YEAR_IN_SECONDS) * 1e3).toISOString()
|
|
1658
|
+
}) };
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
};
|
|
1662
|
+
};
|
|
1663
|
+
//#endregion
|
|
1664
|
+
//#region src/s3LambdaEdgeStorage.ts
|
|
1665
|
+
const awsLambdaEdgeStorage = (config, hooks) => {
|
|
1666
|
+
return withCloudFrontSignedUrl(s3Storage(config, hooks), config);
|
|
1667
|
+
};
|
|
1668
|
+
const s3LambdaEdgeStorage = awsLambdaEdgeStorage;
|
|
1669
|
+
//#endregion
|
|
1670
|
+
exports.awsLambdaEdgeStorage = awsLambdaEdgeStorage;
|
|
1553
1671
|
exports.s3Database = s3Database;
|
|
1554
|
-
exports.
|
|
1672
|
+
exports.s3LambdaEdgeStorage = s3LambdaEdgeStorage;
|
|
1673
|
+
exports.s3Storage = s3Storage;
|
|
1674
|
+
exports.withCloudFrontSignedUrl = withCloudFrontSignedUrl;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
|
+
import { StoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
|
|
2
3
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
3
4
|
|
|
4
5
|
//#region src/s3Database.d.ts
|
|
@@ -12,9 +13,16 @@ interface S3DatabaseConfig extends S3ClientConfig {
|
|
|
12
13
|
* where CloudFront is not available.
|
|
13
14
|
*/
|
|
14
15
|
cloudfrontDistributionId?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Wait for CloudFront invalidations to reach `Completed` before finishing.
|
|
18
|
+
*
|
|
19
|
+
* When enabled, invalidation failures and timeouts are surfaced to callers
|
|
20
|
+
* instead of being logged and ignored.
|
|
21
|
+
*/
|
|
22
|
+
shouldWaitForInvalidation?: boolean;
|
|
15
23
|
apiBasePath?: string;
|
|
16
24
|
}
|
|
17
|
-
declare const s3Database: (config: S3DatabaseConfig, hooks?:
|
|
25
|
+
declare const s3Database: (config: S3DatabaseConfig, hooks?: _$_hot_updater_plugin_core0.DatabasePluginHooks) => () => _$_hot_updater_plugin_core0.DatabasePlugin<unknown>;
|
|
18
26
|
//#endregion
|
|
19
27
|
//#region src/s3Storage.d.ts
|
|
20
28
|
interface S3StorageConfig extends S3ClientConfig {
|
|
@@ -24,6 +32,31 @@ interface S3StorageConfig extends S3ClientConfig {
|
|
|
24
32
|
*/
|
|
25
33
|
basePath?: string;
|
|
26
34
|
}
|
|
27
|
-
declare const s3Storage: (config: S3StorageConfig, hooks?:
|
|
35
|
+
declare const s3Storage: (config: S3StorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.StoragePlugin<unknown>;
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/withCloudFrontSignedUrl.d.ts
|
|
38
|
+
interface CloudFrontPrivateKeyFromGetter {
|
|
39
|
+
getPrivateKey: () => Promise<string>;
|
|
40
|
+
ssmParameterName?: never;
|
|
41
|
+
ssmRegion?: never;
|
|
42
|
+
}
|
|
43
|
+
interface CloudFrontPrivateKeyFromSsm {
|
|
44
|
+
getPrivateKey?: never;
|
|
45
|
+
ssmParameterName: string;
|
|
46
|
+
ssmRegion: string;
|
|
47
|
+
}
|
|
48
|
+
type PublicBaseUrlResolver<TContext = unknown> = (context?: StorageResolveContext<TContext>) => string | Promise<string>;
|
|
49
|
+
type CloudFrontSignedUrlConfig = CloudFrontPrivateKeyFromGetter | CloudFrontPrivateKeyFromSsm;
|
|
50
|
+
type WithCloudFrontSignedUrlOptions<TContext = unknown> = CloudFrontSignedUrlConfig & {
|
|
51
|
+
keyPairId: string;
|
|
52
|
+
publicBaseUrl: string | PublicBaseUrlResolver<TContext>;
|
|
53
|
+
expiresSeconds?: number;
|
|
54
|
+
};
|
|
55
|
+
declare const withCloudFrontSignedUrl: <TContext = unknown>(storageFactory: () => StoragePlugin<TContext>, config: WithCloudFrontSignedUrlOptions<TContext>) => () => StoragePlugin<TContext>;
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/s3LambdaEdgeStorage.d.ts
|
|
58
|
+
type AwsLambdaEdgeStorageConfig = S3StorageConfig & WithCloudFrontSignedUrlOptions;
|
|
59
|
+
declare const awsLambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.StoragePlugin<unknown>;
|
|
60
|
+
declare const s3LambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.StoragePlugin<unknown>;
|
|
28
61
|
//#endregion
|
|
29
|
-
export { S3DatabaseConfig, S3StorageConfig, s3Database, s3Storage };
|
|
62
|
+
export { AwsLambdaEdgeStorageConfig, CloudFrontSignedUrlConfig, PublicBaseUrlResolver, S3DatabaseConfig, S3StorageConfig, WithCloudFrontSignedUrlOptions, awsLambdaEdgeStorage, s3Database, s3LambdaEdgeStorage, s3Storage, withCloudFrontSignedUrl };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
2
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
3
|
+
import { StoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
|
|
4
|
+
|
|
5
|
+
//#region src/s3Database.d.ts
|
|
6
|
+
interface S3DatabaseConfig extends S3ClientConfig {
|
|
7
|
+
bucketName: string;
|
|
8
|
+
/**
|
|
9
|
+
* CloudFront distribution ID used for cache invalidation.
|
|
10
|
+
*
|
|
11
|
+
* If omitted or an empty string, CloudFront invalidation is skipped.
|
|
12
|
+
* This is useful for local development environments (e.g. Localstack)
|
|
13
|
+
* where CloudFront is not available.
|
|
14
|
+
*/
|
|
15
|
+
cloudfrontDistributionId?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Wait for CloudFront invalidations to reach `Completed` before finishing.
|
|
18
|
+
*
|
|
19
|
+
* When enabled, invalidation failures and timeouts are surfaced to callers
|
|
20
|
+
* instead of being logged and ignored.
|
|
21
|
+
*/
|
|
22
|
+
shouldWaitForInvalidation?: boolean;
|
|
23
|
+
apiBasePath?: string;
|
|
24
|
+
}
|
|
25
|
+
declare const s3Database: (config: S3DatabaseConfig, hooks?: _$_hot_updater_plugin_core0.DatabasePluginHooks) => () => _$_hot_updater_plugin_core0.DatabasePlugin<unknown>;
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/s3Storage.d.ts
|
|
28
|
+
interface S3StorageConfig extends S3ClientConfig {
|
|
29
|
+
bucketName: string;
|
|
30
|
+
/**
|
|
31
|
+
* Base path where bundles will be stored in the bucket
|
|
32
|
+
*/
|
|
33
|
+
basePath?: string;
|
|
34
|
+
}
|
|
35
|
+
declare const s3Storage: (config: S3StorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.StoragePlugin<unknown>;
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/withCloudFrontSignedUrl.d.ts
|
|
38
|
+
interface CloudFrontPrivateKeyFromGetter {
|
|
39
|
+
getPrivateKey: () => Promise<string>;
|
|
40
|
+
ssmParameterName?: never;
|
|
41
|
+
ssmRegion?: never;
|
|
42
|
+
}
|
|
43
|
+
interface CloudFrontPrivateKeyFromSsm {
|
|
44
|
+
getPrivateKey?: never;
|
|
45
|
+
ssmParameterName: string;
|
|
46
|
+
ssmRegion: string;
|
|
47
|
+
}
|
|
48
|
+
type PublicBaseUrlResolver<TContext = unknown> = (context?: StorageResolveContext<TContext>) => string | Promise<string>;
|
|
49
|
+
type CloudFrontSignedUrlConfig = CloudFrontPrivateKeyFromGetter | CloudFrontPrivateKeyFromSsm;
|
|
50
|
+
type WithCloudFrontSignedUrlOptions<TContext = unknown> = CloudFrontSignedUrlConfig & {
|
|
51
|
+
keyPairId: string;
|
|
52
|
+
publicBaseUrl: string | PublicBaseUrlResolver<TContext>;
|
|
53
|
+
expiresSeconds?: number;
|
|
54
|
+
};
|
|
55
|
+
declare const withCloudFrontSignedUrl: <TContext = unknown>(storageFactory: () => StoragePlugin<TContext>, config: WithCloudFrontSignedUrlOptions<TContext>) => () => StoragePlugin<TContext>;
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/s3LambdaEdgeStorage.d.ts
|
|
58
|
+
type AwsLambdaEdgeStorageConfig = S3StorageConfig & WithCloudFrontSignedUrlOptions;
|
|
59
|
+
declare const awsLambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.StoragePlugin<unknown>;
|
|
60
|
+
declare const s3LambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.StoragePlugin<unknown>;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { AwsLambdaEdgeStorageConfig, CloudFrontSignedUrlConfig, PublicBaseUrlResolver, S3DatabaseConfig, S3StorageConfig, WithCloudFrontSignedUrlOptions, awsLambdaEdgeStorage, s3Database, s3LambdaEdgeStorage, s3Storage, withCloudFrontSignedUrl };
|