@hot-updater/js 0.20.10 → 0.20.12
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 +81 -107
- package/dist/index.d.cts +32 -31
- package/dist/index.d.ts +32 -31
- package/dist/index.js +81 -107
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -419,8 +419,7 @@ var require_diff = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/semver
|
|
|
419
419
|
const highVersion = v1Higher ? v1 : v2;
|
|
420
420
|
const lowVersion = v1Higher ? v2 : v1;
|
|
421
421
|
const highHasPre = !!highVersion.prerelease.length;
|
|
422
|
-
|
|
423
|
-
if (lowHasPre && !highHasPre) {
|
|
422
|
+
if (!!lowVersion.prerelease.length && !highHasPre) {
|
|
424
423
|
if (!lowVersion.patch && !lowVersion.minor) return "major";
|
|
425
424
|
if (lowVersion.compareMain(highVersion) === 0) {
|
|
426
425
|
if (lowVersion.minor && !lowVersion.patch) return "minor";
|
|
@@ -628,11 +627,7 @@ var require_coerce = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/semv
|
|
|
628
627
|
}
|
|
629
628
|
if (match === null) return null;
|
|
630
629
|
const major$2 = match[2];
|
|
631
|
-
|
|
632
|
-
const patch$2 = match[4] || "0";
|
|
633
|
-
const prerelease$2 = options.includePrerelease && match[5] ? `-${match[5]}` : "";
|
|
634
|
-
const build = options.includePrerelease && match[6] ? `+${match[6]}` : "";
|
|
635
|
-
return parse$1(`${major$2}.${minor$2}.${patch$2}${prerelease$2}${build}`, options);
|
|
630
|
+
return parse$1(`${major$2}.${match[3] || "0"}.${match[4] || "0"}${options.includePrerelease && match[5] ? `-${match[5]}` : ""}${options.includePrerelease && match[6] ? `+${match[6]}` : ""}`, options);
|
|
636
631
|
};
|
|
637
632
|
module.exports = coerce$1;
|
|
638
633
|
}) });
|
|
@@ -647,7 +642,7 @@ var require_lrucache = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/se
|
|
|
647
642
|
}
|
|
648
643
|
get(key) {
|
|
649
644
|
const value = this.map.get(key);
|
|
650
|
-
if (value === void 0) return
|
|
645
|
+
if (value === void 0) return;
|
|
651
646
|
else {
|
|
652
647
|
this.map.delete(key);
|
|
653
648
|
this.map.set(key, value);
|
|
@@ -658,8 +653,7 @@ var require_lrucache = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/se
|
|
|
658
653
|
return this.map.delete(key);
|
|
659
654
|
}
|
|
660
655
|
set(key, value) {
|
|
661
|
-
|
|
662
|
-
if (!deleted && value !== void 0) {
|
|
656
|
+
if (!this.delete(key) && value !== void 0) {
|
|
663
657
|
if (this.map.size >= this.max) {
|
|
664
658
|
const firstKey = this.map.keys().next().value;
|
|
665
659
|
this.delete(firstKey);
|
|
@@ -727,8 +721,7 @@ var require_range = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/semve
|
|
|
727
721
|
return this.range;
|
|
728
722
|
}
|
|
729
723
|
parseRange(range) {
|
|
730
|
-
const
|
|
731
|
-
const memoKey = memoOpts + ":" + range;
|
|
724
|
+
const memoKey = ((this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | (this.options.loose && FLAG_LOOSE)) + ":" + range;
|
|
732
725
|
const cached = cache$1.get(memoKey);
|
|
733
726
|
if (cached) return cached;
|
|
734
727
|
const loose = this.options.loose;
|
|
@@ -782,8 +775,7 @@ var require_range = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/semve
|
|
|
782
775
|
}
|
|
783
776
|
};
|
|
784
777
|
module.exports = Range$11;
|
|
785
|
-
const
|
|
786
|
-
const cache$1 = new LRU();
|
|
778
|
+
const cache$1 = new (require_lrucache())();
|
|
787
779
|
const parseOptions$1 = require_parse_options();
|
|
788
780
|
const Comparator$4 = require_comparator();
|
|
789
781
|
const debug$1 = require_debug();
|
|
@@ -1242,16 +1234,13 @@ var require_simplify = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/se
|
|
|
1242
1234
|
let first = null;
|
|
1243
1235
|
let prev = null;
|
|
1244
1236
|
const v = versions.sort((a, b) => compare$2(a, b, options));
|
|
1245
|
-
for (const version of v) {
|
|
1246
|
-
|
|
1247
|
-
if (
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
prev = null;
|
|
1253
|
-
first = null;
|
|
1254
|
-
}
|
|
1237
|
+
for (const version of v) if (satisfies$2(version, range, options)) {
|
|
1238
|
+
prev = version;
|
|
1239
|
+
if (!first) first = version;
|
|
1240
|
+
} else {
|
|
1241
|
+
if (prev) set.push([first, prev]);
|
|
1242
|
+
prev = null;
|
|
1243
|
+
first = null;
|
|
1255
1244
|
}
|
|
1256
1245
|
if (first) set.push([first, null]);
|
|
1257
1246
|
const ranges = [];
|
|
@@ -1464,6 +1453,20 @@ const semverSatisfies = (targetAppVersion, currentVersion) => {
|
|
|
1464
1453
|
return import_semver.default.satisfies(currentCoerce.version, targetAppVersion);
|
|
1465
1454
|
};
|
|
1466
1455
|
|
|
1456
|
+
//#endregion
|
|
1457
|
+
//#region src/filterCompatibleAppVersions.ts
|
|
1458
|
+
/**
|
|
1459
|
+
* Filters target app versions that are compatible with the current app version.
|
|
1460
|
+
* Returns only versions that are compatible with the current version according to semver rules.
|
|
1461
|
+
*
|
|
1462
|
+
* @param targetAppVersionList - List of target app versions to filter
|
|
1463
|
+
* @param currentVersion - Current app version
|
|
1464
|
+
* @returns Array of target app versions compatible with the current version
|
|
1465
|
+
*/
|
|
1466
|
+
const filterCompatibleAppVersions = (targetAppVersionList, currentVersion) => {
|
|
1467
|
+
return targetAppVersionList.filter((version) => semverSatisfies(version, currentVersion)).sort((a, b) => b.localeCompare(a));
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1467
1470
|
//#endregion
|
|
1468
1471
|
//#region src/getUpdateInfo.ts
|
|
1469
1472
|
const INIT_BUNDLE_ROLLBACK_UPDATE_INFO = {
|
|
@@ -1500,7 +1503,7 @@ const appVersionStrategy = async (bundles, { channel = "production", minBundleId
|
|
|
1500
1503
|
let latestCandidate = null;
|
|
1501
1504
|
let updateCandidate = null;
|
|
1502
1505
|
let rollbackCandidate = null;
|
|
1503
|
-
let currentBundle
|
|
1506
|
+
let currentBundle;
|
|
1504
1507
|
for (const b of candidateBundles) {
|
|
1505
1508
|
if (!latestCandidate || b.id.localeCompare(latestCandidate.id) > 0) latestCandidate = b;
|
|
1506
1509
|
if (b.id === bundleId) currentBundle = b;
|
|
@@ -1538,7 +1541,7 @@ const fingerprintStrategy = async (bundles, { channel = "production", minBundleI
|
|
|
1538
1541
|
let latestCandidate = null;
|
|
1539
1542
|
let updateCandidate = null;
|
|
1540
1543
|
let rollbackCandidate = null;
|
|
1541
|
-
let currentBundle
|
|
1544
|
+
let currentBundle;
|
|
1542
1545
|
for (const b of candidateBundles) {
|
|
1543
1546
|
if (!latestCandidate || b.id.localeCompare(latestCandidate.id) > 0) latestCandidate = b;
|
|
1544
1547
|
if (b.id === bundleId) currentBundle = b;
|
|
@@ -1564,21 +1567,6 @@ const fingerprintStrategy = async (bundles, { channel = "production", minBundleI
|
|
|
1564
1567
|
return INIT_BUNDLE_ROLLBACK_UPDATE_INFO;
|
|
1565
1568
|
};
|
|
1566
1569
|
|
|
1567
|
-
//#endregion
|
|
1568
|
-
//#region src/filterCompatibleAppVersions.ts
|
|
1569
|
-
/**
|
|
1570
|
-
* Filters target app versions that are compatible with the current app version.
|
|
1571
|
-
* Returns only versions that are compatible with the current version according to semver rules.
|
|
1572
|
-
*
|
|
1573
|
-
* @param targetAppVersionList - List of target app versions to filter
|
|
1574
|
-
* @param currentVersion - Current app version
|
|
1575
|
-
* @returns Array of target app versions compatible with the current version
|
|
1576
|
-
*/
|
|
1577
|
-
const filterCompatibleAppVersions = (targetAppVersionList, currentVersion) => {
|
|
1578
|
-
const compatibleAppVersionList = targetAppVersionList.filter((version) => semverSatisfies(version, currentVersion));
|
|
1579
|
-
return compatibleAppVersionList.sort((a, b) => b.localeCompare(a));
|
|
1580
|
-
};
|
|
1581
|
-
|
|
1582
1570
|
//#endregion
|
|
1583
1571
|
//#region ../../node_modules/.pnpm/jose@6.0.10/node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
1584
1572
|
const encoder = new TextEncoder();
|
|
@@ -1741,8 +1729,7 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
1741
1729
|
case "HS512": {
|
|
1742
1730
|
if (!isAlgorithm(key.algorithm, "HMAC")) throw unusable("HMAC");
|
|
1743
1731
|
const expected = parseInt(alg.slice(2), 10);
|
|
1744
|
-
|
|
1745
|
-
if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1732
|
+
if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1746
1733
|
break;
|
|
1747
1734
|
}
|
|
1748
1735
|
case "RS256":
|
|
@@ -1750,8 +1737,7 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
1750
1737
|
case "RS512": {
|
|
1751
1738
|
if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5")) throw unusable("RSASSA-PKCS1-v1_5");
|
|
1752
1739
|
const expected = parseInt(alg.slice(2), 10);
|
|
1753
|
-
|
|
1754
|
-
if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1740
|
+
if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1755
1741
|
break;
|
|
1756
1742
|
}
|
|
1757
1743
|
case "PS256":
|
|
@@ -1759,8 +1745,7 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
1759
1745
|
case "PS512": {
|
|
1760
1746
|
if (!isAlgorithm(key.algorithm, "RSA-PSS")) throw unusable("RSA-PSS");
|
|
1761
1747
|
const expected = parseInt(alg.slice(2), 10);
|
|
1762
|
-
|
|
1763
|
-
if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1748
|
+
if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1764
1749
|
break;
|
|
1765
1750
|
}
|
|
1766
1751
|
case "Ed25519":
|
|
@@ -1772,8 +1757,7 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
1772
1757
|
case "ES512": {
|
|
1773
1758
|
if (!isAlgorithm(key.algorithm, "ECDSA")) throw unusable("ECDSA");
|
|
1774
1759
|
const expected = getNamedCurve(alg);
|
|
1775
|
-
|
|
1776
|
-
if (actual !== expected) throw unusable(expected, "algorithm.namedCurve");
|
|
1760
|
+
if (key.algorithm.namedCurve !== expected) throw unusable(expected, "algorithm.namedCurve");
|
|
1777
1761
|
break;
|
|
1778
1762
|
}
|
|
1779
1763
|
default: throw new TypeError("CryptoKey does not support this operation");
|
|
@@ -1987,7 +1971,7 @@ var validate_crit_default = (Err, recognizedDefault, recognizedOption, protected
|
|
|
1987
1971
|
//#region ../../node_modules/.pnpm/jose@6.0.10/node_modules/jose/dist/webapi/lib/validate_algorithms.js
|
|
1988
1972
|
var validate_algorithms_default = (option, algorithms) => {
|
|
1989
1973
|
if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== "string"))) throw new TypeError(`"${option}" option must be an array of strings`);
|
|
1990
|
-
if (!algorithms) return
|
|
1974
|
+
if (!algorithms) return;
|
|
1991
1975
|
return new Set(algorithms);
|
|
1992
1976
|
};
|
|
1993
1977
|
|
|
@@ -2076,12 +2060,11 @@ const handleKeyObject = (keyObject, alg) => {
|
|
|
2076
2060
|
}, extractable, [isPublic ? "verify" : "sign"]);
|
|
2077
2061
|
}
|
|
2078
2062
|
if (keyObject.asymmetricKeyType === "ec") {
|
|
2079
|
-
const
|
|
2063
|
+
const namedCurve = new Map([
|
|
2080
2064
|
["prime256v1", "P-256"],
|
|
2081
2065
|
["secp384r1", "P-384"],
|
|
2082
2066
|
["secp521r1", "P-521"]
|
|
2083
|
-
]);
|
|
2084
|
-
const namedCurve = nist.get(keyObject.asymmetricKeyDetails?.namedCurve);
|
|
2067
|
+
]).get(keyObject.asymmetricKeyDetails?.namedCurve);
|
|
2085
2068
|
if (!namedCurve) throw new TypeError("given KeyObject instance cannot be used for this algorithm");
|
|
2086
2069
|
if (alg === "ES256" && namedCurve === "P-256") cryptoKey = keyObject.toCryptoKey({
|
|
2087
2070
|
name: "ECDSA",
|
|
@@ -2115,8 +2098,7 @@ var normalize_key_default = async (key, alg) => {
|
|
|
2115
2098
|
} catch (err) {
|
|
2116
2099
|
if (err instanceof TypeError) throw err;
|
|
2117
2100
|
}
|
|
2118
|
-
|
|
2119
|
-
return handleJWK(key, jwk, alg);
|
|
2101
|
+
return handleJWK(key, key.export({ format: "jwk" }), alg);
|
|
2120
2102
|
}
|
|
2121
2103
|
if (isJWK(key)) {
|
|
2122
2104
|
if (key.k) return decode(key.k);
|
|
@@ -2204,8 +2186,7 @@ const asymmetricTypeCheck = (alg, key, usage) => {
|
|
|
2204
2186
|
}
|
|
2205
2187
|
};
|
|
2206
2188
|
var check_key_type_default = (alg, key, usage) => {
|
|
2207
|
-
|
|
2208
|
-
if (symmetric) symmetricTypeCheck(alg, key, usage);
|
|
2189
|
+
if (alg.startsWith("HS") || alg === "dir" || alg.startsWith("PBES2") || /^A(?:128|192|256)(?:GCM)?(?:KW)?$/.test(alg) || /^A(?:128|192|256)CBC-HS(?:256|384|512)$/.test(alg)) symmetricTypeCheck(alg, key, usage);
|
|
2209
2190
|
else asymmetricTypeCheck(alg, key, usage);
|
|
2210
2191
|
};
|
|
2211
2192
|
|
|
@@ -2321,8 +2302,7 @@ async function flattenedVerify(jws, key, options) {
|
|
|
2321
2302
|
throw new JWSInvalid("Failed to base64url decode the signature");
|
|
2322
2303
|
}
|
|
2323
2304
|
const k = await normalize_key_default(key, alg);
|
|
2324
|
-
|
|
2325
|
-
if (!verified) throw new JWSSignatureVerificationFailed();
|
|
2305
|
+
if (!await verify_default(alg, k, signature, data)) throw new JWSSignatureVerificationFailed();
|
|
2326
2306
|
let payload;
|
|
2327
2307
|
if (b64) try {
|
|
2328
2308
|
payload = decode(jws.payload);
|
|
@@ -2537,9 +2517,8 @@ var JWTClaimsBuilder = class {
|
|
|
2537
2517
|
async function jwtVerify(jwt, key, options) {
|
|
2538
2518
|
const verified = await compactVerify(jwt, key, options);
|
|
2539
2519
|
if (verified.protectedHeader.crit?.includes("b64") && verified.protectedHeader.b64 === false) throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
|
|
2540
|
-
const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);
|
|
2541
2520
|
const result = {
|
|
2542
|
-
payload,
|
|
2521
|
+
payload: validateClaimsSet(verified.protectedHeader, verified.payload, options),
|
|
2543
2522
|
protectedHeader: verified.protectedHeader
|
|
2544
2523
|
};
|
|
2545
2524
|
if (typeof key === "function") return {
|
|
@@ -2600,10 +2579,8 @@ var FlattenedSign = class {
|
|
|
2600
2579
|
if (this.#protectedHeader) protectedHeader = encoder.encode(encode(JSON.stringify(this.#protectedHeader)));
|
|
2601
2580
|
else protectedHeader = encoder.encode("");
|
|
2602
2581
|
const data = concat(protectedHeader, encoder.encode("."), payload);
|
|
2603
|
-
const k = await normalize_key_default(key, alg);
|
|
2604
|
-
const signature = await sign_default(alg, k, data);
|
|
2605
2582
|
const jws = {
|
|
2606
|
-
signature: encode(
|
|
2583
|
+
signature: encode(await sign_default(alg, await normalize_key_default(key, alg), data)),
|
|
2607
2584
|
payload: ""
|
|
2608
2585
|
};
|
|
2609
2586
|
if (b64) jws.payload = decoder.decode(payload);
|
|
@@ -2679,41 +2656,6 @@ var SignJWT = class {
|
|
|
2679
2656
|
}
|
|
2680
2657
|
};
|
|
2681
2658
|
|
|
2682
|
-
//#endregion
|
|
2683
|
-
//#region src/withJwtSignedUrl.ts
|
|
2684
|
-
/**
|
|
2685
|
-
* Creates a JWT-signed download URL based on the provided update information.
|
|
2686
|
-
*
|
|
2687
|
-
* @param {Object} options - Function options
|
|
2688
|
-
* @param {T|null} options.data - Update information (null if none)
|
|
2689
|
-
* @param {string} options.reqUrl - Request URL (base URL for token generation)
|
|
2690
|
-
* @param {string} options.jwtSecret - Secret key for JWT signing
|
|
2691
|
-
* @returns {Promise<T|null>} - Update response object with fileUrl or null
|
|
2692
|
-
*/
|
|
2693
|
-
const withJwtSignedUrl = async ({ data, reqUrl, jwtSecret }) => {
|
|
2694
|
-
if (!data) return null;
|
|
2695
|
-
const { storageUri,...rest } = data;
|
|
2696
|
-
if (data.id === __hot_updater_core.NIL_UUID || !storageUri) return {
|
|
2697
|
-
...rest,
|
|
2698
|
-
fileUrl: null
|
|
2699
|
-
};
|
|
2700
|
-
const storageUrl = new URL(storageUri);
|
|
2701
|
-
const key = `${storageUrl.host}${storageUrl.pathname}`;
|
|
2702
|
-
const token = await signToken(key, jwtSecret);
|
|
2703
|
-
const url = new URL(reqUrl);
|
|
2704
|
-
url.pathname = key;
|
|
2705
|
-
url.searchParams.set("token", token);
|
|
2706
|
-
return {
|
|
2707
|
-
...rest,
|
|
2708
|
-
fileUrl: url.toString()
|
|
2709
|
-
};
|
|
2710
|
-
};
|
|
2711
|
-
const signToken = async (key, jwtSecret) => {
|
|
2712
|
-
const secretKey = new TextEncoder().encode(jwtSecret);
|
|
2713
|
-
const token = await new SignJWT({ key }).setProtectedHeader({ alg: "HS256" }).setExpirationTime("60s").sign(secretKey);
|
|
2714
|
-
return token;
|
|
2715
|
-
};
|
|
2716
|
-
|
|
2717
2659
|
//#endregion
|
|
2718
2660
|
//#region src/verifyJwtSignedUrl.ts
|
|
2719
2661
|
/**
|
|
@@ -2726,8 +2668,7 @@ const verifyJwtToken = async ({ path, token, jwtSecret }) => {
|
|
|
2726
2668
|
error: "Missing token"
|
|
2727
2669
|
};
|
|
2728
2670
|
try {
|
|
2729
|
-
const
|
|
2730
|
-
const { payload } = await jwtVerify(token, secretKey);
|
|
2671
|
+
const { payload } = await jwtVerify(token, new TextEncoder().encode(jwtSecret));
|
|
2731
2672
|
if (!payload || payload.key !== key) return {
|
|
2732
2673
|
valid: false,
|
|
2733
2674
|
error: "Token does not match requested file"
|
|
@@ -2736,7 +2677,7 @@ const verifyJwtToken = async ({ path, token, jwtSecret }) => {
|
|
|
2736
2677
|
valid: true,
|
|
2737
2678
|
key
|
|
2738
2679
|
};
|
|
2739
|
-
} catch
|
|
2680
|
+
} catch {
|
|
2740
2681
|
return {
|
|
2741
2682
|
valid: false,
|
|
2742
2683
|
error: "Invalid or expired token"
|
|
@@ -2754,13 +2695,12 @@ const getFileResponse = async ({ key, handler }) => {
|
|
|
2754
2695
|
};
|
|
2755
2696
|
const pathParts = key.split("/");
|
|
2756
2697
|
const fileName = pathParts[pathParts.length - 1];
|
|
2757
|
-
const headers = {
|
|
2758
|
-
"Content-Type": object.contentType || "application/octet-stream",
|
|
2759
|
-
"Content-Disposition": `attachment; filename=${fileName}`
|
|
2760
|
-
};
|
|
2761
2698
|
return {
|
|
2762
2699
|
status: 200,
|
|
2763
|
-
responseHeaders:
|
|
2700
|
+
responseHeaders: {
|
|
2701
|
+
"Content-Type": object.contentType || "application/octet-stream",
|
|
2702
|
+
"Content-Disposition": `attachment; filename=${fileName}`
|
|
2703
|
+
},
|
|
2764
2704
|
responseBody: object.body
|
|
2765
2705
|
};
|
|
2766
2706
|
};
|
|
@@ -2791,6 +2731,40 @@ const verifyJwtSignedUrl = async ({ path, token, jwtSecret, handler }) => {
|
|
|
2791
2731
|
});
|
|
2792
2732
|
};
|
|
2793
2733
|
|
|
2734
|
+
//#endregion
|
|
2735
|
+
//#region src/withJwtSignedUrl.ts
|
|
2736
|
+
/**
|
|
2737
|
+
* Creates a JWT-signed download URL based on the provided update information.
|
|
2738
|
+
*
|
|
2739
|
+
* @param {Object} options - Function options
|
|
2740
|
+
* @param {T|null} options.data - Update information (null if none)
|
|
2741
|
+
* @param {string} options.reqUrl - Request URL (base URL for token generation)
|
|
2742
|
+
* @param {string} options.jwtSecret - Secret key for JWT signing
|
|
2743
|
+
* @returns {Promise<T|null>} - Update response object with fileUrl or null
|
|
2744
|
+
*/
|
|
2745
|
+
const withJwtSignedUrl = async ({ data, reqUrl, jwtSecret }) => {
|
|
2746
|
+
if (!data) return null;
|
|
2747
|
+
const { storageUri,...rest } = data;
|
|
2748
|
+
if (data.id === __hot_updater_core.NIL_UUID || !storageUri) return {
|
|
2749
|
+
...rest,
|
|
2750
|
+
fileUrl: null
|
|
2751
|
+
};
|
|
2752
|
+
const storageUrl = new URL(storageUri);
|
|
2753
|
+
const key = `${storageUrl.host}${storageUrl.pathname}`;
|
|
2754
|
+
const token = await signToken(key, jwtSecret);
|
|
2755
|
+
const url = new URL(reqUrl);
|
|
2756
|
+
url.pathname = key;
|
|
2757
|
+
url.searchParams.set("token", token);
|
|
2758
|
+
return {
|
|
2759
|
+
...rest,
|
|
2760
|
+
fileUrl: url.toString()
|
|
2761
|
+
};
|
|
2762
|
+
};
|
|
2763
|
+
const signToken = async (key, jwtSecret) => {
|
|
2764
|
+
const secretKey = new TextEncoder().encode(jwtSecret);
|
|
2765
|
+
return await new SignJWT({ key }).setProtectedHeader({ alg: "HS256" }).setExpirationTime("60s").sign(secretKey);
|
|
2766
|
+
};
|
|
2767
|
+
|
|
2794
2768
|
//#endregion
|
|
2795
2769
|
exports.filterCompatibleAppVersions = filterCompatibleAppVersions;
|
|
2796
2770
|
exports.getUpdateInfo = getUpdateInfo;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { Bundle, GetBundlesArgs, UpdateInfo } from "@hot-updater/core";
|
|
2
2
|
|
|
3
|
-
//#region src/getUpdateInfo.d.ts
|
|
4
|
-
declare const getUpdateInfo: (bundles: Bundle[], args: GetBundlesArgs) => Promise<UpdateInfo | null>;
|
|
5
|
-
//#endregion
|
|
6
|
-
//#region src/semverSatisfies.d.ts
|
|
7
|
-
declare const semverSatisfies: (targetAppVersion: string, currentVersion: string) => boolean;
|
|
8
|
-
//#endregion
|
|
9
3
|
//#region src/filterCompatibleAppVersions.d.ts
|
|
4
|
+
|
|
10
5
|
/**
|
|
11
6
|
* Filters target app versions that are compatible with the current app version.
|
|
12
7
|
* Returns only versions that are compatible with the current version according to semver rules.
|
|
@@ -17,31 +12,11 @@ declare const semverSatisfies: (targetAppVersion: string, currentVersion: string
|
|
|
17
12
|
*/
|
|
18
13
|
declare const filterCompatibleAppVersions: (targetAppVersionList: string[], currentVersion: string) => string[];
|
|
19
14
|
//#endregion
|
|
20
|
-
//#region src/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* @param {T|null} options.data - Update information (null if none)
|
|
26
|
-
* @param {string} options.reqUrl - Request URL (base URL for token generation)
|
|
27
|
-
* @param {string} options.jwtSecret - Secret key for JWT signing
|
|
28
|
-
* @returns {Promise<T|null>} - Update response object with fileUrl or null
|
|
29
|
-
*/
|
|
30
|
-
declare const withJwtSignedUrl: <T extends {
|
|
31
|
-
id: string;
|
|
32
|
-
storageUri: string | null;
|
|
33
|
-
}>({
|
|
34
|
-
data,
|
|
35
|
-
reqUrl,
|
|
36
|
-
jwtSecret
|
|
37
|
-
}: {
|
|
38
|
-
data: T | null;
|
|
39
|
-
reqUrl: string;
|
|
40
|
-
jwtSecret: string;
|
|
41
|
-
}) => Promise<(Omit<T, "storageUri"> & {
|
|
42
|
-
fileUrl: string | null;
|
|
43
|
-
}) | null>;
|
|
44
|
-
declare const signToken: (key: string, jwtSecret: string) => Promise<string>;
|
|
15
|
+
//#region src/getUpdateInfo.d.ts
|
|
16
|
+
declare const getUpdateInfo: (bundles: Bundle[], args: GetBundlesArgs) => Promise<UpdateInfo | null>;
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/semverSatisfies.d.ts
|
|
19
|
+
declare const semverSatisfies: (targetAppVersion: string, currentVersion: string) => boolean;
|
|
45
20
|
//#endregion
|
|
46
21
|
//#region src/verifyJwtSignedUrl.d.ts
|
|
47
22
|
type SuccessResponse = {
|
|
@@ -90,4 +65,30 @@ declare const verifyJwtSignedUrl: ({
|
|
|
90
65
|
} | null>;
|
|
91
66
|
}) => Promise<VerifyJwtSignedUrlResponse>;
|
|
92
67
|
//#endregion
|
|
68
|
+
//#region src/withJwtSignedUrl.d.ts
|
|
69
|
+
/**
|
|
70
|
+
* Creates a JWT-signed download URL based on the provided update information.
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} options - Function options
|
|
73
|
+
* @param {T|null} options.data - Update information (null if none)
|
|
74
|
+
* @param {string} options.reqUrl - Request URL (base URL for token generation)
|
|
75
|
+
* @param {string} options.jwtSecret - Secret key for JWT signing
|
|
76
|
+
* @returns {Promise<T|null>} - Update response object with fileUrl or null
|
|
77
|
+
*/
|
|
78
|
+
declare const withJwtSignedUrl: <T extends {
|
|
79
|
+
id: string;
|
|
80
|
+
storageUri: string | null;
|
|
81
|
+
}>({
|
|
82
|
+
data,
|
|
83
|
+
reqUrl,
|
|
84
|
+
jwtSecret
|
|
85
|
+
}: {
|
|
86
|
+
data: T | null;
|
|
87
|
+
reqUrl: string;
|
|
88
|
+
jwtSecret: string;
|
|
89
|
+
}) => Promise<(Omit<T, "storageUri"> & {
|
|
90
|
+
fileUrl: string | null;
|
|
91
|
+
}) | null>;
|
|
92
|
+
declare const signToken: (key: string, jwtSecret: string) => Promise<string>;
|
|
93
|
+
//#endregion
|
|
93
94
|
export { ErrorResponse, SuccessResponse, VerifyJwtSignedUrlResponse, filterCompatibleAppVersions, getUpdateInfo, semverSatisfies, signToken, verifyJwtSignedUrl, verifyJwtToken, withJwtSignedUrl };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { Bundle, GetBundlesArgs, UpdateInfo } from "@hot-updater/core";
|
|
2
2
|
|
|
3
|
-
//#region src/getUpdateInfo.d.ts
|
|
4
|
-
declare const getUpdateInfo: (bundles: Bundle[], args: GetBundlesArgs) => Promise<UpdateInfo | null>;
|
|
5
|
-
//#endregion
|
|
6
|
-
//#region src/semverSatisfies.d.ts
|
|
7
|
-
declare const semverSatisfies: (targetAppVersion: string, currentVersion: string) => boolean;
|
|
8
|
-
//#endregion
|
|
9
3
|
//#region src/filterCompatibleAppVersions.d.ts
|
|
4
|
+
|
|
10
5
|
/**
|
|
11
6
|
* Filters target app versions that are compatible with the current app version.
|
|
12
7
|
* Returns only versions that are compatible with the current version according to semver rules.
|
|
@@ -17,31 +12,11 @@ declare const semverSatisfies: (targetAppVersion: string, currentVersion: string
|
|
|
17
12
|
*/
|
|
18
13
|
declare const filterCompatibleAppVersions: (targetAppVersionList: string[], currentVersion: string) => string[];
|
|
19
14
|
//#endregion
|
|
20
|
-
//#region src/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* @param {T|null} options.data - Update information (null if none)
|
|
26
|
-
* @param {string} options.reqUrl - Request URL (base URL for token generation)
|
|
27
|
-
* @param {string} options.jwtSecret - Secret key for JWT signing
|
|
28
|
-
* @returns {Promise<T|null>} - Update response object with fileUrl or null
|
|
29
|
-
*/
|
|
30
|
-
declare const withJwtSignedUrl: <T extends {
|
|
31
|
-
id: string;
|
|
32
|
-
storageUri: string | null;
|
|
33
|
-
}>({
|
|
34
|
-
data,
|
|
35
|
-
reqUrl,
|
|
36
|
-
jwtSecret
|
|
37
|
-
}: {
|
|
38
|
-
data: T | null;
|
|
39
|
-
reqUrl: string;
|
|
40
|
-
jwtSecret: string;
|
|
41
|
-
}) => Promise<(Omit<T, "storageUri"> & {
|
|
42
|
-
fileUrl: string | null;
|
|
43
|
-
}) | null>;
|
|
44
|
-
declare const signToken: (key: string, jwtSecret: string) => Promise<string>;
|
|
15
|
+
//#region src/getUpdateInfo.d.ts
|
|
16
|
+
declare const getUpdateInfo: (bundles: Bundle[], args: GetBundlesArgs) => Promise<UpdateInfo | null>;
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/semverSatisfies.d.ts
|
|
19
|
+
declare const semverSatisfies: (targetAppVersion: string, currentVersion: string) => boolean;
|
|
45
20
|
//#endregion
|
|
46
21
|
//#region src/verifyJwtSignedUrl.d.ts
|
|
47
22
|
type SuccessResponse = {
|
|
@@ -90,4 +65,30 @@ declare const verifyJwtSignedUrl: ({
|
|
|
90
65
|
} | null>;
|
|
91
66
|
}) => Promise<VerifyJwtSignedUrlResponse>;
|
|
92
67
|
//#endregion
|
|
68
|
+
//#region src/withJwtSignedUrl.d.ts
|
|
69
|
+
/**
|
|
70
|
+
* Creates a JWT-signed download URL based on the provided update information.
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} options - Function options
|
|
73
|
+
* @param {T|null} options.data - Update information (null if none)
|
|
74
|
+
* @param {string} options.reqUrl - Request URL (base URL for token generation)
|
|
75
|
+
* @param {string} options.jwtSecret - Secret key for JWT signing
|
|
76
|
+
* @returns {Promise<T|null>} - Update response object with fileUrl or null
|
|
77
|
+
*/
|
|
78
|
+
declare const withJwtSignedUrl: <T extends {
|
|
79
|
+
id: string;
|
|
80
|
+
storageUri: string | null;
|
|
81
|
+
}>({
|
|
82
|
+
data,
|
|
83
|
+
reqUrl,
|
|
84
|
+
jwtSecret
|
|
85
|
+
}: {
|
|
86
|
+
data: T | null;
|
|
87
|
+
reqUrl: string;
|
|
88
|
+
jwtSecret: string;
|
|
89
|
+
}) => Promise<(Omit<T, "storageUri"> & {
|
|
90
|
+
fileUrl: string | null;
|
|
91
|
+
}) | null>;
|
|
92
|
+
declare const signToken: (key: string, jwtSecret: string) => Promise<string>;
|
|
93
|
+
//#endregion
|
|
93
94
|
export { ErrorResponse, SuccessResponse, VerifyJwtSignedUrlResponse, filterCompatibleAppVersions, getUpdateInfo, semverSatisfies, signToken, verifyJwtSignedUrl, verifyJwtToken, withJwtSignedUrl };
|
package/dist/index.js
CHANGED
|
@@ -418,8 +418,7 @@ var require_diff = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/semver
|
|
|
418
418
|
const highVersion = v1Higher ? v1 : v2;
|
|
419
419
|
const lowVersion = v1Higher ? v2 : v1;
|
|
420
420
|
const highHasPre = !!highVersion.prerelease.length;
|
|
421
|
-
|
|
422
|
-
if (lowHasPre && !highHasPre) {
|
|
421
|
+
if (!!lowVersion.prerelease.length && !highHasPre) {
|
|
423
422
|
if (!lowVersion.patch && !lowVersion.minor) return "major";
|
|
424
423
|
if (lowVersion.compareMain(highVersion) === 0) {
|
|
425
424
|
if (lowVersion.minor && !lowVersion.patch) return "minor";
|
|
@@ -627,11 +626,7 @@ var require_coerce = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/semv
|
|
|
627
626
|
}
|
|
628
627
|
if (match === null) return null;
|
|
629
628
|
const major$2 = match[2];
|
|
630
|
-
|
|
631
|
-
const patch$2 = match[4] || "0";
|
|
632
|
-
const prerelease$2 = options.includePrerelease && match[5] ? `-${match[5]}` : "";
|
|
633
|
-
const build = options.includePrerelease && match[6] ? `+${match[6]}` : "";
|
|
634
|
-
return parse$1(`${major$2}.${minor$2}.${patch$2}${prerelease$2}${build}`, options);
|
|
629
|
+
return parse$1(`${major$2}.${match[3] || "0"}.${match[4] || "0"}${options.includePrerelease && match[5] ? `-${match[5]}` : ""}${options.includePrerelease && match[6] ? `+${match[6]}` : ""}`, options);
|
|
635
630
|
};
|
|
636
631
|
module.exports = coerce$1;
|
|
637
632
|
}) });
|
|
@@ -646,7 +641,7 @@ var require_lrucache = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/se
|
|
|
646
641
|
}
|
|
647
642
|
get(key) {
|
|
648
643
|
const value = this.map.get(key);
|
|
649
|
-
if (value === void 0) return
|
|
644
|
+
if (value === void 0) return;
|
|
650
645
|
else {
|
|
651
646
|
this.map.delete(key);
|
|
652
647
|
this.map.set(key, value);
|
|
@@ -657,8 +652,7 @@ var require_lrucache = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/se
|
|
|
657
652
|
return this.map.delete(key);
|
|
658
653
|
}
|
|
659
654
|
set(key, value) {
|
|
660
|
-
|
|
661
|
-
if (!deleted && value !== void 0) {
|
|
655
|
+
if (!this.delete(key) && value !== void 0) {
|
|
662
656
|
if (this.map.size >= this.max) {
|
|
663
657
|
const firstKey = this.map.keys().next().value;
|
|
664
658
|
this.delete(firstKey);
|
|
@@ -726,8 +720,7 @@ var require_range = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/semve
|
|
|
726
720
|
return this.range;
|
|
727
721
|
}
|
|
728
722
|
parseRange(range) {
|
|
729
|
-
const
|
|
730
|
-
const memoKey = memoOpts + ":" + range;
|
|
723
|
+
const memoKey = ((this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | (this.options.loose && FLAG_LOOSE)) + ":" + range;
|
|
731
724
|
const cached = cache$1.get(memoKey);
|
|
732
725
|
if (cached) return cached;
|
|
733
726
|
const loose = this.options.loose;
|
|
@@ -781,8 +774,7 @@ var require_range = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/semve
|
|
|
781
774
|
}
|
|
782
775
|
};
|
|
783
776
|
module.exports = Range$11;
|
|
784
|
-
const
|
|
785
|
-
const cache$1 = new LRU();
|
|
777
|
+
const cache$1 = new (require_lrucache())();
|
|
786
778
|
const parseOptions$1 = require_parse_options();
|
|
787
779
|
const Comparator$4 = require_comparator();
|
|
788
780
|
const debug$1 = require_debug();
|
|
@@ -1241,16 +1233,13 @@ var require_simplify = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/se
|
|
|
1241
1233
|
let first = null;
|
|
1242
1234
|
let prev = null;
|
|
1243
1235
|
const v = versions.sort((a, b) => compare$2(a, b, options));
|
|
1244
|
-
for (const version of v) {
|
|
1245
|
-
|
|
1246
|
-
if (
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
prev = null;
|
|
1252
|
-
first = null;
|
|
1253
|
-
}
|
|
1236
|
+
for (const version of v) if (satisfies$2(version, range, options)) {
|
|
1237
|
+
prev = version;
|
|
1238
|
+
if (!first) first = version;
|
|
1239
|
+
} else {
|
|
1240
|
+
if (prev) set.push([first, prev]);
|
|
1241
|
+
prev = null;
|
|
1242
|
+
first = null;
|
|
1254
1243
|
}
|
|
1255
1244
|
if (first) set.push([first, null]);
|
|
1256
1245
|
const ranges = [];
|
|
@@ -1463,6 +1452,20 @@ const semverSatisfies = (targetAppVersion, currentVersion) => {
|
|
|
1463
1452
|
return import_semver.default.satisfies(currentCoerce.version, targetAppVersion);
|
|
1464
1453
|
};
|
|
1465
1454
|
|
|
1455
|
+
//#endregion
|
|
1456
|
+
//#region src/filterCompatibleAppVersions.ts
|
|
1457
|
+
/**
|
|
1458
|
+
* Filters target app versions that are compatible with the current app version.
|
|
1459
|
+
* Returns only versions that are compatible with the current version according to semver rules.
|
|
1460
|
+
*
|
|
1461
|
+
* @param targetAppVersionList - List of target app versions to filter
|
|
1462
|
+
* @param currentVersion - Current app version
|
|
1463
|
+
* @returns Array of target app versions compatible with the current version
|
|
1464
|
+
*/
|
|
1465
|
+
const filterCompatibleAppVersions = (targetAppVersionList, currentVersion) => {
|
|
1466
|
+
return targetAppVersionList.filter((version) => semverSatisfies(version, currentVersion)).sort((a, b) => b.localeCompare(a));
|
|
1467
|
+
};
|
|
1468
|
+
|
|
1466
1469
|
//#endregion
|
|
1467
1470
|
//#region src/getUpdateInfo.ts
|
|
1468
1471
|
const INIT_BUNDLE_ROLLBACK_UPDATE_INFO = {
|
|
@@ -1499,7 +1502,7 @@ const appVersionStrategy = async (bundles, { channel = "production", minBundleId
|
|
|
1499
1502
|
let latestCandidate = null;
|
|
1500
1503
|
let updateCandidate = null;
|
|
1501
1504
|
let rollbackCandidate = null;
|
|
1502
|
-
let currentBundle
|
|
1505
|
+
let currentBundle;
|
|
1503
1506
|
for (const b of candidateBundles) {
|
|
1504
1507
|
if (!latestCandidate || b.id.localeCompare(latestCandidate.id) > 0) latestCandidate = b;
|
|
1505
1508
|
if (b.id === bundleId) currentBundle = b;
|
|
@@ -1537,7 +1540,7 @@ const fingerprintStrategy = async (bundles, { channel = "production", minBundleI
|
|
|
1537
1540
|
let latestCandidate = null;
|
|
1538
1541
|
let updateCandidate = null;
|
|
1539
1542
|
let rollbackCandidate = null;
|
|
1540
|
-
let currentBundle
|
|
1543
|
+
let currentBundle;
|
|
1541
1544
|
for (const b of candidateBundles) {
|
|
1542
1545
|
if (!latestCandidate || b.id.localeCompare(latestCandidate.id) > 0) latestCandidate = b;
|
|
1543
1546
|
if (b.id === bundleId) currentBundle = b;
|
|
@@ -1563,21 +1566,6 @@ const fingerprintStrategy = async (bundles, { channel = "production", minBundleI
|
|
|
1563
1566
|
return INIT_BUNDLE_ROLLBACK_UPDATE_INFO;
|
|
1564
1567
|
};
|
|
1565
1568
|
|
|
1566
|
-
//#endregion
|
|
1567
|
-
//#region src/filterCompatibleAppVersions.ts
|
|
1568
|
-
/**
|
|
1569
|
-
* Filters target app versions that are compatible with the current app version.
|
|
1570
|
-
* Returns only versions that are compatible with the current version according to semver rules.
|
|
1571
|
-
*
|
|
1572
|
-
* @param targetAppVersionList - List of target app versions to filter
|
|
1573
|
-
* @param currentVersion - Current app version
|
|
1574
|
-
* @returns Array of target app versions compatible with the current version
|
|
1575
|
-
*/
|
|
1576
|
-
const filterCompatibleAppVersions = (targetAppVersionList, currentVersion) => {
|
|
1577
|
-
const compatibleAppVersionList = targetAppVersionList.filter((version) => semverSatisfies(version, currentVersion));
|
|
1578
|
-
return compatibleAppVersionList.sort((a, b) => b.localeCompare(a));
|
|
1579
|
-
};
|
|
1580
|
-
|
|
1581
1569
|
//#endregion
|
|
1582
1570
|
//#region ../../node_modules/.pnpm/jose@6.0.10/node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
1583
1571
|
const encoder = new TextEncoder();
|
|
@@ -1740,8 +1728,7 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
1740
1728
|
case "HS512": {
|
|
1741
1729
|
if (!isAlgorithm(key.algorithm, "HMAC")) throw unusable("HMAC");
|
|
1742
1730
|
const expected = parseInt(alg.slice(2), 10);
|
|
1743
|
-
|
|
1744
|
-
if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1731
|
+
if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1745
1732
|
break;
|
|
1746
1733
|
}
|
|
1747
1734
|
case "RS256":
|
|
@@ -1749,8 +1736,7 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
1749
1736
|
case "RS512": {
|
|
1750
1737
|
if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5")) throw unusable("RSASSA-PKCS1-v1_5");
|
|
1751
1738
|
const expected = parseInt(alg.slice(2), 10);
|
|
1752
|
-
|
|
1753
|
-
if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1739
|
+
if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1754
1740
|
break;
|
|
1755
1741
|
}
|
|
1756
1742
|
case "PS256":
|
|
@@ -1758,8 +1744,7 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
1758
1744
|
case "PS512": {
|
|
1759
1745
|
if (!isAlgorithm(key.algorithm, "RSA-PSS")) throw unusable("RSA-PSS");
|
|
1760
1746
|
const expected = parseInt(alg.slice(2), 10);
|
|
1761
|
-
|
|
1762
|
-
if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1747
|
+
if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
|
|
1763
1748
|
break;
|
|
1764
1749
|
}
|
|
1765
1750
|
case "Ed25519":
|
|
@@ -1771,8 +1756,7 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
1771
1756
|
case "ES512": {
|
|
1772
1757
|
if (!isAlgorithm(key.algorithm, "ECDSA")) throw unusable("ECDSA");
|
|
1773
1758
|
const expected = getNamedCurve(alg);
|
|
1774
|
-
|
|
1775
|
-
if (actual !== expected) throw unusable(expected, "algorithm.namedCurve");
|
|
1759
|
+
if (key.algorithm.namedCurve !== expected) throw unusable(expected, "algorithm.namedCurve");
|
|
1776
1760
|
break;
|
|
1777
1761
|
}
|
|
1778
1762
|
default: throw new TypeError("CryptoKey does not support this operation");
|
|
@@ -1986,7 +1970,7 @@ var validate_crit_default = (Err, recognizedDefault, recognizedOption, protected
|
|
|
1986
1970
|
//#region ../../node_modules/.pnpm/jose@6.0.10/node_modules/jose/dist/webapi/lib/validate_algorithms.js
|
|
1987
1971
|
var validate_algorithms_default = (option, algorithms) => {
|
|
1988
1972
|
if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== "string"))) throw new TypeError(`"${option}" option must be an array of strings`);
|
|
1989
|
-
if (!algorithms) return
|
|
1973
|
+
if (!algorithms) return;
|
|
1990
1974
|
return new Set(algorithms);
|
|
1991
1975
|
};
|
|
1992
1976
|
|
|
@@ -2075,12 +2059,11 @@ const handleKeyObject = (keyObject, alg) => {
|
|
|
2075
2059
|
}, extractable, [isPublic ? "verify" : "sign"]);
|
|
2076
2060
|
}
|
|
2077
2061
|
if (keyObject.asymmetricKeyType === "ec") {
|
|
2078
|
-
const
|
|
2062
|
+
const namedCurve = new Map([
|
|
2079
2063
|
["prime256v1", "P-256"],
|
|
2080
2064
|
["secp384r1", "P-384"],
|
|
2081
2065
|
["secp521r1", "P-521"]
|
|
2082
|
-
]);
|
|
2083
|
-
const namedCurve = nist.get(keyObject.asymmetricKeyDetails?.namedCurve);
|
|
2066
|
+
]).get(keyObject.asymmetricKeyDetails?.namedCurve);
|
|
2084
2067
|
if (!namedCurve) throw new TypeError("given KeyObject instance cannot be used for this algorithm");
|
|
2085
2068
|
if (alg === "ES256" && namedCurve === "P-256") cryptoKey = keyObject.toCryptoKey({
|
|
2086
2069
|
name: "ECDSA",
|
|
@@ -2114,8 +2097,7 @@ var normalize_key_default = async (key, alg) => {
|
|
|
2114
2097
|
} catch (err) {
|
|
2115
2098
|
if (err instanceof TypeError) throw err;
|
|
2116
2099
|
}
|
|
2117
|
-
|
|
2118
|
-
return handleJWK(key, jwk, alg);
|
|
2100
|
+
return handleJWK(key, key.export({ format: "jwk" }), alg);
|
|
2119
2101
|
}
|
|
2120
2102
|
if (isJWK(key)) {
|
|
2121
2103
|
if (key.k) return decode(key.k);
|
|
@@ -2203,8 +2185,7 @@ const asymmetricTypeCheck = (alg, key, usage) => {
|
|
|
2203
2185
|
}
|
|
2204
2186
|
};
|
|
2205
2187
|
var check_key_type_default = (alg, key, usage) => {
|
|
2206
|
-
|
|
2207
|
-
if (symmetric) symmetricTypeCheck(alg, key, usage);
|
|
2188
|
+
if (alg.startsWith("HS") || alg === "dir" || alg.startsWith("PBES2") || /^A(?:128|192|256)(?:GCM)?(?:KW)?$/.test(alg) || /^A(?:128|192|256)CBC-HS(?:256|384|512)$/.test(alg)) symmetricTypeCheck(alg, key, usage);
|
|
2208
2189
|
else asymmetricTypeCheck(alg, key, usage);
|
|
2209
2190
|
};
|
|
2210
2191
|
|
|
@@ -2320,8 +2301,7 @@ async function flattenedVerify(jws, key, options) {
|
|
|
2320
2301
|
throw new JWSInvalid("Failed to base64url decode the signature");
|
|
2321
2302
|
}
|
|
2322
2303
|
const k = await normalize_key_default(key, alg);
|
|
2323
|
-
|
|
2324
|
-
if (!verified) throw new JWSSignatureVerificationFailed();
|
|
2304
|
+
if (!await verify_default(alg, k, signature, data)) throw new JWSSignatureVerificationFailed();
|
|
2325
2305
|
let payload;
|
|
2326
2306
|
if (b64) try {
|
|
2327
2307
|
payload = decode(jws.payload);
|
|
@@ -2536,9 +2516,8 @@ var JWTClaimsBuilder = class {
|
|
|
2536
2516
|
async function jwtVerify(jwt, key, options) {
|
|
2537
2517
|
const verified = await compactVerify(jwt, key, options);
|
|
2538
2518
|
if (verified.protectedHeader.crit?.includes("b64") && verified.protectedHeader.b64 === false) throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
|
|
2539
|
-
const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);
|
|
2540
2519
|
const result = {
|
|
2541
|
-
payload,
|
|
2520
|
+
payload: validateClaimsSet(verified.protectedHeader, verified.payload, options),
|
|
2542
2521
|
protectedHeader: verified.protectedHeader
|
|
2543
2522
|
};
|
|
2544
2523
|
if (typeof key === "function") return {
|
|
@@ -2599,10 +2578,8 @@ var FlattenedSign = class {
|
|
|
2599
2578
|
if (this.#protectedHeader) protectedHeader = encoder.encode(encode(JSON.stringify(this.#protectedHeader)));
|
|
2600
2579
|
else protectedHeader = encoder.encode("");
|
|
2601
2580
|
const data = concat(protectedHeader, encoder.encode("."), payload);
|
|
2602
|
-
const k = await normalize_key_default(key, alg);
|
|
2603
|
-
const signature = await sign_default(alg, k, data);
|
|
2604
2581
|
const jws = {
|
|
2605
|
-
signature: encode(
|
|
2582
|
+
signature: encode(await sign_default(alg, await normalize_key_default(key, alg), data)),
|
|
2606
2583
|
payload: ""
|
|
2607
2584
|
};
|
|
2608
2585
|
if (b64) jws.payload = decoder.decode(payload);
|
|
@@ -2678,41 +2655,6 @@ var SignJWT = class {
|
|
|
2678
2655
|
}
|
|
2679
2656
|
};
|
|
2680
2657
|
|
|
2681
|
-
//#endregion
|
|
2682
|
-
//#region src/withJwtSignedUrl.ts
|
|
2683
|
-
/**
|
|
2684
|
-
* Creates a JWT-signed download URL based on the provided update information.
|
|
2685
|
-
*
|
|
2686
|
-
* @param {Object} options - Function options
|
|
2687
|
-
* @param {T|null} options.data - Update information (null if none)
|
|
2688
|
-
* @param {string} options.reqUrl - Request URL (base URL for token generation)
|
|
2689
|
-
* @param {string} options.jwtSecret - Secret key for JWT signing
|
|
2690
|
-
* @returns {Promise<T|null>} - Update response object with fileUrl or null
|
|
2691
|
-
*/
|
|
2692
|
-
const withJwtSignedUrl = async ({ data, reqUrl, jwtSecret }) => {
|
|
2693
|
-
if (!data) return null;
|
|
2694
|
-
const { storageUri,...rest } = data;
|
|
2695
|
-
if (data.id === NIL_UUID || !storageUri) return {
|
|
2696
|
-
...rest,
|
|
2697
|
-
fileUrl: null
|
|
2698
|
-
};
|
|
2699
|
-
const storageUrl = new URL(storageUri);
|
|
2700
|
-
const key = `${storageUrl.host}${storageUrl.pathname}`;
|
|
2701
|
-
const token = await signToken(key, jwtSecret);
|
|
2702
|
-
const url = new URL(reqUrl);
|
|
2703
|
-
url.pathname = key;
|
|
2704
|
-
url.searchParams.set("token", token);
|
|
2705
|
-
return {
|
|
2706
|
-
...rest,
|
|
2707
|
-
fileUrl: url.toString()
|
|
2708
|
-
};
|
|
2709
|
-
};
|
|
2710
|
-
const signToken = async (key, jwtSecret) => {
|
|
2711
|
-
const secretKey = new TextEncoder().encode(jwtSecret);
|
|
2712
|
-
const token = await new SignJWT({ key }).setProtectedHeader({ alg: "HS256" }).setExpirationTime("60s").sign(secretKey);
|
|
2713
|
-
return token;
|
|
2714
|
-
};
|
|
2715
|
-
|
|
2716
2658
|
//#endregion
|
|
2717
2659
|
//#region src/verifyJwtSignedUrl.ts
|
|
2718
2660
|
/**
|
|
@@ -2725,8 +2667,7 @@ const verifyJwtToken = async ({ path, token, jwtSecret }) => {
|
|
|
2725
2667
|
error: "Missing token"
|
|
2726
2668
|
};
|
|
2727
2669
|
try {
|
|
2728
|
-
const
|
|
2729
|
-
const { payload } = await jwtVerify(token, secretKey);
|
|
2670
|
+
const { payload } = await jwtVerify(token, new TextEncoder().encode(jwtSecret));
|
|
2730
2671
|
if (!payload || payload.key !== key) return {
|
|
2731
2672
|
valid: false,
|
|
2732
2673
|
error: "Token does not match requested file"
|
|
@@ -2735,7 +2676,7 @@ const verifyJwtToken = async ({ path, token, jwtSecret }) => {
|
|
|
2735
2676
|
valid: true,
|
|
2736
2677
|
key
|
|
2737
2678
|
};
|
|
2738
|
-
} catch
|
|
2679
|
+
} catch {
|
|
2739
2680
|
return {
|
|
2740
2681
|
valid: false,
|
|
2741
2682
|
error: "Invalid or expired token"
|
|
@@ -2753,13 +2694,12 @@ const getFileResponse = async ({ key, handler }) => {
|
|
|
2753
2694
|
};
|
|
2754
2695
|
const pathParts = key.split("/");
|
|
2755
2696
|
const fileName = pathParts[pathParts.length - 1];
|
|
2756
|
-
const headers = {
|
|
2757
|
-
"Content-Type": object.contentType || "application/octet-stream",
|
|
2758
|
-
"Content-Disposition": `attachment; filename=${fileName}`
|
|
2759
|
-
};
|
|
2760
2697
|
return {
|
|
2761
2698
|
status: 200,
|
|
2762
|
-
responseHeaders:
|
|
2699
|
+
responseHeaders: {
|
|
2700
|
+
"Content-Type": object.contentType || "application/octet-stream",
|
|
2701
|
+
"Content-Disposition": `attachment; filename=${fileName}`
|
|
2702
|
+
},
|
|
2763
2703
|
responseBody: object.body
|
|
2764
2704
|
};
|
|
2765
2705
|
};
|
|
@@ -2790,5 +2730,39 @@ const verifyJwtSignedUrl = async ({ path, token, jwtSecret, handler }) => {
|
|
|
2790
2730
|
});
|
|
2791
2731
|
};
|
|
2792
2732
|
|
|
2733
|
+
//#endregion
|
|
2734
|
+
//#region src/withJwtSignedUrl.ts
|
|
2735
|
+
/**
|
|
2736
|
+
* Creates a JWT-signed download URL based on the provided update information.
|
|
2737
|
+
*
|
|
2738
|
+
* @param {Object} options - Function options
|
|
2739
|
+
* @param {T|null} options.data - Update information (null if none)
|
|
2740
|
+
* @param {string} options.reqUrl - Request URL (base URL for token generation)
|
|
2741
|
+
* @param {string} options.jwtSecret - Secret key for JWT signing
|
|
2742
|
+
* @returns {Promise<T|null>} - Update response object with fileUrl or null
|
|
2743
|
+
*/
|
|
2744
|
+
const withJwtSignedUrl = async ({ data, reqUrl, jwtSecret }) => {
|
|
2745
|
+
if (!data) return null;
|
|
2746
|
+
const { storageUri,...rest } = data;
|
|
2747
|
+
if (data.id === NIL_UUID || !storageUri) return {
|
|
2748
|
+
...rest,
|
|
2749
|
+
fileUrl: null
|
|
2750
|
+
};
|
|
2751
|
+
const storageUrl = new URL(storageUri);
|
|
2752
|
+
const key = `${storageUrl.host}${storageUrl.pathname}`;
|
|
2753
|
+
const token = await signToken(key, jwtSecret);
|
|
2754
|
+
const url = new URL(reqUrl);
|
|
2755
|
+
url.pathname = key;
|
|
2756
|
+
url.searchParams.set("token", token);
|
|
2757
|
+
return {
|
|
2758
|
+
...rest,
|
|
2759
|
+
fileUrl: url.toString()
|
|
2760
|
+
};
|
|
2761
|
+
};
|
|
2762
|
+
const signToken = async (key, jwtSecret) => {
|
|
2763
|
+
const secretKey = new TextEncoder().encode(jwtSecret);
|
|
2764
|
+
return await new SignJWT({ key }).setProtectedHeader({ alg: "HS256" }).setExpirationTime("60s").sign(secretKey);
|
|
2765
|
+
};
|
|
2766
|
+
|
|
2793
2767
|
//#endregion
|
|
2794
2768
|
export { filterCompatibleAppVersions, getUpdateInfo, semverSatisfies, signToken, verifyJwtSignedUrl, verifyJwtToken, withJwtSignedUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/js",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@hot-updater/core": "0.20.
|
|
41
|
+
"@hot-updater/core": "0.20.12"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^20",
|