@hot-updater/aws 0.30.11 → 0.31.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.
@@ -7418,7 +7418,6 @@ function App() {
7418
7418
  export default HotUpdater.wrap({
7419
7419
  baseURL: "%%source%%",
7420
7420
  updateStrategy: "appVersion", // or "fingerprint"
7421
- updateMode: "auto",
7422
7421
  })(App);`;
7423
7422
  //#endregion
7424
7423
  //#region iac/index.ts
@@ -7413,7 +7413,6 @@ function App() {
7413
7413
  export default HotUpdater.wrap({
7414
7414
  baseURL: "%%source%%",
7415
7415
  updateStrategy: "appVersion", // or "fingerprint"
7416
- updateMode: "auto",
7417
7416
  })(App);`;
7418
7417
  //#endregion
7419
7418
  //#region iac/index.ts
package/dist/index.cjs CHANGED
@@ -1531,7 +1531,7 @@ const s3Database = (0, _hot_updater_plugin_core.createBlobDatabasePlugin)({
1531
1531
  });
1532
1532
  //#endregion
1533
1533
  //#region src/s3Storage.ts
1534
- const s3Storage = (0, _hot_updater_plugin_core.createStoragePlugin)({
1534
+ const s3Storage = (0, _hot_updater_plugin_core.createUniversalStoragePlugin)({
1535
1535
  name: "s3Storage",
1536
1536
  supportedProtocol: "s3",
1537
1537
  factory: (config) => {
@@ -1539,59 +1539,89 @@ const s3Storage = (0, _hot_updater_plugin_core.createStoragePlugin)({
1539
1539
  const client = new _aws_sdk_client_s3.S3Client(applyS3RuntimeAwsConfig(s3Config));
1540
1540
  const getStorageKey = (0, _hot_updater_plugin_core.createStorageKeyBuilder)(config.basePath);
1541
1541
  return {
1542
- async delete(storageUri) {
1543
- const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "s3");
1544
- if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
1545
- const listCommand = new _aws_sdk_client_s3.ListObjectsV2Command({
1546
- Bucket: bucketName,
1547
- Prefix: key
1548
- });
1549
- const listResponse = await client.send(listCommand);
1550
- if (listResponse.Contents && listResponse.Contents.length > 0) {
1551
- const deleteCommand = new _aws_sdk_client_s3.DeleteObjectsCommand({
1542
+ node: {
1543
+ async delete(storageUri) {
1544
+ const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "s3");
1545
+ if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
1546
+ const listCommand = new _aws_sdk_client_s3.ListObjectsV2Command({
1552
1547
  Bucket: bucketName,
1553
- Delete: {
1554
- Objects: listResponse.Contents.map((obj) => ({ Key: obj.Key })),
1555
- Quiet: true
1556
- }
1548
+ Prefix: key
1557
1549
  });
1558
- await client.send(deleteCommand);
1559
- return;
1560
- }
1561
- throw new Error("Bundle Not Found");
1562
- },
1563
- async upload(key, filePath) {
1564
- const Body = await fs_promises.default.readFile(filePath);
1565
- const ContentType = (0, _hot_updater_plugin_core.getContentType)(filePath);
1566
- const Key = getStorageKey(key, path.default.basename(filePath));
1567
- const response = await new _aws_sdk_lib_storage.Upload({
1568
- client,
1569
- params: {
1570
- ContentType,
1571
- Bucket: bucketName,
1572
- Key,
1573
- Body,
1574
- CacheControl: "max-age=31536000"
1550
+ const listResponse = await client.send(listCommand);
1551
+ if (listResponse.Contents && listResponse.Contents.length > 0) {
1552
+ const deleteCommand = new _aws_sdk_client_s3.DeleteObjectsCommand({
1553
+ Bucket: bucketName,
1554
+ Delete: {
1555
+ Objects: listResponse.Contents.map((obj) => ({ Key: obj.Key })),
1556
+ Quiet: true
1557
+ }
1558
+ });
1559
+ await client.send(deleteCommand);
1560
+ return;
1575
1561
  }
1576
- }).done();
1577
- if (!response.Bucket || !response.Key) throw new Error("Upload Failed");
1578
- return { storageUri: `s3://${bucketName}/${Key}` };
1579
- },
1580
- async getDownloadUrl(storageUri) {
1581
- const u = new URL(storageUri);
1582
- if (u.protocol.replace(":", "") !== "s3") throw new Error("Invalid S3 storage URI protocol");
1583
- const bucket = u.host;
1584
- const key = u.pathname.slice(1);
1585
- if (!bucket || !key) throw new Error("Invalid S3 storage URI: missing bucket or key");
1586
- try {
1587
- const signedUrl = await (0, _aws_sdk_s3_request_presigner.getSignedUrl)(client, new _aws_sdk_client_s3.GetObjectCommand({
1562
+ throw new Error("Bundle Not Found");
1563
+ },
1564
+ async upload(key, filePath) {
1565
+ const Body = await fs_promises.default.readFile(filePath);
1566
+ const ContentType = (0, _hot_updater_plugin_core.getContentType)(filePath);
1567
+ const Key = getStorageKey(key, path.default.basename(filePath));
1568
+ const response = await new _aws_sdk_lib_storage.Upload({
1569
+ client,
1570
+ params: {
1571
+ ContentType,
1572
+ Bucket: bucketName,
1573
+ Key,
1574
+ Body,
1575
+ CacheControl: "max-age=31536000"
1576
+ }
1577
+ }).done();
1578
+ if (!response.Bucket || !response.Key) throw new Error("Upload Failed");
1579
+ return { storageUri: `s3://${bucketName}/${Key}` };
1580
+ },
1581
+ async downloadFile(storageUri, filePath) {
1582
+ const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "s3");
1583
+ if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
1584
+ const response = await client.send(new _aws_sdk_client_s3.GetObjectCommand({
1588
1585
  Bucket: bucket,
1589
1586
  Key: key
1590
- }), { expiresIn: 3600 });
1591
- if (!signedUrl) throw new Error("Failed to presign S3 URL");
1592
- return { fileUrl: signedUrl };
1593
- } catch (e) {
1594
- throw new Error(e instanceof Error ? `Failed to presign S3 URL: ${e.message}` : "Failed to presign S3 URL");
1587
+ }));
1588
+ if (!response.Body) throw new Error("S3 object body is empty");
1589
+ await fs_promises.default.mkdir(path.default.dirname(filePath), { recursive: true });
1590
+ await fs_promises.default.writeFile(filePath, await response.Body.transformToByteArray());
1591
+ }
1592
+ },
1593
+ runtime: {
1594
+ async readText(storageUri) {
1595
+ const { bucket, key } = (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, "s3");
1596
+ if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
1597
+ try {
1598
+ const response = await client.send(new _aws_sdk_client_s3.GetObjectCommand({
1599
+ Bucket: bucket,
1600
+ Key: key
1601
+ }));
1602
+ if (!response.Body) return null;
1603
+ return response.Body.transformToString();
1604
+ } catch (error) {
1605
+ if (error instanceof Error && error.name === "NoSuchKey") return null;
1606
+ throw error;
1607
+ }
1608
+ },
1609
+ async getDownloadUrl(storageUri) {
1610
+ const u = new URL(storageUri);
1611
+ if (u.protocol.replace(":", "") !== "s3") throw new Error("Invalid S3 storage URI protocol");
1612
+ const bucket = u.host;
1613
+ const key = u.pathname.slice(1);
1614
+ if (!bucket || !key) throw new Error("Invalid S3 storage URI: missing bucket or key");
1615
+ try {
1616
+ const signedUrl = await (0, _aws_sdk_s3_request_presigner.getSignedUrl)(client, new _aws_sdk_client_s3.GetObjectCommand({
1617
+ Bucket: bucket,
1618
+ Key: key
1619
+ }), { expiresIn: 3600 });
1620
+ if (!signedUrl) throw new Error("Failed to presign S3 URL");
1621
+ return { fileUrl: signedUrl };
1622
+ } catch (e) {
1623
+ throw new Error(e instanceof Error ? `Failed to presign S3 URL: ${e.message}` : "Failed to presign S3 URL");
1624
+ }
1595
1625
  }
1596
1626
  }
1597
1627
  };
@@ -1643,19 +1673,25 @@ const withCloudFrontSignedUrl = (storageFactory, config) => {
1643
1673
  return {
1644
1674
  ...baseStorage,
1645
1675
  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
- }) };
1676
+ profiles: {
1677
+ ...baseStorage.profiles,
1678
+ runtime: {
1679
+ ...baseStorage.profiles.runtime,
1680
+ async getDownloadUrl(storageUri, context) {
1681
+ const storageUrl = new URL(storageUri);
1682
+ if (storageUrl.protocol !== "s3:") return baseStorage.profiles.runtime.getDownloadUrl(storageUri, context);
1683
+ const [privateKey, publicBaseUrl] = await Promise.all([resolvePrivateKey(config), resolvePublicBaseUrl(config, context)]);
1684
+ const url = new URL(publicBaseUrl);
1685
+ url.pathname = storageUrl.pathname;
1686
+ url.search = "";
1687
+ return { fileUrl: (0, _aws_sdk_cloudfront_signer.getSignedUrl)({
1688
+ url: url.toString(),
1689
+ keyPairId: config.keyPairId,
1690
+ privateKey,
1691
+ dateLessThan: new Date(Date.now() + (config.expiresSeconds ?? ONE_YEAR_IN_SECONDS) * 1e3).toISOString()
1692
+ }) };
1693
+ }
1694
+ }
1659
1695
  }
1660
1696
  };
1661
1697
  };
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
2
- import { BlobDatabasePluginConfig, StoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
2
+ import { BlobDatabasePluginConfig, RuntimeStoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
3
3
  import { S3ClientConfig } from "@aws-sdk/client-s3";
4
4
 
5
5
  //#region src/s3Database.d.ts
@@ -32,7 +32,7 @@ interface S3StorageConfig extends S3ClientConfig {
32
32
  */
33
33
  basePath?: string;
34
34
  }
35
- declare const s3Storage: (config: S3StorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.StoragePlugin<unknown>;
35
+ declare const s3Storage: (config: S3StorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
36
36
  //#endregion
37
37
  //#region src/withCloudFrontSignedUrl.d.ts
38
38
  interface CloudFrontPrivateKeyFromGetter {
@@ -52,11 +52,11 @@ type WithCloudFrontSignedUrlOptions<TContext = unknown> = CloudFrontSignedUrlCon
52
52
  publicBaseUrl: string | PublicBaseUrlResolver<TContext>;
53
53
  expiresSeconds?: number;
54
54
  };
55
- declare const withCloudFrontSignedUrl: <TContext = unknown>(storageFactory: () => StoragePlugin<TContext>, config: WithCloudFrontSignedUrlOptions<TContext>) => () => StoragePlugin<TContext>;
55
+ declare const withCloudFrontSignedUrl: <TContext = unknown, TStorage extends RuntimeStoragePlugin<TContext> = RuntimeStoragePlugin<TContext>>(storageFactory: () => TStorage, config: WithCloudFrontSignedUrlOptions<TContext>) => () => TStorage;
56
56
  //#endregion
57
57
  //#region src/s3LambdaEdgeStorage.d.ts
58
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>;
59
+ declare const awsLambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
60
+ declare const s3LambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
61
61
  //#endregion
62
62
  export { AwsLambdaEdgeStorageConfig, CloudFrontSignedUrlConfig, PublicBaseUrlResolver, S3DatabaseConfig, S3StorageConfig, WithCloudFrontSignedUrlOptions, awsLambdaEdgeStorage, s3Database, s3LambdaEdgeStorage, s3Storage, withCloudFrontSignedUrl };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { S3ClientConfig } from "@aws-sdk/client-s3";
2
2
  import * as _$_hot_updater_plugin_core0 from "@hot-updater/plugin-core";
3
- import { BlobDatabasePluginConfig, StoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
3
+ import { BlobDatabasePluginConfig, RuntimeStoragePlugin, StoragePluginHooks, StorageResolveContext } from "@hot-updater/plugin-core";
4
4
 
5
5
  //#region src/s3Database.d.ts
6
6
  interface S3DatabaseConfig extends S3ClientConfig, BlobDatabasePluginConfig {
@@ -32,7 +32,7 @@ interface S3StorageConfig extends S3ClientConfig {
32
32
  */
33
33
  basePath?: string;
34
34
  }
35
- declare const s3Storage: (config: S3StorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.StoragePlugin<unknown>;
35
+ declare const s3Storage: (config: S3StorageConfig, hooks?: _$_hot_updater_plugin_core0.StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
36
36
  //#endregion
37
37
  //#region src/withCloudFrontSignedUrl.d.ts
38
38
  interface CloudFrontPrivateKeyFromGetter {
@@ -52,11 +52,11 @@ type WithCloudFrontSignedUrlOptions<TContext = unknown> = CloudFrontSignedUrlCon
52
52
  publicBaseUrl: string | PublicBaseUrlResolver<TContext>;
53
53
  expiresSeconds?: number;
54
54
  };
55
- declare const withCloudFrontSignedUrl: <TContext = unknown>(storageFactory: () => StoragePlugin<TContext>, config: WithCloudFrontSignedUrlOptions<TContext>) => () => StoragePlugin<TContext>;
55
+ declare const withCloudFrontSignedUrl: <TContext = unknown, TStorage extends RuntimeStoragePlugin<TContext> = RuntimeStoragePlugin<TContext>>(storageFactory: () => TStorage, config: WithCloudFrontSignedUrlOptions<TContext>) => () => TStorage;
56
56
  //#endregion
57
57
  //#region src/s3LambdaEdgeStorage.d.ts
58
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>;
59
+ declare const awsLambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
60
+ declare const s3LambdaEdgeStorage: (config: AwsLambdaEdgeStorageConfig, hooks?: StoragePluginHooks) => () => _$_hot_updater_plugin_core0.UniversalStoragePlugin<unknown>;
61
61
  //#endregion
62
62
  export { AwsLambdaEdgeStorageConfig, CloudFrontSignedUrlConfig, PublicBaseUrlResolver, S3DatabaseConfig, S3StorageConfig, WithCloudFrontSignedUrlOptions, awsLambdaEdgeStorage, s3Database, s3LambdaEdgeStorage, s3Storage, withCloudFrontSignedUrl };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CloudFrontClient, CreateInvalidationCommand, GetInvalidationCommand } from "@aws-sdk/client-cloudfront";
2
2
  import { DeleteObjectCommand, DeleteObjectsCommand, GetObjectCommand, ListObjectsV2Command, NoSuchKey, S3Client } from "@aws-sdk/client-s3";
3
3
  import { Upload } from "@aws-sdk/lib-storage";
4
- import { createBlobDatabasePlugin, createStorageKeyBuilder, createStoragePlugin, getContentType, parseStorageUri } from "@hot-updater/plugin-core";
4
+ import { createBlobDatabasePlugin, createStorageKeyBuilder, createUniversalStoragePlugin, getContentType, parseStorageUri } from "@hot-updater/plugin-core";
5
5
  import fs from "fs/promises";
6
6
  import path from "path";
7
7
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
@@ -1506,7 +1506,7 @@ const s3Database = createBlobDatabasePlugin({
1506
1506
  });
1507
1507
  //#endregion
1508
1508
  //#region src/s3Storage.ts
1509
- const s3Storage = createStoragePlugin({
1509
+ const s3Storage = createUniversalStoragePlugin({
1510
1510
  name: "s3Storage",
1511
1511
  supportedProtocol: "s3",
1512
1512
  factory: (config) => {
@@ -1514,59 +1514,89 @@ const s3Storage = createStoragePlugin({
1514
1514
  const client = new S3Client(applyS3RuntimeAwsConfig(s3Config));
1515
1515
  const getStorageKey = createStorageKeyBuilder(config.basePath);
1516
1516
  return {
1517
- async delete(storageUri) {
1518
- const { bucket, key } = parseStorageUri(storageUri, "s3");
1519
- if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
1520
- const listCommand = new ListObjectsV2Command({
1521
- Bucket: bucketName,
1522
- Prefix: key
1523
- });
1524
- const listResponse = await client.send(listCommand);
1525
- if (listResponse.Contents && listResponse.Contents.length > 0) {
1526
- const deleteCommand = new DeleteObjectsCommand({
1517
+ node: {
1518
+ async delete(storageUri) {
1519
+ const { bucket, key } = parseStorageUri(storageUri, "s3");
1520
+ if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
1521
+ const listCommand = new ListObjectsV2Command({
1527
1522
  Bucket: bucketName,
1528
- Delete: {
1529
- Objects: listResponse.Contents.map((obj) => ({ Key: obj.Key })),
1530
- Quiet: true
1531
- }
1523
+ Prefix: key
1532
1524
  });
1533
- await client.send(deleteCommand);
1534
- return;
1535
- }
1536
- throw new Error("Bundle Not Found");
1537
- },
1538
- async upload(key, filePath) {
1539
- const Body = await fs.readFile(filePath);
1540
- const ContentType = getContentType(filePath);
1541
- const Key = getStorageKey(key, path.basename(filePath));
1542
- const response = await new Upload({
1543
- client,
1544
- params: {
1545
- ContentType,
1546
- Bucket: bucketName,
1547
- Key,
1548
- Body,
1549
- CacheControl: "max-age=31536000"
1525
+ const listResponse = await client.send(listCommand);
1526
+ if (listResponse.Contents && listResponse.Contents.length > 0) {
1527
+ const deleteCommand = new DeleteObjectsCommand({
1528
+ Bucket: bucketName,
1529
+ Delete: {
1530
+ Objects: listResponse.Contents.map((obj) => ({ Key: obj.Key })),
1531
+ Quiet: true
1532
+ }
1533
+ });
1534
+ await client.send(deleteCommand);
1535
+ return;
1550
1536
  }
1551
- }).done();
1552
- if (!response.Bucket || !response.Key) throw new Error("Upload Failed");
1553
- return { storageUri: `s3://${bucketName}/${Key}` };
1554
- },
1555
- async getDownloadUrl(storageUri) {
1556
- const u = new URL(storageUri);
1557
- if (u.protocol.replace(":", "") !== "s3") throw new Error("Invalid S3 storage URI protocol");
1558
- const bucket = u.host;
1559
- const key = u.pathname.slice(1);
1560
- if (!bucket || !key) throw new Error("Invalid S3 storage URI: missing bucket or key");
1561
- try {
1562
- const signedUrl = await getSignedUrl(client, new GetObjectCommand({
1537
+ throw new Error("Bundle Not Found");
1538
+ },
1539
+ async upload(key, filePath) {
1540
+ const Body = await fs.readFile(filePath);
1541
+ const ContentType = getContentType(filePath);
1542
+ const Key = getStorageKey(key, path.basename(filePath));
1543
+ const response = await new Upload({
1544
+ client,
1545
+ params: {
1546
+ ContentType,
1547
+ Bucket: bucketName,
1548
+ Key,
1549
+ Body,
1550
+ CacheControl: "max-age=31536000"
1551
+ }
1552
+ }).done();
1553
+ if (!response.Bucket || !response.Key) throw new Error("Upload Failed");
1554
+ return { storageUri: `s3://${bucketName}/${Key}` };
1555
+ },
1556
+ async downloadFile(storageUri, filePath) {
1557
+ const { bucket, key } = parseStorageUri(storageUri, "s3");
1558
+ if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
1559
+ const response = await client.send(new GetObjectCommand({
1563
1560
  Bucket: bucket,
1564
1561
  Key: key
1565
- }), { expiresIn: 3600 });
1566
- if (!signedUrl) throw new Error("Failed to presign S3 URL");
1567
- return { fileUrl: signedUrl };
1568
- } catch (e) {
1569
- throw new Error(e instanceof Error ? `Failed to presign S3 URL: ${e.message}` : "Failed to presign S3 URL");
1562
+ }));
1563
+ if (!response.Body) throw new Error("S3 object body is empty");
1564
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
1565
+ await fs.writeFile(filePath, await response.Body.transformToByteArray());
1566
+ }
1567
+ },
1568
+ runtime: {
1569
+ async readText(storageUri) {
1570
+ const { bucket, key } = parseStorageUri(storageUri, "s3");
1571
+ if (bucket !== bucketName) throw new Error(`Bucket name mismatch: expected "${bucketName}", but found "${bucket}".`);
1572
+ try {
1573
+ const response = await client.send(new GetObjectCommand({
1574
+ Bucket: bucket,
1575
+ Key: key
1576
+ }));
1577
+ if (!response.Body) return null;
1578
+ return response.Body.transformToString();
1579
+ } catch (error) {
1580
+ if (error instanceof Error && error.name === "NoSuchKey") return null;
1581
+ throw error;
1582
+ }
1583
+ },
1584
+ async getDownloadUrl(storageUri) {
1585
+ const u = new URL(storageUri);
1586
+ if (u.protocol.replace(":", "") !== "s3") throw new Error("Invalid S3 storage URI protocol");
1587
+ const bucket = u.host;
1588
+ const key = u.pathname.slice(1);
1589
+ if (!bucket || !key) throw new Error("Invalid S3 storage URI: missing bucket or key");
1590
+ try {
1591
+ const signedUrl = await getSignedUrl(client, new GetObjectCommand({
1592
+ Bucket: bucket,
1593
+ Key: key
1594
+ }), { expiresIn: 3600 });
1595
+ if (!signedUrl) throw new Error("Failed to presign S3 URL");
1596
+ return { fileUrl: signedUrl };
1597
+ } catch (e) {
1598
+ throw new Error(e instanceof Error ? `Failed to presign S3 URL: ${e.message}` : "Failed to presign S3 URL");
1599
+ }
1570
1600
  }
1571
1601
  }
1572
1602
  };
@@ -1618,19 +1648,25 @@ const withCloudFrontSignedUrl = (storageFactory, config) => {
1618
1648
  return {
1619
1649
  ...baseStorage,
1620
1650
  name: `${baseStorage.name}WithCloudFrontSignedUrl`,
1621
- async getDownloadUrl(storageUri, context) {
1622
- const storageUrl = new URL(storageUri);
1623
- if (storageUrl.protocol !== "s3:") return baseStorage.getDownloadUrl(storageUri, context);
1624
- const [privateKey, publicBaseUrl] = await Promise.all([resolvePrivateKey(config), resolvePublicBaseUrl(config, context)]);
1625
- const url = new URL(publicBaseUrl);
1626
- url.pathname = storageUrl.pathname;
1627
- url.search = "";
1628
- return { fileUrl: getSignedUrl$1({
1629
- url: url.toString(),
1630
- keyPairId: config.keyPairId,
1631
- privateKey,
1632
- dateLessThan: new Date(Date.now() + (config.expiresSeconds ?? ONE_YEAR_IN_SECONDS) * 1e3).toISOString()
1633
- }) };
1651
+ profiles: {
1652
+ ...baseStorage.profiles,
1653
+ runtime: {
1654
+ ...baseStorage.profiles.runtime,
1655
+ async getDownloadUrl(storageUri, context) {
1656
+ const storageUrl = new URL(storageUri);
1657
+ if (storageUrl.protocol !== "s3:") return baseStorage.profiles.runtime.getDownloadUrl(storageUri, context);
1658
+ const [privateKey, publicBaseUrl] = await Promise.all([resolvePrivateKey(config), resolvePublicBaseUrl(config, context)]);
1659
+ const url = new URL(publicBaseUrl);
1660
+ url.pathname = storageUrl.pathname;
1661
+ url.search = "";
1662
+ return { fileUrl: getSignedUrl$1({
1663
+ url: url.toString(),
1664
+ keyPairId: config.keyPairId,
1665
+ privateKey,
1666
+ dateLessThan: new Date(Date.now() + (config.expiresSeconds ?? ONE_YEAR_IN_SECONDS) * 1e3).toISOString()
1667
+ }) };
1668
+ }
1669
+ }
1634
1670
  }
1635
1671
  };
1636
1672
  };