@hot-updater/aws 0.25.13 → 0.26.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.
@@ -30,6 +30,8 @@ let path = require("path");
30
30
  path = __toESM$1(path);
31
31
  let node_crypto = require("node:crypto");
32
32
  node_crypto = __toESM$1(node_crypto);
33
+ let __aws_sdk_client_s3 = require("@aws-sdk/client-s3");
34
+ __aws_sdk_client_s3 = __toESM$1(__aws_sdk_client_s3);
33
35
 
34
36
  //#region ../../packages/core/dist/index.js
35
37
  const HOT_UPDATE_DIR_NAME = ".hot-updater";
@@ -1424,253 +1426,33 @@ var isContentTypeBinary = (contentType) => {
1424
1426
  };
1425
1427
 
1426
1428
  //#endregion
1427
- //#region ../../node_modules/.pnpm/@aws-sdk+cloudfront-signer@3.772.0/node_modules/@aws-sdk/cloudfront-signer/dist-cjs/index.js
1428
- var require_dist_cjs = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/@aws-sdk+cloudfront-signer@3.772.0/node_modules/@aws-sdk/cloudfront-signer/dist-cjs/index.js": ((exports, module) => {
1429
- var __defProp$1 = Object.defineProperty;
1430
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
1431
- var __getOwnPropNames$1 = Object.getOwnPropertyNames;
1432
- var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
1433
- var __name = (target, value) => __defProp$1(target, "name", {
1434
- value,
1435
- configurable: true
1436
- });
1437
- var __export = (target, all) => {
1438
- for (var name in all) __defProp$1(target, name, {
1439
- get: all[name],
1440
- enumerable: true
1441
- });
1442
- };
1443
- var __copyProps$1 = (to, from, except, desc) => {
1444
- if (from && typeof from === "object" || typeof from === "function") {
1445
- for (let key of __getOwnPropNames$1(from)) if (!__hasOwnProp$1.call(to, key) && key !== except) __defProp$1(to, key, {
1446
- get: () => from[key],
1447
- enumerable: !(desc = __getOwnPropDesc$1(from, key)) || desc.enumerable
1448
- });
1449
- }
1450
- return to;
1451
- };
1452
- var __toCommonJS = (mod) => __copyProps$1(__defProp$1({}, "__esModule", { value: true }), mod);
1453
- var index_exports = {};
1454
- __export(index_exports, {
1455
- getSignedCookies: () => getSignedCookies,
1456
- getSignedUrl: () => getSignedUrl$2
1457
- });
1458
- module.exports = __toCommonJS(index_exports);
1459
- var import_crypto = require("crypto");
1460
- function getSignedUrl$2({ dateLessThan, dateGreaterThan, url, keyPairId, privateKey, ipAddress, policy, passphrase }) {
1461
- const cloudfrontSignBuilder = new CloudfrontSignBuilder({
1462
- keyPairId,
1463
- privateKey,
1464
- passphrase
1465
- });
1466
- if (!url && !policy) throw new Error("@aws-sdk/cloudfront-signer: Please provide 'url' or 'policy'.");
1467
- if (policy) cloudfrontSignBuilder.setCustomPolicy(policy);
1468
- else cloudfrontSignBuilder.setPolicyParameters({
1469
- url,
1470
- dateLessThan,
1471
- dateGreaterThan,
1472
- ipAddress
1473
- });
1474
- let baseUrl;
1475
- if (url) baseUrl = url;
1476
- else if (policy) {
1477
- const resources = getPolicyResources(policy);
1478
- if (!resources[0]) throw new Error("@aws-sdk/cloudfront-signer: No URL provided and unable to determine URL from first policy statement resource.");
1479
- baseUrl = resources[0].replace("*://", "https://");
1480
- }
1481
- const newURL = new URL(baseUrl);
1482
- newURL.search = Array.from(newURL.searchParams.entries()).concat(Object.entries(cloudfrontSignBuilder.createCloudfrontAttribute())).filter(([, value]) => value !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
1483
- return getResource(newURL);
1484
- }
1485
- __name(getSignedUrl$2, "getSignedUrl");
1486
- function getSignedCookies({ ipAddress, url, privateKey, keyPairId, dateLessThan, dateGreaterThan, policy, passphrase }) {
1487
- const cloudfrontSignBuilder = new CloudfrontSignBuilder({
1488
- keyPairId,
1489
- privateKey,
1490
- passphrase
1491
- });
1492
- if (policy) cloudfrontSignBuilder.setCustomPolicy(policy);
1493
- else cloudfrontSignBuilder.setPolicyParameters({
1494
- url,
1495
- dateLessThan,
1496
- dateGreaterThan,
1497
- ipAddress
1498
- });
1499
- const cloudfrontCookieAttributes = cloudfrontSignBuilder.createCloudfrontAttribute();
1500
- const cookies = {
1501
- "CloudFront-Key-Pair-Id": cloudfrontCookieAttributes["Key-Pair-Id"],
1502
- "CloudFront-Signature": cloudfrontCookieAttributes["Signature"]
1503
- };
1504
- if (cloudfrontCookieAttributes["Expires"]) cookies["CloudFront-Expires"] = cloudfrontCookieAttributes["Expires"];
1505
- if (cloudfrontCookieAttributes["Policy"]) cookies["CloudFront-Policy"] = cloudfrontCookieAttributes["Policy"];
1506
- return cookies;
1507
- }
1508
- __name(getSignedCookies, "getSignedCookies");
1509
- function getPolicyResources(policy) {
1510
- return ((typeof policy === "string" ? JSON.parse(policy) : policy)?.Statement ?? []).map((s) => s.Resource);
1511
- }
1512
- __name(getPolicyResources, "getPolicyResources");
1513
- function getResource(url) {
1514
- switch (url.protocol) {
1515
- case "http:":
1516
- case "https:":
1517
- case "ws:":
1518
- case "wss:": return url.toString();
1519
- case "rtmp:": return url.pathname.replace(/^\//, "") + url.search + url.hash;
1520
- default: throw new Error("Invalid URI scheme. Scheme must be one of http, https, or rtmp");
1521
- }
1522
- }
1523
- __name(getResource, "getResource");
1524
- var CloudfrontSignBuilder = class {
1525
- static {
1526
- __name(this, "CloudfrontSignBuilder");
1527
- }
1528
- keyPairId;
1529
- privateKey;
1530
- passphrase;
1531
- policy;
1532
- customPolicy = false;
1533
- dateLessThan;
1534
- constructor({ privateKey, keyPairId, passphrase }) {
1535
- this.keyPairId = keyPairId;
1536
- this.privateKey = privateKey;
1537
- this.policy = "";
1538
- this.passphrase = passphrase;
1539
- }
1540
- buildPolicy(args) {
1541
- const policy = { Statement: [{
1542
- Resource: args.resource,
1543
- Condition: { DateLessThan: { "AWS:EpochTime": args.dateLessThan } }
1544
- }] };
1545
- if (args.dateGreaterThan) policy.Statement[0].Condition["DateGreaterThan"] = { "AWS:EpochTime": args.dateGreaterThan };
1546
- if (args.ipAddress) {
1547
- const cidr = this.parseCIDR(args.ipAddress);
1548
- policy.Statement[0].Condition["IpAddress"] = { "AWS:SourceIp": cidr };
1549
- }
1550
- return policy;
1551
- }
1552
- normalizeBase64(str) {
1553
- const replacements = {
1554
- "+": "-",
1555
- "=": "_",
1556
- "/": "~"
1557
- };
1558
- return str.replace(/[+=/]/g, function(match) {
1559
- return replacements[match];
1560
- });
1561
- }
1562
- encodeToBase64(str) {
1563
- return this.normalizeBase64(Buffer.from(str).toString("base64"));
1564
- }
1565
- validateIP(ipStr) {
1566
- const octets = ipStr.split(".");
1567
- if (octets.length !== 4) throw new Error(`IP does not contain four octets.`);
1568
- if (!octets.every((octet) => {
1569
- const num = Number(octet);
1570
- return Number.isInteger(num) && num >= 0 && num <= 255;
1571
- })) throw new Error("invalid IP octets");
1572
- }
1573
- validateMask(maskStr) {
1574
- const mask = Number(maskStr);
1575
- if (!(Number.isInteger(mask) && mask >= 0 && mask <= 32)) throw new Error("invalid mask");
1576
- }
1577
- parseCIDR(cidrStr) {
1578
- try {
1579
- const cidrParts = cidrStr.split("/");
1580
- if (cidrParts.some((part) => part.length === 0)) throw new Error("missing ip or mask part of CIDR");
1581
- this.validateIP(cidrParts[0]);
1582
- let mask = "32";
1583
- if (cidrParts.length === 2) {
1584
- this.validateMask(cidrParts[1]);
1585
- mask = cidrParts[1];
1586
- }
1587
- return `${cidrParts[0]}/${mask}`;
1588
- } catch (error) {
1589
- const errMessage = `IP address "${cidrStr}" is invalid`;
1590
- if (error instanceof Error) throw new Error(`${errMessage} due to ${error.message}.`);
1591
- else throw new Error(`${errMessage}.`);
1592
- }
1593
- }
1594
- epochTime(date) {
1595
- return Math.round(date.getTime() / 1e3);
1596
- }
1597
- parseDate(date) {
1598
- if (!date) return;
1599
- const parsedDate = new Date(date);
1600
- return isNaN(parsedDate.getTime()) ? void 0 : this.epochTime(parsedDate);
1601
- }
1602
- parseDateWindow(expiration, start) {
1603
- const dateLessThan = this.parseDate(expiration);
1604
- if (!dateLessThan) throw new Error("dateLessThan is invalid. Ensure the date value is compatible with the Date constructor.");
1605
- return {
1606
- dateLessThan,
1607
- dateGreaterThan: this.parseDate(start)
1608
- };
1609
- }
1610
- signData(data, privateKey, passphrase) {
1611
- const sign = (0, import_crypto.createSign)("RSA-SHA1");
1612
- sign.update(data);
1613
- return sign.sign({
1614
- key: privateKey,
1615
- passphrase
1616
- }, "base64");
1617
- }
1618
- signPolicy(policy, privateKey, passphrase) {
1619
- return this.normalizeBase64(this.signData(policy, privateKey, passphrase));
1620
- }
1621
- setCustomPolicy(policy) {
1622
- this.customPolicy = true;
1623
- this.policy = policy;
1624
- }
1625
- setPolicyParameters({ url, dateLessThan, dateGreaterThan, ipAddress }) {
1626
- if (!url || !dateLessThan) return false;
1627
- const resource = getResource(new URL(url));
1628
- const parsedDates = this.parseDateWindow(dateLessThan, dateGreaterThan);
1629
- this.dateLessThan = parsedDates.dateLessThan;
1630
- this.customPolicy = Boolean(parsedDates.dateGreaterThan) || Boolean(ipAddress);
1631
- this.policy = JSON.stringify(this.buildPolicy({
1632
- resource,
1633
- ipAddress,
1634
- dateLessThan: parsedDates.dateLessThan,
1635
- dateGreaterThan: parsedDates.dateGreaterThan
1636
- }));
1637
- }
1638
- createCloudfrontAttribute() {
1639
- if (!Boolean(this.policy)) throw new Error("Invalid policy");
1640
- const signature = this.signPolicy(this.policy, this.privateKey, this.passphrase);
1641
- return {
1642
- Expires: this.customPolicy ? void 0 : this.dateLessThan,
1643
- Policy: this.customPolicy ? this.encodeToBase64(this.policy) : void 0,
1644
- "Key-Pair-Id": this.keyPairId,
1645
- Signature: signature
1646
- };
1647
- }
1648
- };
1649
- }) });
1429
+ //#region lambda/cacheControl.ts
1430
+ const ONE_YEAR_IN_SECONDS = 3600 * 24 * 365;
1431
+ const NO_STORE_CACHE_CONTROL = "no-store";
1432
+ const SHARED_EDGE_CACHE_CONTROL = `public, max-age=0, s-maxage=${ONE_YEAR_IN_SECONDS}, must-revalidate`;
1650
1433
 
1651
1434
  //#endregion
1652
1435
  //#region ../js/dist/index.js
1653
- var import_dist_cjs$1 = /* @__PURE__ */ __toESM$1(require_dist_cjs(), 1);
1654
1436
  var __create = Object.create;
1655
- var __defProp = Object.defineProperty;
1656
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
1657
- var __getOwnPropNames = Object.getOwnPropertyNames;
1437
+ var __defProp$1 = Object.defineProperty;
1438
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
1439
+ var __getOwnPropNames$1 = Object.getOwnPropertyNames;
1658
1440
  var __getProtoOf = Object.getPrototypeOf;
1659
- var __hasOwnProp = Object.prototype.hasOwnProperty;
1441
+ var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
1660
1442
  var __commonJS = (cb, mod) => function() {
1661
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
1443
+ return mod || (0, cb[__getOwnPropNames$1(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
1662
1444
  };
1663
- var __copyProps = (to, from, except, desc) => {
1664
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
1445
+ var __copyProps$1 = (to, from, except, desc) => {
1446
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames$1(from), i = 0, n = keys.length, key; i < n; i++) {
1665
1447
  key = keys[i];
1666
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
1448
+ if (!__hasOwnProp$1.call(to, key) && key !== except) __defProp$1(to, key, {
1667
1449
  get: ((k) => from[k]).bind(null, key),
1668
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
1450
+ enumerable: !(desc = __getOwnPropDesc$1(from, key)) || desc.enumerable
1669
1451
  });
1670
1452
  }
1671
1453
  return to;
1672
1454
  };
1673
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
1455
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps$1(isNodeMode || !mod || !mod.__esModule ? __defProp$1(target, "default", {
1674
1456
  value: mod,
1675
1457
  enumerable: true
1676
1458
  }) : target, mod));
@@ -3038,51 +2820,46 @@ const year = day * 365.25;
3038
2820
 
3039
2821
  //#endregion
3040
2822
  //#region lambda/getUpdateInfo.ts
3041
- const getCdnJson = async ({ baseUrl, key, keyPairId, privateKey }) => {
2823
+ const s3Clients = /* @__PURE__ */ new Map();
2824
+ const getS3Client = (region) => {
2825
+ const existingClient = s3Clients.get(region);
2826
+ if (existingClient) return existingClient;
2827
+ const client = new __aws_sdk_client_s3.S3({ region });
2828
+ s3Clients.set(region, client);
2829
+ return client;
2830
+ };
2831
+ const getS3Json = async ({ bucketName, key, region }) => {
3042
2832
  try {
3043
- const url = new URL(baseUrl);
3044
- url.pathname = `/${key}`;
3045
- const signedUrl = (0, import_dist_cjs$1.getSignedUrl)({
3046
- url: url.toString(),
3047
- keyPairId,
3048
- privateKey,
3049
- dateLessThan: new Date(Date.now() + 60 * 1e3).toISOString()
2833
+ const body = await (await getS3Client(region).send(new __aws_sdk_client_s3.GetObjectCommand({
2834
+ Bucket: bucketName,
2835
+ Key: key
2836
+ }))).Body?.transformToString();
2837
+ if (!body) return null;
2838
+ return JSON.parse(body);
2839
+ } catch (error) {
2840
+ if (error instanceof Error && (error.name === "NoSuchKey" || error.name === "NotFound")) return null;
2841
+ console.error("Failed to read Hot Updater manifest from S3:", {
2842
+ key,
2843
+ error
3050
2844
  });
3051
- const res = await fetch(signedUrl, { headers: { "Content-Type": "application/json" } });
3052
- if (!res.ok) return null;
3053
- return res.json();
3054
- } catch {
3055
2845
  return null;
3056
2846
  }
3057
2847
  };
3058
- const getUpdateInfo = async ({ baseUrl, keyPairId, privateKey }, args) => {
2848
+ const getUpdateInfo = async ({ bucketName, region, readManifestJson }, args) => {
2849
+ const manifestReader = readManifestJson ?? ((key) => getS3Json({
2850
+ bucketName,
2851
+ key,
2852
+ region
2853
+ }));
3059
2854
  switch (args._updateStrategy) {
3060
- case "appVersion": return appVersionStrategy({
3061
- baseUrl,
3062
- keyPairId,
3063
- privateKey
3064
- }, args);
3065
- case "fingerprint": return fingerprintStrategy({
3066
- baseUrl,
3067
- keyPairId,
3068
- privateKey
3069
- }, args);
2855
+ case "appVersion": return appVersionStrategy({ readManifestJson: manifestReader }, args);
2856
+ case "fingerprint": return fingerprintStrategy({ readManifestJson: manifestReader }, args);
3070
2857
  default: return null;
3071
2858
  }
3072
2859
  };
3073
- const appVersionStrategy = async ({ baseUrl, keyPairId, privateKey }, { platform, appVersion, bundleId, minBundleId = NIL_UUID, channel = "production" }) => {
3074
- const matchingVersions = filterCompatibleAppVersions(await getCdnJson({
3075
- baseUrl,
3076
- key: `${channel}/${platform}/target-app-versions.json`,
3077
- keyPairId,
3078
- privateKey
3079
- }) ?? [], appVersion);
3080
- return getUpdateInfo$1((await Promise.allSettled(matchingVersions.map((targetAppVersion) => getCdnJson({
3081
- baseUrl,
3082
- key: `${channel}/${platform}/${targetAppVersion}/update.json`,
3083
- keyPairId,
3084
- privateKey
3085
- })))).filter((r) => r.status === "fulfilled").flatMap((r) => r.value ?? []), {
2860
+ const appVersionStrategy = async ({ readManifestJson }, { platform, appVersion, bundleId, minBundleId = NIL_UUID, channel = "production" }) => {
2861
+ const matchingVersions = filterCompatibleAppVersions(await readManifestJson(`${channel}/${platform}/target-app-versions.json`) ?? [], appVersion);
2862
+ return getUpdateInfo$1((await Promise.allSettled(matchingVersions.map((targetAppVersion) => readManifestJson(`${channel}/${platform}/${targetAppVersion}/update.json`)))).filter((r) => r.status === "fulfilled").flatMap((r) => r.value ?? []), {
3086
2863
  platform,
3087
2864
  bundleId,
3088
2865
  appVersion,
@@ -3091,15 +2868,8 @@ const appVersionStrategy = async ({ baseUrl, keyPairId, privateKey }, { platform
3091
2868
  _updateStrategy: "appVersion"
3092
2869
  });
3093
2870
  };
3094
- const fingerprintStrategy = async ({ baseUrl, keyPairId, privateKey }, { platform, fingerprintHash, bundleId, minBundleId = NIL_UUID, channel = "production" }) => {
3095
- const result = await getCdnJson({
3096
- baseUrl,
3097
- key: `${channel}/${platform}/${fingerprintHash}/update.json`,
3098
- keyPairId,
3099
- privateKey
3100
- });
3101
- console.log("result", `${channel}/${platform}/${fingerprintHash}/update.json`, result);
3102
- return getUpdateInfo$1(result ?? [], {
2871
+ const fingerprintStrategy = async ({ readManifestJson }, { platform, fingerprintHash, bundleId, minBundleId = NIL_UUID, channel = "production" }) => {
2872
+ return getUpdateInfo$1(await readManifestJson(`${channel}/${platform}/${fingerprintHash}/update.json`) ?? [], {
3103
2873
  platform,
3104
2874
  bundleId,
3105
2875
  fingerprintHash,
@@ -3109,6 +2879,231 @@ const fingerprintStrategy = async ({ baseUrl, keyPairId, privateKey }, { platfor
3109
2879
  });
3110
2880
  };
3111
2881
 
2882
+ //#endregion
2883
+ //#region ../../node_modules/.pnpm/@aws-sdk+cloudfront-signer@3.772.0/node_modules/@aws-sdk/cloudfront-signer/dist-cjs/index.js
2884
+ var require_dist_cjs = /* @__PURE__ */ __commonJS$1({ "../../node_modules/.pnpm/@aws-sdk+cloudfront-signer@3.772.0/node_modules/@aws-sdk/cloudfront-signer/dist-cjs/index.js": ((exports, module) => {
2885
+ var __defProp = Object.defineProperty;
2886
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
2887
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2888
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
2889
+ var __name = (target, value) => __defProp(target, "name", {
2890
+ value,
2891
+ configurable: true
2892
+ });
2893
+ var __export = (target, all) => {
2894
+ for (var name in all) __defProp(target, name, {
2895
+ get: all[name],
2896
+ enumerable: true
2897
+ });
2898
+ };
2899
+ var __copyProps = (to, from, except, desc) => {
2900
+ if (from && typeof from === "object" || typeof from === "function") {
2901
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
2902
+ get: () => from[key],
2903
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
2904
+ });
2905
+ }
2906
+ return to;
2907
+ };
2908
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
2909
+ var index_exports = {};
2910
+ __export(index_exports, {
2911
+ getSignedCookies: () => getSignedCookies,
2912
+ getSignedUrl: () => getSignedUrl$1
2913
+ });
2914
+ module.exports = __toCommonJS(index_exports);
2915
+ var import_crypto = require("crypto");
2916
+ function getSignedUrl$1({ dateLessThan, dateGreaterThan, url, keyPairId, privateKey, ipAddress, policy, passphrase }) {
2917
+ const cloudfrontSignBuilder = new CloudfrontSignBuilder({
2918
+ keyPairId,
2919
+ privateKey,
2920
+ passphrase
2921
+ });
2922
+ if (!url && !policy) throw new Error("@aws-sdk/cloudfront-signer: Please provide 'url' or 'policy'.");
2923
+ if (policy) cloudfrontSignBuilder.setCustomPolicy(policy);
2924
+ else cloudfrontSignBuilder.setPolicyParameters({
2925
+ url,
2926
+ dateLessThan,
2927
+ dateGreaterThan,
2928
+ ipAddress
2929
+ });
2930
+ let baseUrl;
2931
+ if (url) baseUrl = url;
2932
+ else if (policy) {
2933
+ const resources = getPolicyResources(policy);
2934
+ if (!resources[0]) throw new Error("@aws-sdk/cloudfront-signer: No URL provided and unable to determine URL from first policy statement resource.");
2935
+ baseUrl = resources[0].replace("*://", "https://");
2936
+ }
2937
+ const newURL = new URL(baseUrl);
2938
+ newURL.search = Array.from(newURL.searchParams.entries()).concat(Object.entries(cloudfrontSignBuilder.createCloudfrontAttribute())).filter(([, value]) => value !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
2939
+ return getResource(newURL);
2940
+ }
2941
+ __name(getSignedUrl$1, "getSignedUrl");
2942
+ function getSignedCookies({ ipAddress, url, privateKey, keyPairId, dateLessThan, dateGreaterThan, policy, passphrase }) {
2943
+ const cloudfrontSignBuilder = new CloudfrontSignBuilder({
2944
+ keyPairId,
2945
+ privateKey,
2946
+ passphrase
2947
+ });
2948
+ if (policy) cloudfrontSignBuilder.setCustomPolicy(policy);
2949
+ else cloudfrontSignBuilder.setPolicyParameters({
2950
+ url,
2951
+ dateLessThan,
2952
+ dateGreaterThan,
2953
+ ipAddress
2954
+ });
2955
+ const cloudfrontCookieAttributes = cloudfrontSignBuilder.createCloudfrontAttribute();
2956
+ const cookies = {
2957
+ "CloudFront-Key-Pair-Id": cloudfrontCookieAttributes["Key-Pair-Id"],
2958
+ "CloudFront-Signature": cloudfrontCookieAttributes["Signature"]
2959
+ };
2960
+ if (cloudfrontCookieAttributes["Expires"]) cookies["CloudFront-Expires"] = cloudfrontCookieAttributes["Expires"];
2961
+ if (cloudfrontCookieAttributes["Policy"]) cookies["CloudFront-Policy"] = cloudfrontCookieAttributes["Policy"];
2962
+ return cookies;
2963
+ }
2964
+ __name(getSignedCookies, "getSignedCookies");
2965
+ function getPolicyResources(policy) {
2966
+ return ((typeof policy === "string" ? JSON.parse(policy) : policy)?.Statement ?? []).map((s) => s.Resource);
2967
+ }
2968
+ __name(getPolicyResources, "getPolicyResources");
2969
+ function getResource(url) {
2970
+ switch (url.protocol) {
2971
+ case "http:":
2972
+ case "https:":
2973
+ case "ws:":
2974
+ case "wss:": return url.toString();
2975
+ case "rtmp:": return url.pathname.replace(/^\//, "") + url.search + url.hash;
2976
+ default: throw new Error("Invalid URI scheme. Scheme must be one of http, https, or rtmp");
2977
+ }
2978
+ }
2979
+ __name(getResource, "getResource");
2980
+ var CloudfrontSignBuilder = class {
2981
+ static {
2982
+ __name(this, "CloudfrontSignBuilder");
2983
+ }
2984
+ keyPairId;
2985
+ privateKey;
2986
+ passphrase;
2987
+ policy;
2988
+ customPolicy = false;
2989
+ dateLessThan;
2990
+ constructor({ privateKey, keyPairId, passphrase }) {
2991
+ this.keyPairId = keyPairId;
2992
+ this.privateKey = privateKey;
2993
+ this.policy = "";
2994
+ this.passphrase = passphrase;
2995
+ }
2996
+ buildPolicy(args) {
2997
+ const policy = { Statement: [{
2998
+ Resource: args.resource,
2999
+ Condition: { DateLessThan: { "AWS:EpochTime": args.dateLessThan } }
3000
+ }] };
3001
+ if (args.dateGreaterThan) policy.Statement[0].Condition["DateGreaterThan"] = { "AWS:EpochTime": args.dateGreaterThan };
3002
+ if (args.ipAddress) {
3003
+ const cidr = this.parseCIDR(args.ipAddress);
3004
+ policy.Statement[0].Condition["IpAddress"] = { "AWS:SourceIp": cidr };
3005
+ }
3006
+ return policy;
3007
+ }
3008
+ normalizeBase64(str) {
3009
+ const replacements = {
3010
+ "+": "-",
3011
+ "=": "_",
3012
+ "/": "~"
3013
+ };
3014
+ return str.replace(/[+=/]/g, function(match) {
3015
+ return replacements[match];
3016
+ });
3017
+ }
3018
+ encodeToBase64(str) {
3019
+ return this.normalizeBase64(Buffer.from(str).toString("base64"));
3020
+ }
3021
+ validateIP(ipStr) {
3022
+ const octets = ipStr.split(".");
3023
+ if (octets.length !== 4) throw new Error(`IP does not contain four octets.`);
3024
+ if (!octets.every((octet) => {
3025
+ const num = Number(octet);
3026
+ return Number.isInteger(num) && num >= 0 && num <= 255;
3027
+ })) throw new Error("invalid IP octets");
3028
+ }
3029
+ validateMask(maskStr) {
3030
+ const mask = Number(maskStr);
3031
+ if (!(Number.isInteger(mask) && mask >= 0 && mask <= 32)) throw new Error("invalid mask");
3032
+ }
3033
+ parseCIDR(cidrStr) {
3034
+ try {
3035
+ const cidrParts = cidrStr.split("/");
3036
+ if (cidrParts.some((part) => part.length === 0)) throw new Error("missing ip or mask part of CIDR");
3037
+ this.validateIP(cidrParts[0]);
3038
+ let mask = "32";
3039
+ if (cidrParts.length === 2) {
3040
+ this.validateMask(cidrParts[1]);
3041
+ mask = cidrParts[1];
3042
+ }
3043
+ return `${cidrParts[0]}/${mask}`;
3044
+ } catch (error) {
3045
+ const errMessage = `IP address "${cidrStr}" is invalid`;
3046
+ if (error instanceof Error) throw new Error(`${errMessage} due to ${error.message}.`);
3047
+ else throw new Error(`${errMessage}.`);
3048
+ }
3049
+ }
3050
+ epochTime(date) {
3051
+ return Math.round(date.getTime() / 1e3);
3052
+ }
3053
+ parseDate(date) {
3054
+ if (!date) return;
3055
+ const parsedDate = new Date(date);
3056
+ return isNaN(parsedDate.getTime()) ? void 0 : this.epochTime(parsedDate);
3057
+ }
3058
+ parseDateWindow(expiration, start) {
3059
+ const dateLessThan = this.parseDate(expiration);
3060
+ if (!dateLessThan) throw new Error("dateLessThan is invalid. Ensure the date value is compatible with the Date constructor.");
3061
+ return {
3062
+ dateLessThan,
3063
+ dateGreaterThan: this.parseDate(start)
3064
+ };
3065
+ }
3066
+ signData(data, privateKey, passphrase) {
3067
+ const sign = (0, import_crypto.createSign)("RSA-SHA1");
3068
+ sign.update(data);
3069
+ return sign.sign({
3070
+ key: privateKey,
3071
+ passphrase
3072
+ }, "base64");
3073
+ }
3074
+ signPolicy(policy, privateKey, passphrase) {
3075
+ return this.normalizeBase64(this.signData(policy, privateKey, passphrase));
3076
+ }
3077
+ setCustomPolicy(policy) {
3078
+ this.customPolicy = true;
3079
+ this.policy = policy;
3080
+ }
3081
+ setPolicyParameters({ url, dateLessThan, dateGreaterThan, ipAddress }) {
3082
+ if (!url || !dateLessThan) return false;
3083
+ const resource = getResource(new URL(url));
3084
+ const parsedDates = this.parseDateWindow(dateLessThan, dateGreaterThan);
3085
+ this.dateLessThan = parsedDates.dateLessThan;
3086
+ this.customPolicy = Boolean(parsedDates.dateGreaterThan) || Boolean(ipAddress);
3087
+ this.policy = JSON.stringify(this.buildPolicy({
3088
+ resource,
3089
+ ipAddress,
3090
+ dateLessThan: parsedDates.dateLessThan,
3091
+ dateGreaterThan: parsedDates.dateGreaterThan
3092
+ }));
3093
+ }
3094
+ createCloudfrontAttribute() {
3095
+ if (!Boolean(this.policy)) throw new Error("Invalid policy");
3096
+ const signature = this.signPolicy(this.policy, this.privateKey, this.passphrase);
3097
+ return {
3098
+ Expires: this.customPolicy ? void 0 : this.dateLessThan,
3099
+ Policy: this.customPolicy ? this.encodeToBase64(this.policy) : void 0,
3100
+ "Key-Pair-Id": this.keyPairId,
3101
+ Signature: signature
3102
+ };
3103
+ }
3104
+ };
3105
+ }) });
3106
+
3112
3107
  //#endregion
3113
3108
  //#region lambda/withSignedUrl.ts
3114
3109
  var import_dist_cjs = /* @__PURE__ */ __toESM$1(require_dist_cjs(), 1);
@@ -3149,11 +3144,8 @@ const withSignedUrl = async ({ data, reqUrl, keyPairId, privateKey, expiresSecon
3149
3144
  const CLOUDFRONT_KEY_PAIR_ID = HotUpdater.CLOUDFRONT_KEY_PAIR_ID;
3150
3145
  const SSM_PARAMETER_NAME = HotUpdater.SSM_PARAMETER_NAME;
3151
3146
  const SSM_REGION = HotUpdater.SSM_REGION;
3147
+ const S3_BUCKET_NAME = HotUpdater.S3_BUCKET_NAME;
3152
3148
  let cachedPrivateKey = null;
3153
- /**
3154
- * Retrieves CloudFront private key from SSM Parameter Store
3155
- * Uses global cache to avoid repeated SSM calls on warm Lambda invocations
3156
- */
3157
3149
  async function getPrivateKey() {
3158
3150
  if (cachedPrivateKey !== null) return cachedPrivateKey;
3159
3151
  if (!SSM_REGION) throw new Error(`Invalid AWS region format: ${SSM_REGION}. Expected format like 'us-east-1' or 'ap-southeast-1'`);
@@ -3186,10 +3178,8 @@ const processDefaultValues = (channel, minBundleId) => ({
3186
3178
  actualChannel: channel === "default" ? "production" : channel,
3187
3179
  actualMinBundleId: minBundleId === "default" ? NIL_UUID : minBundleId
3188
3180
  });
3189
- const handleUpdateRequest = async (c, params, strategy, expiresSeconds) => {
3181
+ const handleUpdateRequest = async (c, params, strategy, expiresSeconds, cacheControl) => {
3190
3182
  try {
3191
- if (!c.req.header("host")) return c.json({ error: "Missing host header." }, 500);
3192
- const privateKey = await getPrivateKey();
3193
3183
  const updateConfig = {
3194
3184
  platform: params.platform,
3195
3185
  bundleId: params.bundleId,
@@ -3204,11 +3194,12 @@ const handleUpdateRequest = async (c, params, strategy, expiresSeconds) => {
3204
3194
  }
3205
3195
  };
3206
3196
  const updateInfo = await getUpdateInfo({
3207
- baseUrl: c.req.url,
3208
- keyPairId: CLOUDFRONT_KEY_PAIR_ID,
3209
- privateKey
3197
+ bucketName: S3_BUCKET_NAME,
3198
+ region: SSM_REGION
3210
3199
  }, updateConfig);
3200
+ if (cacheControl) c.header("Cache-Control", cacheControl);
3211
3201
  if (!updateInfo) return c.json(null);
3202
+ const privateKey = await getPrivateKey();
3212
3203
  const appUpdateInfo = await withSignedUrl({
3213
3204
  data: updateInfo,
3214
3205
  reqUrl: c.req.url,
@@ -3244,7 +3235,7 @@ app.get("/api/check-update", async (c) => {
3244
3235
  channel,
3245
3236
  minBundleId,
3246
3237
  ...fingerprintHash ? { fingerprintHash } : { appVersion }
3247
- }, fingerprintHash ? "fingerprint" : "appVersion", 60);
3238
+ }, fingerprintHash ? "fingerprint" : "appVersion", 60, NO_STORE_CACHE_CONTROL);
3248
3239
  } catch (error) {
3249
3240
  console.error("Legacy endpoint error:", error);
3250
3241
  return c.json({ error: "Internal Server Error" }, 500);
@@ -3270,7 +3261,7 @@ app.get("/api/check-update/app-version/:platform/:appVersion/:channel/:minBundle
3270
3261
  channel: actualChannel,
3271
3262
  minBundleId: actualMinBundleId,
3272
3263
  appVersion
3273
- }, "appVersion", 3600 * 24 * 365);
3264
+ }, "appVersion", ONE_YEAR_IN_SECONDS, SHARED_EDGE_CACHE_CONTROL);
3274
3265
  });
3275
3266
  app.get("/api/check-update/fingerprint/:platform/:fingerprintHash/:channel/:minBundleId/:bundleId", async (c) => {
3276
3267
  const { platform, fingerprintHash, channel, minBundleId, bundleId } = c.req.param();
@@ -3292,10 +3283,7 @@ app.get("/api/check-update/fingerprint/:platform/:fingerprintHash/:channel/:minB
3292
3283
  channel: actualChannel,
3293
3284
  minBundleId: actualMinBundleId,
3294
3285
  fingerprintHash
3295
- }, "fingerprint", 3600 * 24 * 365);
3296
- });
3297
- app.get("*", async (c) => {
3298
- return c.env.callback(null, c.env.request);
3286
+ }, "fingerprint", ONE_YEAR_IN_SECONDS, SHARED_EDGE_CACHE_CONTROL);
3299
3287
  });
3300
3288
  const handler = handle(app);
3301
3289
 
@@ -6,6 +6,7 @@ declare global {
6
6
  CLOUDFRONT_KEY_PAIR_ID: string;
7
7
  SSM_PARAMETER_NAME: string;
8
8
  SSM_REGION: string;
9
+ S3_BUCKET_NAME: string;
9
10
  };
10
11
  }
11
12
  declare const handler: CloudFrontRequestHandler;