@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.
@@ -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.7";
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(clientConfig);
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(config);
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 src/s3Storage.ts
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 PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
14346
- const isObjectNotFoundError = (error) => error instanceof Error && (error.name === "NotFound" || error.name === "NoSuchKey");
14347
- function s3Storage(config) {
14348
- const { bucketName, basePath, getDownloadUrl, ...s3Config } = config;
14349
- const client = new _aws_sdk_client_s3.S3Client(applyS3RuntimeAwsConfig(s3Config));
14350
- const normalizedBasePath = basePath?.replace(/^\/+|\/+$/g, "") ?? "";
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 normalizedPrefix = prefix.replace(/^\/+|\/+$/g, "");
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: "s3"
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, "s3");
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: "s3Storage",
14384
- protocol: "s3",
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
- const response = await new _aws_sdk_lib_storage.Upload({
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: "s3"
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
- const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
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: PRESIGNED_URL_EXPIRES_IN_SECONDS }) };
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.7",
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.7",
49
- "@hot-updater/mock": "1.2.0-rc.7",
50
- "@hot-updater/test-utils": "1.2.0-rc.7"
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.7",
68
- "@hot-updater/plugin-core": "1.2.0-rc.7",
69
- "@hot-updater/server": "1.2.0-rc.7"
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",