@hot-updater/aws 1.2.0-rc.7 → 1.2.0-rc.9
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 +40 -28
- package/dist/init/index.mjs +42 -30
- package/dist/lambda/index.cjs +133 -62
- package/package.json +8 -7
package/dist/lambda/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ let _aws_sdk_client_cloudfront = require("@aws-sdk/client-cloudfront");
|
|
|
30
30
|
let _aws_sdk_lib_dynamodb = require("@aws-sdk/lib-dynamodb");
|
|
31
31
|
let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
32
32
|
let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
|
|
33
|
+
let _aws_sdk_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
33
34
|
const SHA256_FILE_HASH_RE = /^[0-9a-f]{64}$/;
|
|
34
35
|
const isContentAddressedAssetFileHash = (value) => {
|
|
35
36
|
return typeof value === "string" && SHA256_FILE_HASH_RE.test(value);
|
|
@@ -1437,6 +1438,10 @@ const semverSatisfies = (targetAppVersion, currentVersion) => {
|
|
|
1437
1438
|
return satisfies(currentCoerce, targetAppVersion);
|
|
1438
1439
|
};
|
|
1439
1440
|
//#endregion
|
|
1441
|
+
//#region ../plugin-core/dist/types/index.mjs
|
|
1442
|
+
/** The value of a `Lazy`, calling its function. */
|
|
1443
|
+
const resolveLazy = async (value) => typeof value === "function" ? await value() : value;
|
|
1444
|
+
//#endregion
|
|
1440
1445
|
//#region ../plugin-core/dist/serverPlugin/definePlugin.mjs
|
|
1441
1446
|
/** Declares a plugin; Hot Updater's own plugins and third-party ones use the same contract. */
|
|
1442
1447
|
const definePlugin = (plugin) => plugin;
|
|
@@ -4707,7 +4712,7 @@ const createKvAdapter = ({ store, tablePrefix = "" }) => {
|
|
|
4707
4712
|
};
|
|
4708
4713
|
//#endregion
|
|
4709
4714
|
//#region ../../packages/server/dist/version.mjs
|
|
4710
|
-
const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.
|
|
4715
|
+
const HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.9";
|
|
4711
4716
|
//#endregion
|
|
4712
4717
|
//#region ../../packages/server/dist/handlerVersionRoutes.mjs
|
|
4713
4718
|
/**
|
|
@@ -14295,10 +14300,30 @@ async apply() {
|
|
|
14295
14300
|
};
|
|
14296
14301
|
};
|
|
14297
14302
|
//#endregion
|
|
14303
|
+
//#region src/lazyCredentials.ts
|
|
14304
|
+
/**
|
|
14305
|
+
* `credentials` as the AWS SDK takes them: access keys with a lazy field
|
|
14306
|
+
* become a provider, which the SDK calls once, on its first request.
|
|
14307
|
+
*/
|
|
14308
|
+
const toSdkCredentials$1 = (credentials) => {
|
|
14309
|
+
if (credentials === void 0 || typeof credentials === "function") return credentials;
|
|
14310
|
+
const { accessKeyId, secretAccessKey, sessionToken, ...rest } = credentials;
|
|
14311
|
+
if (typeof accessKeyId !== "function" && typeof secretAccessKey !== "function" && typeof sessionToken !== "function") return credentials;
|
|
14312
|
+
return async () => ({
|
|
14313
|
+
...rest,
|
|
14314
|
+
accessKeyId: await resolveLazy(accessKeyId),
|
|
14315
|
+
secretAccessKey: await resolveLazy(secretAccessKey),
|
|
14316
|
+
...sessionToken === void 0 ? {} : { sessionToken: await resolveLazy(sessionToken) }
|
|
14317
|
+
});
|
|
14318
|
+
};
|
|
14319
|
+
//#endregion
|
|
14298
14320
|
//#region src/dynamoDB.ts
|
|
14299
14321
|
/** The DynamoDB client ignores the CloudFront and batching settings. */
|
|
14300
|
-
const adapterOf = ({ tableName, ...clientConfig }) => {
|
|
14301
|
-
const client = new _aws_sdk_client_dynamodb.DynamoDBClient(
|
|
14322
|
+
const adapterOf = ({ tableName, credentials, ...clientConfig }) => {
|
|
14323
|
+
const client = new _aws_sdk_client_dynamodb.DynamoDBClient({
|
|
14324
|
+
...clientConfig,
|
|
14325
|
+
credentials: toSdkCredentials$1(credentials)
|
|
14326
|
+
});
|
|
14302
14327
|
const store = createDynamoDBStore({
|
|
14303
14328
|
client,
|
|
14304
14329
|
tableName
|
|
@@ -14323,7 +14348,10 @@ const marked = (target) => ({ [DATABASE_TARGET]: target });
|
|
|
14323
14348
|
*/
|
|
14324
14349
|
const dynamoDB = (config) => {
|
|
14325
14350
|
const { client, adapter } = adapterOf(config);
|
|
14326
|
-
const cloudFront = createUpdateRouteInvalidation(
|
|
14351
|
+
const cloudFront = createUpdateRouteInvalidation({
|
|
14352
|
+
...config,
|
|
14353
|
+
credentials: toSdkCredentials$1(config.credentials)
|
|
14354
|
+
});
|
|
14327
14355
|
return {
|
|
14328
14356
|
...createEngineDatabase({
|
|
14329
14357
|
name: "dynamoDB",
|
|
@@ -14340,18 +14368,49 @@ const dynamoDB = (config) => {
|
|
|
14340
14368
|
};
|
|
14341
14369
|
};
|
|
14342
14370
|
//#endregion
|
|
14343
|
-
//#region
|
|
14371
|
+
//#region ../storage-s3/dist/index.mjs
|
|
14372
|
+
/**
|
|
14373
|
+
* `credentials` as the AWS SDK takes them: access keys with a lazy field
|
|
14374
|
+
* become a provider, which the SDK calls once, on its first request.
|
|
14375
|
+
*/
|
|
14376
|
+
const toSdkCredentials = (credentials) => {
|
|
14377
|
+
if (credentials === void 0 || typeof credentials === "function") return credentials;
|
|
14378
|
+
const { accessKeyId, secretAccessKey, sessionToken, ...rest } = credentials;
|
|
14379
|
+
if (typeof accessKeyId !== "function" && typeof secretAccessKey !== "function" && typeof sessionToken !== "function") return credentials;
|
|
14380
|
+
return async () => ({
|
|
14381
|
+
...rest,
|
|
14382
|
+
accessKeyId: await resolveLazy(accessKeyId),
|
|
14383
|
+
secretAccessKey: await resolveLazy(secretAccessKey),
|
|
14384
|
+
...sessionToken === void 0 ? {} : { sessionToken: await resolveLazy(sessionToken) }
|
|
14385
|
+
});
|
|
14386
|
+
};
|
|
14344
14387
|
/** Devices download right after the update check that returns the URL. */
|
|
14345
|
-
const
|
|
14346
|
-
|
|
14347
|
-
|
|
14348
|
-
|
|
14349
|
-
|
|
14350
|
-
|
|
14388
|
+
const DEFAULT_SIGNED_LIFETIME_SECONDS = 3600;
|
|
14389
|
+
/** SigV4's limit on a presigned URL. */
|
|
14390
|
+
const MAX_SIGNED_LIFETIME_SECONDS = 604800;
|
|
14391
|
+
/** DeleteObjects takes at most 1000 keys a request. */
|
|
14392
|
+
const DELETE_BATCH_SIZE = 1e3;
|
|
14393
|
+
const isObjectNotFoundError = (error) => {
|
|
14394
|
+
if (error instanceof Error && (error.name === "NotFound" || error.name === "NoSuchKey")) return true;
|
|
14395
|
+
return typeof error === "object" && error !== null && "$metadata" in error && error.$metadata?.httpStatusCode === 404;
|
|
14396
|
+
};
|
|
14397
|
+
const trimSlashes = (value) => value.replace(/^\/+|\/+$/g, "");
|
|
14398
|
+
/**
|
|
14399
|
+
* A storage adapter over a bucket's S3 API, which AWS S3, Cloudflare R2, and
|
|
14400
|
+
* Neon Object Storage all serve. Each provider package passes its own name,
|
|
14401
|
+
* URI protocol, and client defaults.
|
|
14402
|
+
*/
|
|
14403
|
+
const createS3Storage = ({ name, protocol }, config) => {
|
|
14404
|
+
const { bucketName, basePath, getDownloadUrl, signedUrlExpiresIn = DEFAULT_SIGNED_LIFETIME_SECONDS, credentials, ...clientConfig } = config;
|
|
14405
|
+
if (!Number.isFinite(signedUrlExpiresIn) || signedUrlExpiresIn <= 0 || signedUrlExpiresIn > MAX_SIGNED_LIFETIME_SECONDS) throw new Error(`${name} signedUrlExpiresIn must be between 1 and ${MAX_SIGNED_LIFETIME_SECONDS} seconds (seven days).`);
|
|
14406
|
+
const client = new _aws_sdk_client_s3.S3Client({
|
|
14407
|
+
...clientConfig,
|
|
14408
|
+
credentials: toSdkCredentials(credentials)
|
|
14409
|
+
});
|
|
14410
|
+
const normalizedBasePath = trimSlashes(basePath ?? "");
|
|
14351
14411
|
const getStorageKey = createStorageKeyBuilder(normalizedBasePath);
|
|
14352
14412
|
const getListPrefix = (prefix = "") => {
|
|
14353
|
-
const
|
|
14354
|
-
const value = [normalizedBasePath, normalizedPrefix].filter(Boolean).join("/");
|
|
14413
|
+
const value = [normalizedBasePath, trimSlashes(prefix)].filter(Boolean).join("/");
|
|
14355
14414
|
return value ? `${value}/` : "";
|
|
14356
14415
|
};
|
|
14357
14416
|
/**
|
|
@@ -14363,7 +14422,7 @@ function s3Storage(config) {
|
|
|
14363
14422
|
return createStorageUri({
|
|
14364
14423
|
bucket: bucketName,
|
|
14365
14424
|
key,
|
|
14366
|
-
protocol
|
|
14425
|
+
protocol
|
|
14367
14426
|
});
|
|
14368
14427
|
} catch {
|
|
14369
14428
|
return null;
|
|
@@ -14375,55 +14434,16 @@ function s3Storage(config) {
|
|
|
14375
14434
|
return key.startsWith(basePrefix) ? key.slice(basePrefix.length) : key;
|
|
14376
14435
|
};
|
|
14377
14436
|
const parseAndValidate = (storageUri) => {
|
|
14378
|
-
const parsed = parseStorageUri(storageUri,
|
|
14437
|
+
const parsed = parseStorageUri(storageUri, protocol);
|
|
14379
14438
|
if (parsed.bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${parsed.bucket}".`);
|
|
14380
14439
|
return parsed;
|
|
14381
14440
|
};
|
|
14382
14441
|
return createStorageAdapter({
|
|
14383
|
-
name
|
|
14384
|
-
protocol
|
|
14385
|
-
async listObjects(prefix) {
|
|
14386
|
-
const objects = [];
|
|
14387
|
-
let continuationToken;
|
|
14388
|
-
do {
|
|
14389
|
-
const response = await client.send(new _aws_sdk_client_s3.ListObjectsV2Command({
|
|
14390
|
-
Bucket: bucketName,
|
|
14391
|
-
ContinuationToken: continuationToken,
|
|
14392
|
-
Prefix: getListPrefix(prefix)
|
|
14393
|
-
}));
|
|
14394
|
-
for (const object of response.Contents ?? []) {
|
|
14395
|
-
if (!object.Key) continue;
|
|
14396
|
-
const storageUri = getListedStorageUri(object.Key);
|
|
14397
|
-
if (storageUri === null) continue;
|
|
14398
|
-
objects.push({
|
|
14399
|
-
key: getRelativeKey(object.Key),
|
|
14400
|
-
lastModifiedAt: object.LastModified,
|
|
14401
|
-
size: object.Size ?? 0,
|
|
14402
|
-
storageUri
|
|
14403
|
-
});
|
|
14404
|
-
}
|
|
14405
|
-
continuationToken = response.NextContinuationToken;
|
|
14406
|
-
} while (continuationToken);
|
|
14407
|
-
return objects;
|
|
14408
|
-
},
|
|
14409
|
-
async deleteObjects(relativeKeys) {
|
|
14410
|
-
const keys = relativeKeys.map((key) => getStorageKey(key));
|
|
14411
|
-
for (let offset = 0; offset < keys.length; offset += 1e3) {
|
|
14412
|
-
const batch = keys.slice(offset, offset + 1e3);
|
|
14413
|
-
if (batch.length === 0) continue;
|
|
14414
|
-
const response = await client.send(new _aws_sdk_client_s3.DeleteObjectsCommand({
|
|
14415
|
-
Bucket: bucketName,
|
|
14416
|
-
Delete: {
|
|
14417
|
-
Objects: batch.map((Key) => ({ Key })),
|
|
14418
|
-
Quiet: true
|
|
14419
|
-
}
|
|
14420
|
-
}));
|
|
14421
|
-
if (response.Errors && response.Errors.length > 0) throw new Error(`Failed to delete ${response.Errors.length} S3 object(s).`);
|
|
14422
|
-
}
|
|
14423
|
-
},
|
|
14442
|
+
name,
|
|
14443
|
+
protocol,
|
|
14424
14444
|
async put({ key, body, contentLength, contentType }) {
|
|
14425
14445
|
const storageKey = getStorageKey(key);
|
|
14426
|
-
|
|
14446
|
+
await new _aws_sdk_lib_storage.Upload({
|
|
14427
14447
|
client,
|
|
14428
14448
|
params: {
|
|
14429
14449
|
Body: body,
|
|
@@ -14434,11 +14454,10 @@ function s3Storage(config) {
|
|
|
14434
14454
|
Key: storageKey
|
|
14435
14455
|
}
|
|
14436
14456
|
}).done();
|
|
14437
|
-
if (!response.Bucket || !response.Key) throw new Error("Upload failed");
|
|
14438
14457
|
return { storageUri: createStorageUri({
|
|
14439
14458
|
bucket: bucketName,
|
|
14440
14459
|
key: storageKey,
|
|
14441
|
-
protocol
|
|
14460
|
+
protocol
|
|
14442
14461
|
}) };
|
|
14443
14462
|
},
|
|
14444
14463
|
async get({ storageUri }) {
|
|
@@ -14461,11 +14480,10 @@ function s3Storage(config) {
|
|
|
14461
14480
|
async getDownloadUrl({ storageUri }) {
|
|
14462
14481
|
const { key } = parseAndValidate(storageUri);
|
|
14463
14482
|
if (getDownloadUrl) return getDownloadUrl({ storageUri });
|
|
14464
|
-
|
|
14465
|
-
return { url: await getSignedUrl(client, new _aws_sdk_client_s3.GetObjectCommand({
|
|
14483
|
+
return { url: await (0, _aws_sdk_s3_request_presigner.getSignedUrl)(client, new _aws_sdk_client_s3.GetObjectCommand({
|
|
14466
14484
|
Bucket: bucketName,
|
|
14467
14485
|
Key: key
|
|
14468
|
-
}), { expiresIn:
|
|
14486
|
+
}), { expiresIn: signedUrlExpiresIn }) };
|
|
14469
14487
|
},
|
|
14470
14488
|
async exists({ storageUri }) {
|
|
14471
14489
|
const { key } = parseAndValidate(storageUri);
|
|
@@ -14487,8 +14505,61 @@ function s3Storage(config) {
|
|
|
14487
14505
|
Key: key
|
|
14488
14506
|
}));
|
|
14489
14507
|
return { deleted: true };
|
|
14508
|
+
},
|
|
14509
|
+
async listObjects(prefix) {
|
|
14510
|
+
const objects = [];
|
|
14511
|
+
let continuationToken;
|
|
14512
|
+
do {
|
|
14513
|
+
const response = await client.send(new _aws_sdk_client_s3.ListObjectsV2Command({
|
|
14514
|
+
Bucket: bucketName,
|
|
14515
|
+
ContinuationToken: continuationToken,
|
|
14516
|
+
Prefix: getListPrefix(prefix)
|
|
14517
|
+
}));
|
|
14518
|
+
for (const object of response.Contents ?? []) {
|
|
14519
|
+
if (!object.Key) continue;
|
|
14520
|
+
const storageUri = getListedStorageUri(object.Key);
|
|
14521
|
+
if (storageUri === null) continue;
|
|
14522
|
+
objects.push({
|
|
14523
|
+
key: getRelativeKey(object.Key),
|
|
14524
|
+
lastModifiedAt: object.LastModified,
|
|
14525
|
+
size: object.Size ?? 0,
|
|
14526
|
+
storageUri
|
|
14527
|
+
});
|
|
14528
|
+
}
|
|
14529
|
+
continuationToken = response.NextContinuationToken;
|
|
14530
|
+
} while (continuationToken);
|
|
14531
|
+
return objects;
|
|
14532
|
+
},
|
|
14533
|
+
async deleteObjects(relativeKeys) {
|
|
14534
|
+
const keys = relativeKeys.map((key) => getStorageKey(key));
|
|
14535
|
+
for (let offset = 0; offset < keys.length; offset += DELETE_BATCH_SIZE) {
|
|
14536
|
+
const batch = keys.slice(offset, offset + DELETE_BATCH_SIZE);
|
|
14537
|
+
const response = await client.send(new _aws_sdk_client_s3.DeleteObjectsCommand({
|
|
14538
|
+
Bucket: bucketName,
|
|
14539
|
+
Delete: {
|
|
14540
|
+
Objects: batch.map((Key) => ({ Key })),
|
|
14541
|
+
Quiet: true
|
|
14542
|
+
}
|
|
14543
|
+
}));
|
|
14544
|
+
if (response.Errors && response.Errors.length > 0) throw new Error(`Failed to delete ${response.Errors.length} S3 object(s) from bucket "${bucketName}".`);
|
|
14545
|
+
}
|
|
14490
14546
|
}
|
|
14491
14547
|
});
|
|
14548
|
+
};
|
|
14549
|
+
//#endregion
|
|
14550
|
+
//#region src/s3Storage.ts
|
|
14551
|
+
function s3Storage(config) {
|
|
14552
|
+
const { bucketName, basePath, getDownloadUrl, credentials, ...s3Config } = config;
|
|
14553
|
+
return createS3Storage({
|
|
14554
|
+
name: "s3Storage",
|
|
14555
|
+
protocol: "s3"
|
|
14556
|
+
}, {
|
|
14557
|
+
...applyS3RuntimeAwsConfig(s3Config),
|
|
14558
|
+
credentials,
|
|
14559
|
+
bucketName,
|
|
14560
|
+
basePath,
|
|
14561
|
+
getDownloadUrl
|
|
14562
|
+
});
|
|
14492
14563
|
}
|
|
14493
14564
|
//#endregion
|
|
14494
14565
|
//#region lambda/index.ts
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/aws",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.2.0-rc.
|
|
4
|
+
"version": "1.2.0-rc.9",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"es-toolkit": "^1.32.0",
|
|
46
46
|
"execa": "9.5.2",
|
|
47
47
|
"mime": "^4.0.4",
|
|
48
|
-
"@hot-updater/protocol": "1.2.0-rc.
|
|
49
|
-
"@hot-updater/mock": "1.2.0-rc.
|
|
50
|
-
"@hot-updater/test-utils": "1.2.0-rc.
|
|
48
|
+
"@hot-updater/protocol": "1.2.0-rc.9",
|
|
49
|
+
"@hot-updater/mock": "1.2.0-rc.9",
|
|
50
|
+
"@hot-updater/test-utils": "1.2.0-rc.9"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@aws-sdk/client-cloudfront": "3.1066.0",
|
|
@@ -64,9 +64,10 @@
|
|
|
64
64
|
"@aws-sdk/lib-dynamodb": "3.1066.0",
|
|
65
65
|
"@aws-sdk/s3-request-presigner": "3.1066.0",
|
|
66
66
|
"hono": "4.12.34",
|
|
67
|
-
"@hot-updater/cli-tools": "1.2.0-rc.
|
|
68
|
-
"@hot-updater/plugin-core": "1.2.0-rc.
|
|
69
|
-
"@hot-updater/server": "1.2.0-rc.
|
|
67
|
+
"@hot-updater/cli-tools": "1.2.0-rc.9",
|
|
68
|
+
"@hot-updater/plugin-core": "1.2.0-rc.9",
|
|
69
|
+
"@hot-updater/server": "1.2.0-rc.9",
|
|
70
|
+
"@hot-updater/storage-s3": "1.2.0-rc.9"
|
|
70
71
|
},
|
|
71
72
|
"scripts": {
|
|
72
73
|
"build": "tsdown",
|