@hot-updater/aws 0.31.4 → 0.33.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 +7 -7
- package/dist/iac/index.mjs +1 -1
- package/dist/index.cjs +59 -7
- package/dist/index.d.cts +10 -5
- package/dist/index.d.mts +9 -4
- package/dist/index.mjs +58 -6
- package/dist/lambda/index.cjs +487 -192
- package/package.json +8 -8
package/dist/iac/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __commonJSMin = (cb, mod) => () => (mod ||
|
|
9
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
10
10
|
var __copyProps = (to, from, except, desc) => {
|
|
11
11
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
12
|
key = keys[i];
|
|
@@ -29,13 +29,13 @@ let node_child_process = require("node:child_process");
|
|
|
29
29
|
let node_string_decoder = require("node:string_decoder");
|
|
30
30
|
let node_util = require("node:util");
|
|
31
31
|
let node_process = require("node:process");
|
|
32
|
-
node_process = __toESM(node_process
|
|
32
|
+
node_process = __toESM(node_process);
|
|
33
33
|
let node_tty = require("node:tty");
|
|
34
|
-
node_tty = __toESM(node_tty
|
|
34
|
+
node_tty = __toESM(node_tty);
|
|
35
35
|
let node_path = require("node:path");
|
|
36
|
-
node_path = __toESM(node_path
|
|
36
|
+
node_path = __toESM(node_path);
|
|
37
37
|
let path = require("path");
|
|
38
|
-
path = __toESM(path
|
|
38
|
+
path = __toESM(path);
|
|
39
39
|
let node_timers_promises = require("node:timers/promises");
|
|
40
40
|
let node_os = require("node:os");
|
|
41
41
|
let node_events = require("node:events");
|
|
@@ -45,12 +45,12 @@ let node_stream_promises = require("node:stream/promises");
|
|
|
45
45
|
let node_stream = require("node:stream");
|
|
46
46
|
let node_buffer = require("node:buffer");
|
|
47
47
|
let crypto = require("crypto");
|
|
48
|
-
crypto = __toESM(crypto
|
|
48
|
+
crypto = __toESM(crypto);
|
|
49
49
|
let _aws_sdk_client_cloudfront = require("@aws-sdk/client-cloudfront");
|
|
50
50
|
let _aws_sdk_client_iam = require("@aws-sdk/client-iam");
|
|
51
51
|
let _aws_sdk_client_sts = require("@aws-sdk/client-sts");
|
|
52
52
|
let fs_promises = require("fs/promises");
|
|
53
|
-
fs_promises = __toESM(fs_promises
|
|
53
|
+
fs_promises = __toESM(fs_promises);
|
|
54
54
|
let _aws_sdk_client_lambda = require("@aws-sdk/client-lambda");
|
|
55
55
|
let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
56
56
|
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
package/dist/iac/index.mjs
CHANGED
|
@@ -33,7 +33,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
33
33
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
34
34
|
var __getProtoOf = Object.getPrototypeOf;
|
|
35
35
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
36
|
-
var __commonJSMin = (cb, mod) => () => (mod ||
|
|
36
|
+
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
37
37
|
var __copyProps = (to, from, except, desc) => {
|
|
38
38
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
39
39
|
key = keys[i];
|
package/dist/index.cjs
CHANGED
|
@@ -26,9 +26,9 @@ let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
|
26
26
|
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
|
27
27
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
28
28
|
let fs_promises = require("fs/promises");
|
|
29
|
-
fs_promises = __toESM(fs_promises
|
|
29
|
+
fs_promises = __toESM(fs_promises);
|
|
30
30
|
let path = require("path");
|
|
31
|
-
path = __toESM(path
|
|
31
|
+
path = __toESM(path);
|
|
32
32
|
let _aws_sdk_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
33
33
|
let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
|
|
34
34
|
let _aws_sdk_cloudfront_signer = require("@aws-sdk/cloudfront-signer");
|
|
@@ -1412,6 +1412,37 @@ const streamToString = (stream) => {
|
|
|
1412
1412
|
const DEFAULT_INVALIDATION_POLL_INTERVAL_MS = 2e3;
|
|
1413
1413
|
const DEFAULT_INVALIDATION_TIMEOUT_MS = 300 * 1e3;
|
|
1414
1414
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1415
|
+
const getS3ErrorProperty = (error, key) => {
|
|
1416
|
+
if (typeof error !== "object" || error === null) return;
|
|
1417
|
+
const value = error[key];
|
|
1418
|
+
return typeof value === "string" ? value : void 0;
|
|
1419
|
+
};
|
|
1420
|
+
const isArchivedS3ObjectError = (error) => {
|
|
1421
|
+
if (!(error instanceof Error)) return false;
|
|
1422
|
+
return error.name === "InvalidObjectState" || getS3ErrorProperty(error, "Code") === "InvalidObjectState";
|
|
1423
|
+
};
|
|
1424
|
+
const createArchivedS3ObjectError = ({ bucket, key, error }) => {
|
|
1425
|
+
const storageClass = getS3ErrorProperty(error, "StorageClass") ?? "archived storage";
|
|
1426
|
+
const nextError = new Error(`S3 object "${key}" in bucket "${bucket}" is archived (${storageClass}) and cannot be read. Restore the object in S3 or exclude Hot Updater metadata from lifecycle archival: "_index/**", "**/target-app-versions.json", and "**/update.json".`, { cause: error });
|
|
1427
|
+
nextError.name = "S3ArchivedObjectError";
|
|
1428
|
+
return nextError;
|
|
1429
|
+
};
|
|
1430
|
+
function normalizeBasePath(basePath) {
|
|
1431
|
+
return basePath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
1432
|
+
}
|
|
1433
|
+
function createDatabaseKeyBuilder(basePath) {
|
|
1434
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
1435
|
+
const toStorageKey = (key) => [normalizedBasePath, key].filter(Boolean).join("/");
|
|
1436
|
+
const fromStorageKey = (key) => {
|
|
1437
|
+
if (!normalizedBasePath) return key;
|
|
1438
|
+
const prefix = `${normalizedBasePath}/`;
|
|
1439
|
+
return key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
1440
|
+
};
|
|
1441
|
+
return {
|
|
1442
|
+
fromStorageKey,
|
|
1443
|
+
toStorageKey
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1415
1446
|
/**
|
|
1416
1447
|
* Loads JSON data from S3.
|
|
1417
1448
|
* Returns null if NoSuchKey error occurs.
|
|
@@ -1427,6 +1458,11 @@ async function loadJsonFromS3(client, bucket, key) {
|
|
|
1427
1458
|
return JSON.parse(bodyContents);
|
|
1428
1459
|
} catch (e) {
|
|
1429
1460
|
if (e instanceof _aws_sdk_client_s3.NoSuchKey) return null;
|
|
1461
|
+
if (isArchivedS3ObjectError(e)) throw createArchivedS3ObjectError({
|
|
1462
|
+
bucket,
|
|
1463
|
+
key,
|
|
1464
|
+
error: e
|
|
1465
|
+
});
|
|
1430
1466
|
throw e;
|
|
1431
1467
|
}
|
|
1432
1468
|
}
|
|
@@ -1510,18 +1546,20 @@ async function invalidateCloudFront(client, distributionId, paths, options) {
|
|
|
1510
1546
|
const s3Database = (0, _hot_updater_plugin_core.createBlobDatabasePlugin)({
|
|
1511
1547
|
name: "s3Database",
|
|
1512
1548
|
factory: (config) => {
|
|
1513
|
-
const { bucketName, cloudfrontDistributionId, apiBasePath = "/api/check-update", shouldWaitForInvalidation = false, ...s3Config } = config;
|
|
1549
|
+
const { basePath, bucketName, cloudfrontDistributionId, apiBasePath = "/api/check-update", shouldWaitForInvalidation = false, ...s3Config } = config;
|
|
1514
1550
|
const client = new _aws_sdk_client_s3.S3Client(applyS3RuntimeAwsConfig(s3Config));
|
|
1551
|
+
const { fromStorageKey, toStorageKey } = createDatabaseKeyBuilder(basePath);
|
|
1515
1552
|
const cloudfrontClient = cloudfrontDistributionId ? new _aws_sdk_client_cloudfront.CloudFrontClient({
|
|
1516
1553
|
credentials: s3Config.credentials,
|
|
1517
1554
|
region: s3Config.region
|
|
1518
1555
|
}) : void 0;
|
|
1519
1556
|
return {
|
|
1520
1557
|
apiBasePath,
|
|
1521
|
-
listObjects: (prefix) => listObjectsInS3(client, bucketName, prefix),
|
|
1522
|
-
loadObject: (key) => loadJsonFromS3(client, bucketName, key),
|
|
1523
|
-
uploadObject: (key, data) => uploadJsonToS3(client, bucketName, key, data),
|
|
1524
|
-
deleteObject: (key) => deleteObjectInS3(client, bucketName, key),
|
|
1558
|
+
listObjects: (prefix) => listObjectsInS3(client, bucketName, toStorageKey(prefix)).then((keys) => keys.map(fromStorageKey)),
|
|
1559
|
+
loadObject: (key) => loadJsonFromS3(client, bucketName, toStorageKey(key)),
|
|
1560
|
+
uploadObject: (key, data) => uploadJsonToS3(client, bucketName, toStorageKey(key), data),
|
|
1561
|
+
deleteObject: (key) => deleteObjectInS3(client, bucketName, toStorageKey(key)),
|
|
1562
|
+
shouldSkipLoadObjectError: (error) => error instanceof Error && error.name === "S3ArchivedObjectError",
|
|
1525
1563
|
invalidatePaths: (pathsToInvalidate) => {
|
|
1526
1564
|
if (cloudfrontClient && cloudfrontDistributionId && pathsToInvalidate.length > 0) return invalidateCloudFront(cloudfrontClient, cloudfrontDistributionId, pathsToInvalidate, { shouldWaitForInvalidation });
|
|
1527
1565
|
return Promise.resolve();
|
|
@@ -1578,6 +1616,20 @@ const s3Storage = (0, _hot_updater_plugin_core.createUniversalStoragePlugin)({
|
|
|
1578
1616
|
if (!response.Bucket || !response.Key) throw new Error("Upload Failed");
|
|
1579
1617
|
return { storageUri: `s3://${bucketName}/${Key}` };
|
|
1580
1618
|
},
|
|
1619
|
+
async exists(storageUri) {
|
|
1620
|
+
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "s3");
|
|
1621
|
+
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
1622
|
+
try {
|
|
1623
|
+
await client.send(new _aws_sdk_client_s3.HeadObjectCommand({
|
|
1624
|
+
Bucket: bucketName,
|
|
1625
|
+
Key: key
|
|
1626
|
+
}));
|
|
1627
|
+
return true;
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
if (error instanceof Error && (error.name === "NotFound" || error.name === "NoSuchKey")) return false;
|
|
1630
|
+
throw error;
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1581
1633
|
async downloadFile(storageUri, filePath) {
|
|
1582
1634
|
const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "s3");
|
|
1583
1635
|
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
2
|
import { BlobDatabasePluginConfig, RuntimeStoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
|
|
3
|
+
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
3
4
|
|
|
4
5
|
//#region src/s3Database.d.ts
|
|
5
6
|
interface S3DatabaseConfig extends S3ClientConfig, BlobDatabasePluginConfig {
|
|
6
7
|
bucketName: string;
|
|
8
|
+
/**
|
|
9
|
+
* Base path where database objects will be stored in the bucket.
|
|
10
|
+
*/
|
|
11
|
+
basePath?: string;
|
|
7
12
|
/**
|
|
8
13
|
* CloudFront distribution ID used for cache invalidation.
|
|
9
14
|
*
|
|
@@ -21,7 +26,7 @@ interface S3DatabaseConfig extends S3ClientConfig, BlobDatabasePluginConfig {
|
|
|
21
26
|
shouldWaitForInvalidation?: boolean;
|
|
22
27
|
apiBasePath?: string;
|
|
23
28
|
}
|
|
24
|
-
declare const s3Database: (config: S3DatabaseConfig, hooks?:
|
|
29
|
+
declare const s3Database: (config: S3DatabaseConfig, hooks?: _$_hot_updater_plugin_core0.DatabasePluginHooks) => () => _$_hot_updater_plugin_core0.DatabasePlugin<unknown>;
|
|
25
30
|
//#endregion
|
|
26
31
|
//#region src/s3Storage.d.ts
|
|
27
32
|
interface S3StorageConfig extends S3ClientConfig {
|
|
@@ -31,7 +36,7 @@ interface S3StorageConfig extends S3ClientConfig {
|
|
|
31
36
|
*/
|
|
32
37
|
basePath?: string;
|
|
33
38
|
}
|
|
34
|
-
declare const s3Storage: (config: S3StorageConfig, hooks?:
|
|
39
|
+
declare const s3Storage: (config: S3StorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
35
40
|
//#endregion
|
|
36
41
|
//#region src/withCloudFrontSignedUrl.d.ts
|
|
37
42
|
interface CloudFrontPrivateKeyFromGetter {
|
|
@@ -55,7 +60,7 @@ declare const withCloudFrontSignedUrl: <TContext = unknown, TStorage extends Run
|
|
|
55
60
|
//#endregion
|
|
56
61
|
//#region src/s3LambdaEdgeStorage.d.ts
|
|
57
62
|
type AwsLambdaEdgeStorageConfig = S3StorageConfig & WithCloudFrontSignedUrlOptions;
|
|
58
|
-
declare const awsLambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () =>
|
|
59
|
-
declare const s3LambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () =>
|
|
63
|
+
declare const awsLambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
64
|
+
declare const s3LambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
60
65
|
//#endregion
|
|
61
66
|
export { AwsLambdaEdgeStorageConfig, CloudFrontSignedUrlConfig, PublicBaseUrlResolver, S3DatabaseConfig, S3StorageConfig, WithCloudFrontSignedUrlOptions, awsLambdaEdgeStorage, s3Database, s3LambdaEdgeStorage, s3Storage, withCloudFrontSignedUrl };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { S3ClientConfig } from "@aws-sdk/client-s3";
|
|
2
|
+
import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
|
|
2
3
|
import { BlobDatabasePluginConfig, RuntimeStoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
|
|
3
4
|
|
|
4
5
|
//#region src/s3Database.d.ts
|
|
5
6
|
interface S3DatabaseConfig extends S3ClientConfig, BlobDatabasePluginConfig {
|
|
6
7
|
bucketName: string;
|
|
8
|
+
/**
|
|
9
|
+
* Base path where database objects will be stored in the bucket.
|
|
10
|
+
*/
|
|
11
|
+
basePath?: string;
|
|
7
12
|
/**
|
|
8
13
|
* CloudFront distribution ID used for cache invalidation.
|
|
9
14
|
*
|
|
@@ -21,7 +26,7 @@ interface S3DatabaseConfig extends S3ClientConfig, BlobDatabasePluginConfig {
|
|
|
21
26
|
shouldWaitForInvalidation?: boolean;
|
|
22
27
|
apiBasePath?: string;
|
|
23
28
|
}
|
|
24
|
-
declare const s3Database: (config: S3DatabaseConfig, hooks?:
|
|
29
|
+
declare const s3Database: (config: S3DatabaseConfig, hooks?: _$_hot_updater_plugin_core0.DatabasePluginHooks) => () => _$_hot_updater_plugin_core0.DatabasePlugin<unknown>;
|
|
25
30
|
//#endregion
|
|
26
31
|
//#region src/s3Storage.d.ts
|
|
27
32
|
interface S3StorageConfig extends S3ClientConfig {
|
|
@@ -31,7 +36,7 @@ interface S3StorageConfig extends S3ClientConfig {
|
|
|
31
36
|
*/
|
|
32
37
|
basePath?: string;
|
|
33
38
|
}
|
|
34
|
-
declare const s3Storage: (config: S3StorageConfig, hooks?:
|
|
39
|
+
declare const s3Storage: (config: S3StorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
35
40
|
//#endregion
|
|
36
41
|
//#region src/withCloudFrontSignedUrl.d.ts
|
|
37
42
|
interface CloudFrontPrivateKeyFromGetter {
|
|
@@ -55,7 +60,7 @@ declare const withCloudFrontSignedUrl: <TContext = unknown, TStorage extends Run
|
|
|
55
60
|
//#endregion
|
|
56
61
|
//#region src/s3LambdaEdgeStorage.d.ts
|
|
57
62
|
type AwsLambdaEdgeStorageConfig = S3StorageConfig & WithCloudFrontSignedUrlOptions;
|
|
58
|
-
declare const awsLambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () =>
|
|
59
|
-
declare const s3LambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () =>
|
|
63
|
+
declare const awsLambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
64
|
+
declare const s3LambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
|
|
60
65
|
//#endregion
|
|
61
66
|
export { AwsLambdaEdgeStorageConfig, CloudFrontSignedUrlConfig, PublicBaseUrlResolver, S3DatabaseConfig, S3StorageConfig, WithCloudFrontSignedUrlOptions, awsLambdaEdgeStorage, s3Database, s3LambdaEdgeStorage, s3Storage, withCloudFrontSignedUrl };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CloudFrontClient, CreateInvalidationCommand, GetInvalidationCommand } from "@aws-sdk/client-cloudfront";
|
|
2
|
-
import { DeleteObjectCommand, DeleteObjectsCommand, GetObjectCommand, ListObjectsV2Command, NoSuchKey, S3Client } from "@aws-sdk/client-s3";
|
|
2
|
+
import { DeleteObjectCommand, DeleteObjectsCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, NoSuchKey, S3Client } from "@aws-sdk/client-s3";
|
|
3
3
|
import { Upload } from "@aws-sdk/lib-storage";
|
|
4
4
|
import { createBlobDatabasePlugin, createStorageKeyBuilder, createUniversalStoragePlugin, getContentType, parseStorageUri } from "@hot-updater/plugin-core";
|
|
5
5
|
import fs from "fs/promises";
|
|
@@ -1387,6 +1387,37 @@ const streamToString = (stream) => {
|
|
|
1387
1387
|
const DEFAULT_INVALIDATION_POLL_INTERVAL_MS = 2e3;
|
|
1388
1388
|
const DEFAULT_INVALIDATION_TIMEOUT_MS = 300 * 1e3;
|
|
1389
1389
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1390
|
+
const getS3ErrorProperty = (error, key) => {
|
|
1391
|
+
if (typeof error !== "object" || error === null) return;
|
|
1392
|
+
const value = error[key];
|
|
1393
|
+
return typeof value === "string" ? value : void 0;
|
|
1394
|
+
};
|
|
1395
|
+
const isArchivedS3ObjectError = (error) => {
|
|
1396
|
+
if (!(error instanceof Error)) return false;
|
|
1397
|
+
return error.name === "InvalidObjectState" || getS3ErrorProperty(error, "Code") === "InvalidObjectState";
|
|
1398
|
+
};
|
|
1399
|
+
const createArchivedS3ObjectError = ({ bucket, key, error }) => {
|
|
1400
|
+
const storageClass = getS3ErrorProperty(error, "StorageClass") ?? "archived storage";
|
|
1401
|
+
const nextError = new Error(`S3 object "${key}" in bucket "${bucket}" is archived (${storageClass}) and cannot be read. Restore the object in S3 or exclude Hot Updater metadata from lifecycle archival: "_index/**", "**/target-app-versions.json", and "**/update.json".`, { cause: error });
|
|
1402
|
+
nextError.name = "S3ArchivedObjectError";
|
|
1403
|
+
return nextError;
|
|
1404
|
+
};
|
|
1405
|
+
function normalizeBasePath(basePath) {
|
|
1406
|
+
return basePath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
1407
|
+
}
|
|
1408
|
+
function createDatabaseKeyBuilder(basePath) {
|
|
1409
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
1410
|
+
const toStorageKey = (key) => [normalizedBasePath, key].filter(Boolean).join("/");
|
|
1411
|
+
const fromStorageKey = (key) => {
|
|
1412
|
+
if (!normalizedBasePath) return key;
|
|
1413
|
+
const prefix = `${normalizedBasePath}/`;
|
|
1414
|
+
return key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
1415
|
+
};
|
|
1416
|
+
return {
|
|
1417
|
+
fromStorageKey,
|
|
1418
|
+
toStorageKey
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1390
1421
|
/**
|
|
1391
1422
|
* Loads JSON data from S3.
|
|
1392
1423
|
* Returns null if NoSuchKey error occurs.
|
|
@@ -1402,6 +1433,11 @@ async function loadJsonFromS3(client, bucket, key) {
|
|
|
1402
1433
|
return JSON.parse(bodyContents);
|
|
1403
1434
|
} catch (e) {
|
|
1404
1435
|
if (e instanceof NoSuchKey) return null;
|
|
1436
|
+
if (isArchivedS3ObjectError(e)) throw createArchivedS3ObjectError({
|
|
1437
|
+
bucket,
|
|
1438
|
+
key,
|
|
1439
|
+
error: e
|
|
1440
|
+
});
|
|
1405
1441
|
throw e;
|
|
1406
1442
|
}
|
|
1407
1443
|
}
|
|
@@ -1485,18 +1521,20 @@ async function invalidateCloudFront(client, distributionId, paths, options) {
|
|
|
1485
1521
|
const s3Database = createBlobDatabasePlugin({
|
|
1486
1522
|
name: "s3Database",
|
|
1487
1523
|
factory: (config) => {
|
|
1488
|
-
const { bucketName, cloudfrontDistributionId, apiBasePath = "/api/check-update", shouldWaitForInvalidation = false, ...s3Config } = config;
|
|
1524
|
+
const { basePath, bucketName, cloudfrontDistributionId, apiBasePath = "/api/check-update", shouldWaitForInvalidation = false, ...s3Config } = config;
|
|
1489
1525
|
const client = new S3Client(applyS3RuntimeAwsConfig(s3Config));
|
|
1526
|
+
const { fromStorageKey, toStorageKey } = createDatabaseKeyBuilder(basePath);
|
|
1490
1527
|
const cloudfrontClient = cloudfrontDistributionId ? new CloudFrontClient({
|
|
1491
1528
|
credentials: s3Config.credentials,
|
|
1492
1529
|
region: s3Config.region
|
|
1493
1530
|
}) : void 0;
|
|
1494
1531
|
return {
|
|
1495
1532
|
apiBasePath,
|
|
1496
|
-
listObjects: (prefix) => listObjectsInS3(client, bucketName, prefix),
|
|
1497
|
-
loadObject: (key) => loadJsonFromS3(client, bucketName, key),
|
|
1498
|
-
uploadObject: (key, data) => uploadJsonToS3(client, bucketName, key, data),
|
|
1499
|
-
deleteObject: (key) => deleteObjectInS3(client, bucketName, key),
|
|
1533
|
+
listObjects: (prefix) => listObjectsInS3(client, bucketName, toStorageKey(prefix)).then((keys) => keys.map(fromStorageKey)),
|
|
1534
|
+
loadObject: (key) => loadJsonFromS3(client, bucketName, toStorageKey(key)),
|
|
1535
|
+
uploadObject: (key, data) => uploadJsonToS3(client, bucketName, toStorageKey(key), data),
|
|
1536
|
+
deleteObject: (key) => deleteObjectInS3(client, bucketName, toStorageKey(key)),
|
|
1537
|
+
shouldSkipLoadObjectError: (error) => error instanceof Error && error.name === "S3ArchivedObjectError",
|
|
1500
1538
|
invalidatePaths: (pathsToInvalidate) => {
|
|
1501
1539
|
if (cloudfrontClient && cloudfrontDistributionId && pathsToInvalidate.length > 0) return invalidateCloudFront(cloudfrontClient, cloudfrontDistributionId, pathsToInvalidate, { shouldWaitForInvalidation });
|
|
1502
1540
|
return Promise.resolve();
|
|
@@ -1553,6 +1591,20 @@ const s3Storage = createUniversalStoragePlugin({
|
|
|
1553
1591
|
if (!response.Bucket || !response.Key) throw new Error("Upload Failed");
|
|
1554
1592
|
return { storageUri: `s3://${bucketName}/${Key}` };
|
|
1555
1593
|
},
|
|
1594
|
+
async exists(storageUri) {
|
|
1595
|
+
const { bucket, key } = parseStorageUri(storageUri, "s3");
|
|
1596
|
+
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|
|
1597
|
+
try {
|
|
1598
|
+
await client.send(new HeadObjectCommand({
|
|
1599
|
+
Bucket: bucketName,
|
|
1600
|
+
Key: key
|
|
1601
|
+
}));
|
|
1602
|
+
return true;
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
if (error instanceof Error && (error.name === "NotFound" || error.name === "NoSuchKey")) return false;
|
|
1605
|
+
throw error;
|
|
1606
|
+
}
|
|
1607
|
+
},
|
|
1556
1608
|
async downloadFile(storageUri, filePath) {
|
|
1557
1609
|
const { bucket, key } = parseStorageUri(storageUri, "s3");
|
|
1558
1610
|
if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
|