@frontmcp/utils 1.9.1 → 1.9.3
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/crypto/browser.d.ts +5 -0
- package/crypto/browser.js +33 -5
- package/crypto/node.d.ts +1 -1
- package/env/browser-env.d.ts +6 -0
- package/env/browser-env.js +12 -3
- package/env/browser-runtime-context.d.ts +4 -0
- package/env/browser-runtime-context.js +29 -3
- package/esm/crypto/browser.mjs +27 -4
- package/esm/env/browser-env.mjs +12 -3
- package/esm/env/browser-runtime-context.mjs +29 -3
- package/esm/index.mjs +196 -223
- package/index.d.ts +2 -2
- package/index.js +201 -225
- package/package.json +6 -2
- package/storage/adapters/index.d.ts +1 -1
- package/storage/adapters/redis.d.ts +10 -0
- package/storage/adapters/vercel-kv.d.ts +41 -0
- package/storage/index.d.ts +5 -3
- package/storage/redis-url.d.ts +50 -0
- package/storage/types.d.ts +31 -9
package/index.js
CHANGED
|
@@ -2541,180 +2541,19 @@ var init_key_persistence2 = __esm({
|
|
|
2541
2541
|
}
|
|
2542
2542
|
});
|
|
2543
2543
|
|
|
2544
|
-
// libs/utils/src/crypto/node.ts
|
|
2545
|
-
var node_exports = {};
|
|
2546
|
-
__export(node_exports, {
|
|
2547
|
-
createSignedJwt: () => createSignedJwt,
|
|
2548
|
-
cryptoProvider: () => nodeCrypto,
|
|
2549
|
-
generateRsaKeyPair: () => generateRsaKeyPair,
|
|
2550
|
-
isRsaPssAlg: () => isRsaPssAlg,
|
|
2551
|
-
jwtAlgToNodeAlg: () => jwtAlgToNodeAlg,
|
|
2552
|
-
nodeCrypto: () => nodeCrypto,
|
|
2553
|
-
pemToPublicJwk: () => pemToPublicJwk,
|
|
2554
|
-
rsaSign: () => rsaSign,
|
|
2555
|
-
rsaSignBase64Url: () => rsaSignBase64Url,
|
|
2556
|
-
rsaVerify: () => rsaVerify,
|
|
2557
|
-
rsaVerifySync: () => rsaVerifySync
|
|
2558
|
-
});
|
|
2559
|
-
function toUint8Array(buf) {
|
|
2560
|
-
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
2561
|
-
}
|
|
2562
|
-
function toBuffer(data) {
|
|
2563
|
-
if (typeof data === "string") {
|
|
2564
|
-
return Buffer.from(data, "utf8");
|
|
2565
|
-
}
|
|
2566
|
-
return Buffer.from(data);
|
|
2567
|
-
}
|
|
2568
|
-
function generateRsaKeyPair(modulusLength = 2048, alg = "RS256") {
|
|
2569
|
-
const kid = `rsa-key-${Date.now()}-${import_node_crypto.default.randomBytes(8).toString("hex")}`;
|
|
2570
|
-
const { privateKey, publicKey } = import_node_crypto.default.generateKeyPairSync("rsa", {
|
|
2571
|
-
modulusLength
|
|
2572
|
-
});
|
|
2573
|
-
const exported = publicKey.export({ format: "jwk" });
|
|
2574
|
-
const publicJwk = {
|
|
2575
|
-
...exported,
|
|
2576
|
-
kid,
|
|
2577
|
-
alg,
|
|
2578
|
-
use: "sig",
|
|
2579
|
-
kty: "RSA"
|
|
2580
|
-
};
|
|
2581
|
-
return { privateKey, publicKey, publicJwk };
|
|
2582
|
-
}
|
|
2583
|
-
function rsaSign(algorithm, data, privateKey, options) {
|
|
2584
|
-
const signingKey = options ? { key: privateKey, ...options } : privateKey;
|
|
2585
|
-
return import_node_crypto.default.sign(algorithm, data, signingKey);
|
|
2586
|
-
}
|
|
2587
|
-
function rsaVerify(jwtAlg, data, publicJwk, signature) {
|
|
2588
|
-
const publicKey = import_node_crypto.default.createPublicKey({ key: publicJwk, format: "jwk" });
|
|
2589
|
-
const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
|
|
2590
|
-
const verifyKey = isRsaPssAlg(jwtAlg) ? {
|
|
2591
|
-
key: publicKey,
|
|
2592
|
-
padding: import_node_crypto.default.constants.RSA_PKCS1_PSS_PADDING,
|
|
2593
|
-
saltLength: import_node_crypto.default.constants.RSA_PSS_SALTLEN_DIGEST
|
|
2594
|
-
} : publicKey;
|
|
2595
|
-
return import_node_crypto.default.verify(nodeAlgorithm, data, verifyKey, signature);
|
|
2596
|
-
}
|
|
2597
|
-
function rsaSignBase64Url(jwtAlg, data, privateJwk) {
|
|
2598
|
-
const privateKey = import_node_crypto.default.createPrivateKey({ key: privateJwk, format: "jwk" });
|
|
2599
|
-
const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
|
|
2600
|
-
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
2601
|
-
const sig = isRsaPssAlg(jwtAlg) ? rsaSign(nodeAlgorithm, buf, privateKey, {
|
|
2602
|
-
padding: import_node_crypto.default.constants.RSA_PKCS1_PSS_PADDING,
|
|
2603
|
-
saltLength: import_node_crypto.default.constants.RSA_PSS_SALTLEN_DIGEST
|
|
2604
|
-
}) : rsaSign(nodeAlgorithm, buf, privateKey);
|
|
2605
|
-
return sig.toString("base64url");
|
|
2606
|
-
}
|
|
2607
|
-
function rsaVerifySync(jwtAlg, data, publicJwk, signature) {
|
|
2608
|
-
try {
|
|
2609
|
-
const publicKey = import_node_crypto.default.createPublicKey({ key: publicJwk, format: "jwk" });
|
|
2610
|
-
const dataBuf = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
2611
|
-
const sigBuf = Buffer.isBuffer(signature) ? signature : Buffer.from(signature);
|
|
2612
|
-
if (jwtAlg === "EdDSA") {
|
|
2613
|
-
return import_node_crypto.default.verify(null, dataBuf, publicKey, sigBuf);
|
|
2614
|
-
}
|
|
2615
|
-
const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
|
|
2616
|
-
const verifyKey = isRsaPssAlg(jwtAlg) ? {
|
|
2617
|
-
key: publicKey,
|
|
2618
|
-
padding: import_node_crypto.default.constants.RSA_PKCS1_PSS_PADDING,
|
|
2619
|
-
saltLength: import_node_crypto.default.constants.RSA_PSS_SALTLEN_DIGEST
|
|
2620
|
-
} : publicKey;
|
|
2621
|
-
return import_node_crypto.default.verify(nodeAlgorithm, dataBuf, verifyKey, sigBuf);
|
|
2622
|
-
} catch {
|
|
2623
|
-
return false;
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2626
|
-
function pemToPublicJwk(pem) {
|
|
2627
|
-
const key = import_node_crypto.default.createPublicKey({ key: pem, format: "pem" });
|
|
2628
|
-
return key.export({ format: "jwk" });
|
|
2629
|
-
}
|
|
2630
|
-
function createSignedJwt(payload, privateKey, kid, alg = "RS256") {
|
|
2631
|
-
const header = { alg, typ: "JWT", kid };
|
|
2632
|
-
const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
|
|
2633
|
-
const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
2634
|
-
const signatureInput = `${headerB64}.${payloadB64}`;
|
|
2635
|
-
const nodeAlgorithm = jwtAlgToNodeAlg(alg);
|
|
2636
|
-
const signature = rsaSign(
|
|
2637
|
-
nodeAlgorithm,
|
|
2638
|
-
Buffer.from(signatureInput),
|
|
2639
|
-
privateKey,
|
|
2640
|
-
isRsaPssAlg(alg) ? {
|
|
2641
|
-
padding: import_node_crypto.default.constants.RSA_PKCS1_PSS_PADDING,
|
|
2642
|
-
saltLength: import_node_crypto.default.constants.RSA_PSS_SALTLEN_DIGEST
|
|
2643
|
-
} : void 0
|
|
2644
|
-
);
|
|
2645
|
-
const signatureB64 = signature.toString("base64url");
|
|
2646
|
-
return `${headerB64}.${payloadB64}.${signatureB64}`;
|
|
2647
|
-
}
|
|
2648
|
-
var import_node_crypto, nodeCrypto;
|
|
2649
|
-
var init_node = __esm({
|
|
2650
|
-
"libs/utils/src/crypto/node.ts"() {
|
|
2651
|
-
"use strict";
|
|
2652
|
-
import_node_crypto = __toESM(require("node:crypto"));
|
|
2653
|
-
init_jwt_alg();
|
|
2654
|
-
init_jwt_alg();
|
|
2655
|
-
nodeCrypto = {
|
|
2656
|
-
randomUUID() {
|
|
2657
|
-
return import_node_crypto.default.randomUUID();
|
|
2658
|
-
},
|
|
2659
|
-
randomBytes(length) {
|
|
2660
|
-
return toUint8Array(import_node_crypto.default.randomBytes(length));
|
|
2661
|
-
},
|
|
2662
|
-
sha256(data) {
|
|
2663
|
-
const hash = import_node_crypto.default.createHash("sha256").update(toBuffer(data)).digest();
|
|
2664
|
-
return toUint8Array(hash);
|
|
2665
|
-
},
|
|
2666
|
-
sha256Hex(data) {
|
|
2667
|
-
return import_node_crypto.default.createHash("sha256").update(toBuffer(data)).digest("hex");
|
|
2668
|
-
},
|
|
2669
|
-
hmacSha256(key, data) {
|
|
2670
|
-
const hmac2 = import_node_crypto.default.createHmac("sha256", Buffer.from(key)).update(Buffer.from(data)).digest();
|
|
2671
|
-
return toUint8Array(hmac2);
|
|
2672
|
-
},
|
|
2673
|
-
hkdfSha256(ikm, salt, info, length) {
|
|
2674
|
-
const ikmBuf = Buffer.from(ikm);
|
|
2675
|
-
const saltBuf = salt.length > 0 ? Buffer.from(salt) : Buffer.alloc(32);
|
|
2676
|
-
const prk = import_node_crypto.default.createHmac("sha256", saltBuf).update(ikmBuf).digest();
|
|
2677
|
-
const hashLen = 32;
|
|
2678
|
-
const n = Math.ceil(length / hashLen);
|
|
2679
|
-
const chunks = [];
|
|
2680
|
-
let prev = Buffer.alloc(0);
|
|
2681
|
-
for (let i = 1; i <= n; i++) {
|
|
2682
|
-
prev = import_node_crypto.default.createHmac("sha256", prk).update(Buffer.concat([prev, Buffer.from(info), Buffer.from([i])])).digest();
|
|
2683
|
-
chunks.push(prev);
|
|
2684
|
-
}
|
|
2685
|
-
return toUint8Array(Buffer.concat(chunks).subarray(0, length));
|
|
2686
|
-
},
|
|
2687
|
-
encryptAesGcm(key, plaintext, iv) {
|
|
2688
|
-
const cipher = import_node_crypto.default.createCipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
|
|
2689
|
-
const encrypted = Buffer.concat([cipher.update(Buffer.from(plaintext)), cipher.final()]);
|
|
2690
|
-
const tag = cipher.getAuthTag();
|
|
2691
|
-
return {
|
|
2692
|
-
ciphertext: toUint8Array(encrypted),
|
|
2693
|
-
tag: toUint8Array(tag)
|
|
2694
|
-
};
|
|
2695
|
-
},
|
|
2696
|
-
decryptAesGcm(key, ciphertext, iv, tag) {
|
|
2697
|
-
const decipher = import_node_crypto.default.createDecipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
|
|
2698
|
-
decipher.setAuthTag(Buffer.from(tag));
|
|
2699
|
-
const decrypted = Buffer.concat([decipher.update(Buffer.from(ciphertext)), decipher.final()]);
|
|
2700
|
-
return toUint8Array(decrypted);
|
|
2701
|
-
},
|
|
2702
|
-
timingSafeEqual(a, b) {
|
|
2703
|
-
if (a.length !== b.length) return false;
|
|
2704
|
-
return import_node_crypto.default.timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
2705
|
-
}
|
|
2706
|
-
};
|
|
2707
|
-
}
|
|
2708
|
-
});
|
|
2709
|
-
|
|
2710
2544
|
// libs/utils/src/crypto/browser.ts
|
|
2711
2545
|
var browser_exports = {};
|
|
2712
2546
|
__export(browser_exports, {
|
|
2713
2547
|
browserCrypto: () => browserCrypto,
|
|
2714
2548
|
cryptoProvider: () => browserCrypto,
|
|
2549
|
+
generateRsaKeyPair: () => generateRsaKeyPair,
|
|
2715
2550
|
isRsaPssAlg: () => isRsaPssAlg,
|
|
2716
2551
|
jwtAlgToWebCryptoAlg: () => jwtAlgToWebCryptoAlg,
|
|
2717
|
-
|
|
2552
|
+
pemToPublicJwk: () => pemToPublicJwk,
|
|
2553
|
+
rsaSignBase64Url: () => rsaSignBase64Url,
|
|
2554
|
+
rsaVerify: () => rsaVerify,
|
|
2555
|
+
rsaVerifyBrowser: () => rsaVerifyBrowser,
|
|
2556
|
+
rsaVerifySync: () => rsaVerifySync
|
|
2718
2557
|
});
|
|
2719
2558
|
function toBytes(data) {
|
|
2720
2559
|
if (typeof data === "string") {
|
|
@@ -2743,6 +2582,24 @@ function constantTimeEqual(a, b) {
|
|
|
2743
2582
|
}
|
|
2744
2583
|
return result === 0;
|
|
2745
2584
|
}
|
|
2585
|
+
function nodeOnly(name) {
|
|
2586
|
+
throw new Error(`${name} is only available in Node.js runtimes`);
|
|
2587
|
+
}
|
|
2588
|
+
function rsaVerify() {
|
|
2589
|
+
return nodeOnly("rsaVerify");
|
|
2590
|
+
}
|
|
2591
|
+
function rsaSignBase64Url() {
|
|
2592
|
+
return nodeOnly("rsaSignBase64Url");
|
|
2593
|
+
}
|
|
2594
|
+
function rsaVerifySync() {
|
|
2595
|
+
return nodeOnly("rsaVerifySync");
|
|
2596
|
+
}
|
|
2597
|
+
function pemToPublicJwk() {
|
|
2598
|
+
return nodeOnly("pemToPublicJwk");
|
|
2599
|
+
}
|
|
2600
|
+
function generateRsaKeyPair() {
|
|
2601
|
+
return nodeOnly("generateRsaKeyPair");
|
|
2602
|
+
}
|
|
2746
2603
|
async function rsaVerifyBrowser(jwtAlg, data, publicJwk, signature) {
|
|
2747
2604
|
if (typeof globalThis.crypto?.subtle === "undefined") {
|
|
2748
2605
|
throw new Error("WebCrypto API (crypto.subtle) is not available in this environment");
|
|
@@ -2774,15 +2631,15 @@ function getSaltLength(jwtAlg) {
|
|
|
2774
2631
|
return 32;
|
|
2775
2632
|
}
|
|
2776
2633
|
}
|
|
2777
|
-
var
|
|
2634
|
+
var import_aes, import_hkdf, import_hmac, import_sha2, import_utils, browserCrypto;
|
|
2778
2635
|
var init_browser = __esm({
|
|
2779
2636
|
"libs/utils/src/crypto/browser.ts"() {
|
|
2780
2637
|
"use strict";
|
|
2781
|
-
|
|
2782
|
-
import_hmac = require("@noble/hashes/hmac.js");
|
|
2638
|
+
import_aes = require("@noble/ciphers/aes.js");
|
|
2783
2639
|
import_hkdf = require("@noble/hashes/hkdf.js");
|
|
2640
|
+
import_hmac = require("@noble/hashes/hmac.js");
|
|
2641
|
+
import_sha2 = require("@noble/hashes/sha2.js");
|
|
2784
2642
|
import_utils = require("@noble/hashes/utils.js");
|
|
2785
|
-
import_aes = require("@noble/ciphers/aes.js");
|
|
2786
2643
|
init_jwt_alg();
|
|
2787
2644
|
init_jwt_alg();
|
|
2788
2645
|
browserCrypto = {
|
|
@@ -2828,38 +2685,38 @@ var init_browser = __esm({
|
|
|
2828
2685
|
|
|
2829
2686
|
// libs/utils/src/crypto/index.ts
|
|
2830
2687
|
function getCrypto() {
|
|
2831
|
-
return
|
|
2688
|
+
return platformCrypto.cryptoProvider;
|
|
2832
2689
|
}
|
|
2833
|
-
function
|
|
2690
|
+
function rsaVerify3(jwtAlg, data, publicJwk, signature) {
|
|
2834
2691
|
if (isNode()) {
|
|
2835
|
-
return Promise.resolve(
|
|
2692
|
+
return Promise.resolve(platformCrypto.rsaVerify(jwtAlg, data, publicJwk, signature));
|
|
2836
2693
|
}
|
|
2837
2694
|
const { rsaVerifyBrowser: rsaVerifyBrowser2 } = (init_browser(), __toCommonJS(browser_exports));
|
|
2838
2695
|
return rsaVerifyBrowser2(jwtAlg, data, publicJwk, signature);
|
|
2839
2696
|
}
|
|
2840
|
-
function
|
|
2697
|
+
function rsaSignBase64Url3(jwtAlg, data, privateJwk) {
|
|
2841
2698
|
if (!isNode()) {
|
|
2842
2699
|
throw new Error("rsaSignBase64Url is only available in Node.js runtimes");
|
|
2843
2700
|
}
|
|
2844
|
-
return
|
|
2701
|
+
return platformCrypto.rsaSignBase64Url(jwtAlg, data, privateJwk);
|
|
2845
2702
|
}
|
|
2846
|
-
function
|
|
2703
|
+
function rsaVerifySync3(jwtAlg, data, publicJwk, signature) {
|
|
2847
2704
|
if (!isNode()) {
|
|
2848
2705
|
throw new Error("rsaVerifySync is only available in Node.js runtimes; use the async rsaVerify in browsers");
|
|
2849
2706
|
}
|
|
2850
|
-
return
|
|
2707
|
+
return platformCrypto.rsaVerifySync(jwtAlg, data, publicJwk, signature);
|
|
2851
2708
|
}
|
|
2852
|
-
function
|
|
2709
|
+
function pemToPublicJwk3(pem) {
|
|
2853
2710
|
if (!isNode()) {
|
|
2854
2711
|
throw new Error("pemToPublicJwk is only available in Node.js runtimes");
|
|
2855
2712
|
}
|
|
2856
|
-
return
|
|
2713
|
+
return platformCrypto.pemToPublicJwk(pem);
|
|
2857
2714
|
}
|
|
2858
|
-
function
|
|
2715
|
+
function generateRsaKeyPair3(modulusLength, alg) {
|
|
2859
2716
|
if (!isNode()) {
|
|
2860
2717
|
throw new Error("generateRsaKeyPair is only available in Node.js runtimes");
|
|
2861
2718
|
}
|
|
2862
|
-
return
|
|
2719
|
+
return platformCrypto.generateRsaKeyPair(modulusLength, alg);
|
|
2863
2720
|
}
|
|
2864
2721
|
function randomUUID() {
|
|
2865
2722
|
return getCrypto().randomUUID();
|
|
@@ -2965,11 +2822,11 @@ function base64Decode(data) {
|
|
|
2965
2822
|
function sha256Base64url(data) {
|
|
2966
2823
|
return base64urlEncode(sha256(data));
|
|
2967
2824
|
}
|
|
2968
|
-
var
|
|
2825
|
+
var platformCrypto, HKDF_SHA256_MAX_LENGTH;
|
|
2969
2826
|
var init_crypto = __esm({
|
|
2970
2827
|
"libs/utils/src/crypto/index.ts"() {
|
|
2971
2828
|
"use strict";
|
|
2972
|
-
|
|
2829
|
+
platformCrypto = __toESM(require("#crypto-provider"));
|
|
2973
2830
|
init_runtime();
|
|
2974
2831
|
init_jwt_alg();
|
|
2975
2832
|
init_runtime();
|
|
@@ -3017,6 +2874,73 @@ var init_redis_error_listener = __esm({
|
|
|
3017
2874
|
}
|
|
3018
2875
|
});
|
|
3019
2876
|
|
|
2877
|
+
// libs/utils/src/storage/redis-url.ts
|
|
2878
|
+
function decodeOrRaw(encoded) {
|
|
2879
|
+
try {
|
|
2880
|
+
return decodeURIComponent(encoded);
|
|
2881
|
+
} catch {
|
|
2882
|
+
return encoded;
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
function integerOrUndefined(text) {
|
|
2886
|
+
const value = Number.parseInt(text ?? "", 10);
|
|
2887
|
+
return Number.isNaN(value) ? void 0 : value;
|
|
2888
|
+
}
|
|
2889
|
+
function readRedisUrl(rawUrl) {
|
|
2890
|
+
let url;
|
|
2891
|
+
try {
|
|
2892
|
+
url = new URL(rawUrl);
|
|
2893
|
+
} catch {
|
|
2894
|
+
return void 0;
|
|
2895
|
+
}
|
|
2896
|
+
if (url.protocol !== "redis:" && url.protocol !== "rediss:") return void 0;
|
|
2897
|
+
const query = Object.fromEntries(url.searchParams);
|
|
2898
|
+
const { host, port, username, password, db, ...otherOptions } = query;
|
|
2899
|
+
const queryOptions = {};
|
|
2900
|
+
for (const [name, value = ""] of Object.entries(otherOptions)) {
|
|
2901
|
+
queryOptions[name] = name === "family" ? integerOrUndefined(value) ?? value : value;
|
|
2902
|
+
}
|
|
2903
|
+
return {
|
|
2904
|
+
host: url.hostname.replace(/^\[(.*)\]$/, "$1") || host,
|
|
2905
|
+
port: integerOrUndefined(url.port || port),
|
|
2906
|
+
username: decodeOrRaw(url.username) || username || void 0,
|
|
2907
|
+
password: decodeOrRaw(url.password) || password || void 0,
|
|
2908
|
+
db: integerOrUndefined(url.pathname.replace(/^\/+/, "") || db),
|
|
2909
|
+
tls: url.protocol === "rediss:",
|
|
2910
|
+
queryOptions
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
function mergeRedisUrlFields(rawUrl, fields) {
|
|
2914
|
+
const connection = readRedisUrl(rawUrl);
|
|
2915
|
+
if (!connection) return void 0;
|
|
2916
|
+
const fillIns = {};
|
|
2917
|
+
const conflicts = [];
|
|
2918
|
+
if (fields.host !== void 0 && fields.host !== connection.host) conflicts.push("host");
|
|
2919
|
+
if (fields.port !== void 0) {
|
|
2920
|
+
if (connection.port === void 0) fillIns.port = fields.port;
|
|
2921
|
+
else if (fields.port !== connection.port) conflicts.push("port");
|
|
2922
|
+
}
|
|
2923
|
+
if (fields.password !== void 0) {
|
|
2924
|
+
if (connection.password === void 0) fillIns.password = fields.password;
|
|
2925
|
+
else if (fields.password !== connection.password) conflicts.push("password");
|
|
2926
|
+
}
|
|
2927
|
+
if (fields.db !== void 0) {
|
|
2928
|
+
if (connection.db === void 0) fillIns.db = fields.db;
|
|
2929
|
+
else if (fields.db !== connection.db) conflicts.push("db");
|
|
2930
|
+
}
|
|
2931
|
+
if (fields.tls === true && !connection.tls) fillIns.tls = true;
|
|
2932
|
+
if (fields.tls === false && connection.tls) conflicts.push("tls");
|
|
2933
|
+
return { connection, fillIns, conflicts };
|
|
2934
|
+
}
|
|
2935
|
+
function describeRedisUrlConflicts(conflicts) {
|
|
2936
|
+
return `redis ${conflicts.join(", ")} contradict${conflicts.length === 1 ? "s" : ""} redis.url. Fields beside a url only fill in what the URL leaves out (port, password, db, tls); put the value in the URL or drop the field.`;
|
|
2937
|
+
}
|
|
2938
|
+
var init_redis_url = __esm({
|
|
2939
|
+
"libs/utils/src/storage/redis-url.ts"() {
|
|
2940
|
+
"use strict";
|
|
2941
|
+
}
|
|
2942
|
+
});
|
|
2943
|
+
|
|
3020
2944
|
// libs/utils/src/storage/utils/index.ts
|
|
3021
2945
|
var init_utils = __esm({
|
|
3022
2946
|
"libs/utils/src/storage/utils/index.ts"() {
|
|
@@ -3056,17 +2980,49 @@ function getRedisClass() {
|
|
|
3056
2980
|
});
|
|
3057
2981
|
}
|
|
3058
2982
|
}
|
|
2983
|
+
function resolveUrlOptions(connectionUrl, options) {
|
|
2984
|
+
const { url: configUrl, ...siblingFields } = { url: void 0, ...options.config };
|
|
2985
|
+
if (options.url && configUrl && configUrl !== options.url) {
|
|
2986
|
+
throw new StorageConfigError("redis", "redis.url and redis.config.url name different servers; set one of them.");
|
|
2987
|
+
}
|
|
2988
|
+
return urlConnectionOptions(connectionUrl, siblingFields);
|
|
2989
|
+
}
|
|
2990
|
+
function urlConnectionOptions(url, fields) {
|
|
2991
|
+
const merge = mergeRedisUrlFields(url, fields);
|
|
2992
|
+
if (!merge) return void 0;
|
|
2993
|
+
if (merge.conflicts.length > 0) {
|
|
2994
|
+
throw new StorageConfigError("redis", describeRedisUrlConflicts(merge.conflicts));
|
|
2995
|
+
}
|
|
2996
|
+
const { queryOptions, tls, ...stated } = merge.connection;
|
|
2997
|
+
return { ...queryOptions, ...stated, ...toFillInOptions(merge.fillIns), ...tls ? { tls: {} } : {} };
|
|
2998
|
+
}
|
|
2999
|
+
function toFillInOptions({ port, password, db, tls }) {
|
|
3000
|
+
return {
|
|
3001
|
+
...port !== void 0 ? { port } : {},
|
|
3002
|
+
...password !== void 0 ? { password } : {},
|
|
3003
|
+
...db !== void 0 ? { db } : {},
|
|
3004
|
+
...tls ? { tls: {} } : {}
|
|
3005
|
+
};
|
|
3006
|
+
}
|
|
3059
3007
|
function createRedisClient(options) {
|
|
3008
|
+
const urlOptions = options.url ? urlConnectionOptions(options.url, options) : void 0;
|
|
3060
3009
|
const RedisClass = getRedisClass();
|
|
3061
3010
|
const baseOptions = { lazyConnect: false, maxRetriesPerRequest: 3 };
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3011
|
+
let client;
|
|
3012
|
+
if (urlOptions) {
|
|
3013
|
+
client = new RedisClass({ ...baseOptions, ...urlOptions });
|
|
3014
|
+
} else if (options.url) {
|
|
3015
|
+
client = new RedisClass(options.url, baseOptions);
|
|
3016
|
+
} else {
|
|
3017
|
+
client = new RedisClass({
|
|
3018
|
+
...baseOptions,
|
|
3019
|
+
host: options.host ?? "localhost",
|
|
3020
|
+
port: options.port ?? 6379,
|
|
3021
|
+
password: options.password,
|
|
3022
|
+
db: options.db ?? 0,
|
|
3023
|
+
tls: options.tls ? {} : void 0
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3070
3026
|
attachRedisErrorListener(client, { label: options.label ?? "redis", logger: options.logger });
|
|
3071
3027
|
return client;
|
|
3072
3028
|
}
|
|
@@ -3076,6 +3032,7 @@ var init_redis = __esm({
|
|
|
3076
3032
|
"use strict";
|
|
3077
3033
|
init_errors();
|
|
3078
3034
|
init_redis_error_listener();
|
|
3035
|
+
init_redis_url();
|
|
3079
3036
|
init_utils();
|
|
3080
3037
|
init_compare_and_delete();
|
|
3081
3038
|
init_base();
|
|
@@ -3089,6 +3046,9 @@ var init_redis = __esm({
|
|
|
3089
3046
|
connecting;
|
|
3090
3047
|
keyPrefix;
|
|
3091
3048
|
subscriptionHandlers = /* @__PURE__ */ new Map();
|
|
3049
|
+
connectionUrl;
|
|
3050
|
+
/** The connection options of a URL this adapter reads itself; undefined for one only ioredis reads. */
|
|
3051
|
+
urlOptions;
|
|
3092
3052
|
constructor(options = {}) {
|
|
3093
3053
|
super();
|
|
3094
3054
|
const hasClient = options.client !== void 0;
|
|
@@ -3110,6 +3070,9 @@ var init_redis = __esm({
|
|
|
3110
3070
|
this.options = options;
|
|
3111
3071
|
this.ownsClient = !hasClient;
|
|
3112
3072
|
this.keyPrefix = options.keyPrefix ?? "";
|
|
3073
|
+
const configUrl = options.config?.url;
|
|
3074
|
+
this.connectionUrl = options.url ?? (typeof configUrl === "string" ? configUrl : void 0);
|
|
3075
|
+
this.urlOptions = this.connectionUrl ? resolveUrlOptions(this.connectionUrl, options) : void 0;
|
|
3113
3076
|
}
|
|
3114
3077
|
// ============================================
|
|
3115
3078
|
// Connection Lifecycle
|
|
@@ -3131,12 +3094,7 @@ var init_redis = __esm({
|
|
|
3131
3094
|
if (this.options.client) {
|
|
3132
3095
|
client = this.options.client;
|
|
3133
3096
|
} else {
|
|
3134
|
-
|
|
3135
|
-
if (this.options.url) {
|
|
3136
|
-
client = new RedisClass(this.options.url, this.buildRedisOptions());
|
|
3137
|
-
} else {
|
|
3138
|
-
client = new RedisClass(this.buildRedisOptions());
|
|
3139
|
-
}
|
|
3097
|
+
client = this.newClient();
|
|
3140
3098
|
client.on("error", recordSocketError);
|
|
3141
3099
|
}
|
|
3142
3100
|
await client.ping();
|
|
@@ -3369,14 +3327,11 @@ var init_redis = __esm({
|
|
|
3369
3327
|
* Build Redis options from config.
|
|
3370
3328
|
*/
|
|
3371
3329
|
buildRedisOptions() {
|
|
3372
|
-
if (this.
|
|
3373
|
-
return {
|
|
3374
|
-
lazyConnect: false,
|
|
3375
|
-
maxRetriesPerRequest: 3
|
|
3376
|
-
};
|
|
3330
|
+
if (this.connectionUrl) {
|
|
3331
|
+
return { lazyConnect: false, maxRetriesPerRequest: 3, ...this.urlOptions };
|
|
3377
3332
|
}
|
|
3378
3333
|
const config = this.options.config;
|
|
3379
|
-
if (!config) {
|
|
3334
|
+
if (!config || typeof config.url === "string" || config.host === void 0) {
|
|
3380
3335
|
throw new StorageConfigError("redis", "Redis config is required when URL is not provided");
|
|
3381
3336
|
}
|
|
3382
3337
|
return {
|
|
@@ -3389,16 +3344,23 @@ var init_redis = __esm({
|
|
|
3389
3344
|
maxRetriesPerRequest: 3
|
|
3390
3345
|
};
|
|
3391
3346
|
}
|
|
3347
|
+
/**
|
|
3348
|
+
* Create a client this adapter owns. A URL only ioredis reads goes to it as is.
|
|
3349
|
+
*/
|
|
3350
|
+
newClient() {
|
|
3351
|
+
const RedisClass = getRedisClass();
|
|
3352
|
+
if (this.connectionUrl && !this.urlOptions) {
|
|
3353
|
+
return new RedisClass(this.connectionUrl, this.buildRedisOptions());
|
|
3354
|
+
}
|
|
3355
|
+
return new RedisClass(this.buildRedisOptions());
|
|
3356
|
+
}
|
|
3392
3357
|
/**
|
|
3393
3358
|
* Create subscriber connection.
|
|
3394
3359
|
*/
|
|
3395
3360
|
async createSubscriber() {
|
|
3396
|
-
const RedisClass = getRedisClass();
|
|
3397
3361
|
let subscriber;
|
|
3398
|
-
if (this.options.
|
|
3399
|
-
subscriber =
|
|
3400
|
-
} else if (this.options.config) {
|
|
3401
|
-
subscriber = new RedisClass(this.buildRedisOptions());
|
|
3362
|
+
if (this.connectionUrl || this.options.config) {
|
|
3363
|
+
subscriber = this.newClient();
|
|
3402
3364
|
} else if (this.options.client) {
|
|
3403
3365
|
subscriber = this.options.client.duplicate();
|
|
3404
3366
|
} else {
|
|
@@ -3447,7 +3409,8 @@ var init_redis = __esm({
|
|
|
3447
3409
|
// libs/utils/src/storage/adapters/vercel-kv.ts
|
|
3448
3410
|
var vercel_kv_exports = {};
|
|
3449
3411
|
__export(vercel_kv_exports, {
|
|
3450
|
-
VercelKvStorageAdapter: () => VercelKvStorageAdapter
|
|
3412
|
+
VercelKvStorageAdapter: () => VercelKvStorageAdapter,
|
|
3413
|
+
createVercelKvClient: () => createVercelKvClient
|
|
3451
3414
|
});
|
|
3452
3415
|
async function loadVercelKv() {
|
|
3453
3416
|
try {
|
|
@@ -3459,6 +3422,15 @@ async function loadVercelKv() {
|
|
|
3459
3422
|
throw new Error("@vercel/kv is required for Vercel KV storage adapter. Install it with: npm install @vercel/kv");
|
|
3460
3423
|
}
|
|
3461
3424
|
}
|
|
3425
|
+
async function createVercelKvClient(connection = {}) {
|
|
3426
|
+
const url = connection.url ?? process.env["KV_REST_API_URL"];
|
|
3427
|
+
const token = connection.token ?? process.env["KV_REST_API_TOKEN"];
|
|
3428
|
+
if (!url || !token) {
|
|
3429
|
+
throw new StorageConfigError("vercel-kv", "Pass url and token, or set KV_REST_API_URL and KV_REST_API_TOKEN.");
|
|
3430
|
+
}
|
|
3431
|
+
const { createClient } = await loadVercelKv();
|
|
3432
|
+
return createClient({ url, token, cache: void 0, automaticDeserialization: false });
|
|
3433
|
+
}
|
|
3462
3434
|
var VercelKvStorageAdapter;
|
|
3463
3435
|
var init_vercel_kv = __esm({
|
|
3464
3436
|
"libs/utils/src/storage/adapters/vercel-kv.ts"() {
|
|
@@ -3491,13 +3463,7 @@ var init_vercel_kv = __esm({
|
|
|
3491
3463
|
async connect() {
|
|
3492
3464
|
if (this.connected) return;
|
|
3493
3465
|
try {
|
|
3494
|
-
|
|
3495
|
-
const url = this.options.url;
|
|
3496
|
-
const token = this.options.token;
|
|
3497
|
-
if (!url || !token) {
|
|
3498
|
-
throw new StorageConfigError("vercel-kv", "URL and token are required");
|
|
3499
|
-
}
|
|
3500
|
-
this.client = createClient({ url, token, cache: void 0, automaticDeserialization: false });
|
|
3466
|
+
this.client = await createVercelKvClient({ url: this.options.url, token: this.options.token });
|
|
3501
3467
|
await this.client.exists("__healthcheck__");
|
|
3502
3468
|
this.connected = true;
|
|
3503
3469
|
} catch (e) {
|
|
@@ -3670,9 +3636,12 @@ var upstash_exports = {};
|
|
|
3670
3636
|
__export(upstash_exports, {
|
|
3671
3637
|
UpstashStorageAdapter: () => UpstashStorageAdapter
|
|
3672
3638
|
});
|
|
3673
|
-
function
|
|
3639
|
+
async function loadUpstashRedis() {
|
|
3674
3640
|
try {
|
|
3675
|
-
|
|
3641
|
+
const mod = await import("@upstash/redis");
|
|
3642
|
+
const resolved = typeof mod.Redis === "function" ? mod : mod.default;
|
|
3643
|
+
if (!resolved || typeof resolved.Redis !== "function") throw new Error("Redis missing");
|
|
3644
|
+
return resolved;
|
|
3676
3645
|
} catch {
|
|
3677
3646
|
throw new Error(
|
|
3678
3647
|
"@upstash/redis is required for Upstash storage adapter. Install it with: npm install @upstash/redis"
|
|
@@ -3717,7 +3686,7 @@ var init_upstash = __esm({
|
|
|
3717
3686
|
async connect() {
|
|
3718
3687
|
if (this.connected) return;
|
|
3719
3688
|
try {
|
|
3720
|
-
const { Redis } =
|
|
3689
|
+
const { Redis } = await loadUpstashRedis();
|
|
3721
3690
|
const url = this.options.url;
|
|
3722
3691
|
const token = this.options.token;
|
|
3723
3692
|
if (!url || !token) {
|
|
@@ -4184,9 +4153,11 @@ __export(index_exports, {
|
|
|
4184
4153
|
createSafeRegExp: () => createSafeRegExp,
|
|
4185
4154
|
createSecretData: () => createSecretData,
|
|
4186
4155
|
createStorage: () => createStorage,
|
|
4156
|
+
createVercelKvClient: () => createVercelKvClient,
|
|
4187
4157
|
decryptAesGcm: () => decryptAesGcm,
|
|
4188
4158
|
decryptValue: () => decryptValue,
|
|
4189
4159
|
deleteSecret: () => deleteSecret,
|
|
4160
|
+
describeRedisUrlConflicts: () => describeRedisUrlConflicts,
|
|
4190
4161
|
deserializeAndDecrypt: () => deserializeAndDecrypt,
|
|
4191
4162
|
deserializeBlob: () => deserializeBlob,
|
|
4192
4163
|
detectRuntimeContext: () => import_runtime_context.detectRuntimeContext,
|
|
@@ -4217,7 +4188,7 @@ __export(index_exports, {
|
|
|
4217
4188
|
generateCodeChallenge: () => generateCodeChallenge,
|
|
4218
4189
|
generateCodeVerifier: () => generateCodeVerifier,
|
|
4219
4190
|
generatePkcePair: () => generatePkcePair,
|
|
4220
|
-
generateRsaKeyPair: () =>
|
|
4191
|
+
generateRsaKeyPair: () => generateRsaKeyPair3,
|
|
4221
4192
|
generateSecret: () => generateSecret,
|
|
4222
4193
|
getAsyncContextMode: () => import_async_context.getAsyncContextMode,
|
|
4223
4194
|
getCookie: () => getCookie,
|
|
@@ -4270,6 +4241,7 @@ __export(index_exports, {
|
|
|
4270
4241
|
loadSecret: () => loadSecret,
|
|
4271
4242
|
matchUriTemplate: () => matchUriTemplate,
|
|
4272
4243
|
matchesPattern: () => matchesPattern,
|
|
4244
|
+
mergeRedisUrlFields: () => mergeRedisUrlFields,
|
|
4273
4245
|
mkdir: () => mkdir,
|
|
4274
4246
|
mkdtemp: () => mkdtemp,
|
|
4275
4247
|
normalizeTTL: () => normalizeTTL,
|
|
@@ -4281,7 +4253,7 @@ __export(index_exports, {
|
|
|
4281
4253
|
pathJoin: () => import_path2.join,
|
|
4282
4254
|
pathResolve: () => import_path2.resolve,
|
|
4283
4255
|
pathToFileURL: () => import_path2.pathToFileURL,
|
|
4284
|
-
pemToPublicJwk: () =>
|
|
4256
|
+
pemToPublicJwk: () => pemToPublicJwk3,
|
|
4285
4257
|
processPlatformToolCalls: () => processPlatformToolCalls,
|
|
4286
4258
|
randomBytes: () => randomBytes,
|
|
4287
4259
|
randomUUID: () => randomUUID,
|
|
@@ -4296,9 +4268,9 @@ __export(index_exports, {
|
|
|
4296
4268
|
resetRuntimeContext: () => import_runtime_context.resetRuntimeContext,
|
|
4297
4269
|
resolveSecretPath: () => resolveSecretPath,
|
|
4298
4270
|
rm: () => rm,
|
|
4299
|
-
rsaSignBase64Url: () =>
|
|
4300
|
-
rsaVerify: () =>
|
|
4301
|
-
rsaVerifySync: () =>
|
|
4271
|
+
rsaSignBase64Url: () => rsaSignBase64Url3,
|
|
4272
|
+
rsaVerify: () => rsaVerify3,
|
|
4273
|
+
rsaVerifySync: () => rsaVerifySync3,
|
|
4302
4274
|
runCmd: () => runCmd,
|
|
4303
4275
|
runRequestExclusive: () => import_async_context.runRequestExclusive,
|
|
4304
4276
|
safeExec: () => safeExec,
|
|
@@ -5446,6 +5418,7 @@ function getDetectedStorageType() {
|
|
|
5446
5418
|
|
|
5447
5419
|
// libs/utils/src/storage/index.ts
|
|
5448
5420
|
init_redis_error_listener();
|
|
5421
|
+
init_redis_url();
|
|
5449
5422
|
init_errors();
|
|
5450
5423
|
|
|
5451
5424
|
// libs/utils/src/storage/typed-storage.ts
|
|
@@ -6160,9 +6133,11 @@ function setMachineIdOverride(id) {
|
|
|
6160
6133
|
createSafeRegExp,
|
|
6161
6134
|
createSecretData,
|
|
6162
6135
|
createStorage,
|
|
6136
|
+
createVercelKvClient,
|
|
6163
6137
|
decryptAesGcm,
|
|
6164
6138
|
decryptValue,
|
|
6165
6139
|
deleteSecret,
|
|
6140
|
+
describeRedisUrlConflicts,
|
|
6166
6141
|
deserializeAndDecrypt,
|
|
6167
6142
|
deserializeBlob,
|
|
6168
6143
|
detectRuntimeContext,
|
|
@@ -6246,6 +6221,7 @@ function setMachineIdOverride(id) {
|
|
|
6246
6221
|
loadSecret,
|
|
6247
6222
|
matchUriTemplate,
|
|
6248
6223
|
matchesPattern,
|
|
6224
|
+
mergeRedisUrlFields,
|
|
6249
6225
|
mkdir,
|
|
6250
6226
|
mkdtemp,
|
|
6251
6227
|
normalizeTTL,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/utils",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"description": "Shared utility functions for FrontMCP - string manipulation, URI handling, path utilities, and more",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -32,11 +32,15 @@
|
|
|
32
32
|
"@noble/hashes": "^2.0.1"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@frontmcp/lazy-zod": "1.9.
|
|
35
|
+
"@frontmcp/lazy-zod": "1.9.3",
|
|
36
|
+
"@upstash/redis": "^1.34.0",
|
|
36
37
|
"@vercel/kv": "^2.0.0 || ^3.0.0",
|
|
37
38
|
"ioredis": "^5.0.0"
|
|
38
39
|
},
|
|
39
40
|
"peerDependenciesMeta": {
|
|
41
|
+
"@upstash/redis": {
|
|
42
|
+
"optional": true
|
|
43
|
+
},
|
|
40
44
|
"@vercel/kv": {
|
|
41
45
|
"optional": true
|
|
42
46
|
},
|
|
@@ -7,7 +7,7 @@ export { BaseStorageAdapter } from './base';
|
|
|
7
7
|
export { MemoryStorageAdapter } from './memory';
|
|
8
8
|
export { RedisStorageAdapter, createRedisClient } from './redis';
|
|
9
9
|
export type { CreateRedisClientOptions } from './redis';
|
|
10
|
-
export { VercelKvStorageAdapter } from './vercel-kv';
|
|
10
|
+
export { VercelKvStorageAdapter, createVercelKvClient, type VercelKvConnection } from './vercel-kv';
|
|
11
11
|
export { UpstashStorageAdapter } from './upstash';
|
|
12
12
|
export { CloudflareKvStorageAdapter } from './cloudflare-kv';
|
|
13
13
|
export { FileSystemStorageAdapter } from './filesystem';
|