@frontmcp/utils 1.9.2 → 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.
@@ -11,6 +11,11 @@ import type { CryptoProvider } from './types';
11
11
  export declare const browserCrypto: CryptoProvider;
12
12
  /** Alias for conditional import resolution via `#crypto-provider`. */
13
13
  export { browserCrypto as cryptoProvider };
14
+ export declare function rsaVerify(): boolean;
15
+ export declare function rsaSignBase64Url(): string;
16
+ export declare function rsaVerifySync(): boolean;
17
+ export declare function pemToPublicJwk(): JsonWebKey;
18
+ export declare function generateRsaKeyPair(): never;
14
19
  export { isRsaPssAlg, jwtAlgToWebCryptoAlg } from './jwt-alg';
15
20
  /**
16
21
  * Verify an RSA signature using WebCrypto.
package/crypto/browser.js CHANGED
@@ -22,16 +22,21 @@ var browser_exports = {};
22
22
  __export(browser_exports, {
23
23
  browserCrypto: () => browserCrypto,
24
24
  cryptoProvider: () => browserCrypto,
25
+ generateRsaKeyPair: () => generateRsaKeyPair,
25
26
  isRsaPssAlg: () => isRsaPssAlg,
26
27
  jwtAlgToWebCryptoAlg: () => jwtAlgToWebCryptoAlg,
27
- rsaVerifyBrowser: () => rsaVerifyBrowser
28
+ pemToPublicJwk: () => pemToPublicJwk,
29
+ rsaSignBase64Url: () => rsaSignBase64Url,
30
+ rsaVerify: () => rsaVerify,
31
+ rsaVerifyBrowser: () => rsaVerifyBrowser,
32
+ rsaVerifySync: () => rsaVerifySync
28
33
  });
29
34
  module.exports = __toCommonJS(browser_exports);
30
- var import_sha2 = require("@noble/hashes/sha2.js");
31
- var import_hmac = require("@noble/hashes/hmac.js");
35
+ var import_aes = require("@noble/ciphers/aes.js");
32
36
  var import_hkdf = require("@noble/hashes/hkdf.js");
37
+ var import_hmac = require("@noble/hashes/hmac.js");
38
+ var import_sha2 = require("@noble/hashes/sha2.js");
33
39
  var import_utils = require("@noble/hashes/utils.js");
34
- var import_aes = require("@noble/ciphers/aes.js");
35
40
 
36
41
  // libs/utils/src/crypto/jwt-alg.ts
37
42
  function isRsaPssAlg(jwtAlg) {
@@ -119,6 +124,24 @@ var browserCrypto = {
119
124
  return constantTimeEqual(a, b);
120
125
  }
121
126
  };
127
+ function nodeOnly(name) {
128
+ throw new Error(`${name} is only available in Node.js runtimes`);
129
+ }
130
+ function rsaVerify() {
131
+ return nodeOnly("rsaVerify");
132
+ }
133
+ function rsaSignBase64Url() {
134
+ return nodeOnly("rsaSignBase64Url");
135
+ }
136
+ function rsaVerifySync() {
137
+ return nodeOnly("rsaVerifySync");
138
+ }
139
+ function pemToPublicJwk() {
140
+ return nodeOnly("pemToPublicJwk");
141
+ }
142
+ function generateRsaKeyPair() {
143
+ return nodeOnly("generateRsaKeyPair");
144
+ }
122
145
  async function rsaVerifyBrowser(jwtAlg, data, publicJwk, signature) {
123
146
  if (typeof globalThis.crypto?.subtle === "undefined") {
124
147
  throw new Error("WebCrypto API (crypto.subtle) is not available in this environment");
@@ -154,7 +177,12 @@ function getSaltLength(jwtAlg) {
154
177
  0 && (module.exports = {
155
178
  browserCrypto,
156
179
  cryptoProvider,
180
+ generateRsaKeyPair,
157
181
  isRsaPssAlg,
158
182
  jwtAlgToWebCryptoAlg,
159
- rsaVerifyBrowser
183
+ pemToPublicJwk,
184
+ rsaSignBase64Url,
185
+ rsaVerify,
186
+ rsaVerifyBrowser,
187
+ rsaVerifySync
160
188
  });
package/crypto/node.d.ts CHANGED
@@ -48,7 +48,7 @@ export declare function generateRsaKeyPair(modulusLength?: number, alg?: string)
48
48
  * For RSA-PSS (PS256/PS384/PS512), callers must pass appropriate padding/saltLength options.
49
49
  */
50
50
  export declare function rsaSign(algorithm: string, data: Buffer, privateKey: crypto.KeyObject, options?: Omit<crypto.SignKeyObjectInput, 'key'>): Buffer;
51
- export declare function rsaVerify(jwtAlg: string, data: Buffer, publicJwk: JsonWebKey, signature: Buffer): boolean;
51
+ export declare function rsaVerify(jwtAlg: string, data: Buffer | Uint8Array, publicJwk: JsonWebKey, signature: Buffer | Uint8Array): boolean;
52
52
  /**
53
53
  * Sign data with an RSA private JWK and return a base64url-encoded signature.
54
54
  *
@@ -2,11 +2,11 @@ var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuilt
2
2
  const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
3
3
  // __frontmcpCreateRequire
4
4
  // libs/utils/src/crypto/browser.ts
5
- import { sha256 as sha256Hash } from "@noble/hashes/sha2.js";
6
- import { hmac } from "@noble/hashes/hmac.js";
5
+ import { gcm } from "@noble/ciphers/aes.js";
7
6
  import { hkdf } from "@noble/hashes/hkdf.js";
7
+ import { hmac } from "@noble/hashes/hmac.js";
8
+ import { sha256 as sha256Hash } from "@noble/hashes/sha2.js";
8
9
  import { randomBytes as nobleRandomBytes } from "@noble/hashes/utils.js";
9
- import { gcm } from "@noble/ciphers/aes.js";
10
10
 
11
11
  // libs/utils/src/crypto/jwt-alg.ts
12
12
  function isRsaPssAlg(jwtAlg) {
@@ -94,6 +94,24 @@ var browserCrypto = {
94
94
  return constantTimeEqual(a, b);
95
95
  }
96
96
  };
97
+ function nodeOnly(name) {
98
+ throw new Error(`${name} is only available in Node.js runtimes`);
99
+ }
100
+ function rsaVerify() {
101
+ return nodeOnly("rsaVerify");
102
+ }
103
+ function rsaSignBase64Url() {
104
+ return nodeOnly("rsaSignBase64Url");
105
+ }
106
+ function rsaVerifySync() {
107
+ return nodeOnly("rsaVerifySync");
108
+ }
109
+ function pemToPublicJwk() {
110
+ return nodeOnly("pemToPublicJwk");
111
+ }
112
+ function generateRsaKeyPair() {
113
+ return nodeOnly("generateRsaKeyPair");
114
+ }
97
115
  async function rsaVerifyBrowser(jwtAlg, data, publicJwk, signature) {
98
116
  if (typeof globalThis.crypto?.subtle === "undefined") {
99
117
  throw new Error("WebCrypto API (crypto.subtle) is not available in this environment");
@@ -128,7 +146,12 @@ function getSaltLength(jwtAlg) {
128
146
  export {
129
147
  browserCrypto,
130
148
  browserCrypto as cryptoProvider,
149
+ generateRsaKeyPair,
131
150
  isRsaPssAlg,
132
151
  jwtAlgToWebCryptoAlg,
133
- rsaVerifyBrowser
152
+ pemToPublicJwk,
153
+ rsaSignBase64Url,
154
+ rsaVerify,
155
+ rsaVerifyBrowser,
156
+ rsaVerifySync
134
157
  };
package/esm/index.mjs CHANGED
@@ -2539,186 +2539,25 @@ var init_key_persistence2 = __esm({
2539
2539
  }
2540
2540
  });
2541
2541
 
2542
- // libs/utils/src/crypto/node.ts
2543
- var node_exports = {};
2544
- __export(node_exports, {
2545
- createSignedJwt: () => createSignedJwt,
2546
- cryptoProvider: () => nodeCrypto,
2547
- generateRsaKeyPair: () => generateRsaKeyPair,
2548
- isRsaPssAlg: () => isRsaPssAlg,
2549
- jwtAlgToNodeAlg: () => jwtAlgToNodeAlg,
2550
- nodeCrypto: () => nodeCrypto,
2551
- pemToPublicJwk: () => pemToPublicJwk,
2552
- rsaSign: () => rsaSign,
2553
- rsaSignBase64Url: () => rsaSignBase64Url,
2554
- rsaVerify: () => rsaVerify,
2555
- rsaVerifySync: () => rsaVerifySync
2556
- });
2557
- import crypto2 from "node:crypto";
2558
- function toUint8Array(buf) {
2559
- return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
2560
- }
2561
- function toBuffer(data) {
2562
- if (typeof data === "string") {
2563
- return Buffer.from(data, "utf8");
2564
- }
2565
- return Buffer.from(data);
2566
- }
2567
- function generateRsaKeyPair(modulusLength = 2048, alg = "RS256") {
2568
- const kid = `rsa-key-${Date.now()}-${crypto2.randomBytes(8).toString("hex")}`;
2569
- const { privateKey, publicKey } = crypto2.generateKeyPairSync("rsa", {
2570
- modulusLength
2571
- });
2572
- const exported = publicKey.export({ format: "jwk" });
2573
- const publicJwk = {
2574
- ...exported,
2575
- kid,
2576
- alg,
2577
- use: "sig",
2578
- kty: "RSA"
2579
- };
2580
- return { privateKey, publicKey, publicJwk };
2581
- }
2582
- function rsaSign(algorithm, data, privateKey, options) {
2583
- const signingKey = options ? { key: privateKey, ...options } : privateKey;
2584
- return crypto2.sign(algorithm, data, signingKey);
2585
- }
2586
- function rsaVerify(jwtAlg, data, publicJwk, signature) {
2587
- const publicKey = crypto2.createPublicKey({ key: publicJwk, format: "jwk" });
2588
- const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
2589
- const verifyKey = isRsaPssAlg(jwtAlg) ? {
2590
- key: publicKey,
2591
- padding: crypto2.constants.RSA_PKCS1_PSS_PADDING,
2592
- saltLength: crypto2.constants.RSA_PSS_SALTLEN_DIGEST
2593
- } : publicKey;
2594
- return crypto2.verify(nodeAlgorithm, data, verifyKey, signature);
2595
- }
2596
- function rsaSignBase64Url(jwtAlg, data, privateJwk) {
2597
- const privateKey = crypto2.createPrivateKey({ key: privateJwk, format: "jwk" });
2598
- const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
2599
- const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
2600
- const sig = isRsaPssAlg(jwtAlg) ? rsaSign(nodeAlgorithm, buf, privateKey, {
2601
- padding: crypto2.constants.RSA_PKCS1_PSS_PADDING,
2602
- saltLength: crypto2.constants.RSA_PSS_SALTLEN_DIGEST
2603
- }) : rsaSign(nodeAlgorithm, buf, privateKey);
2604
- return sig.toString("base64url");
2605
- }
2606
- function rsaVerifySync(jwtAlg, data, publicJwk, signature) {
2607
- try {
2608
- const publicKey = crypto2.createPublicKey({ key: publicJwk, format: "jwk" });
2609
- const dataBuf = Buffer.isBuffer(data) ? data : Buffer.from(data);
2610
- const sigBuf = Buffer.isBuffer(signature) ? signature : Buffer.from(signature);
2611
- if (jwtAlg === "EdDSA") {
2612
- return crypto2.verify(null, dataBuf, publicKey, sigBuf);
2613
- }
2614
- const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
2615
- const verifyKey = isRsaPssAlg(jwtAlg) ? {
2616
- key: publicKey,
2617
- padding: crypto2.constants.RSA_PKCS1_PSS_PADDING,
2618
- saltLength: crypto2.constants.RSA_PSS_SALTLEN_DIGEST
2619
- } : publicKey;
2620
- return crypto2.verify(nodeAlgorithm, dataBuf, verifyKey, sigBuf);
2621
- } catch {
2622
- return false;
2623
- }
2624
- }
2625
- function pemToPublicJwk(pem) {
2626
- const key = crypto2.createPublicKey({ key: pem, format: "pem" });
2627
- return key.export({ format: "jwk" });
2628
- }
2629
- function createSignedJwt(payload, privateKey, kid, alg = "RS256") {
2630
- const header = { alg, typ: "JWT", kid };
2631
- const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
2632
- const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
2633
- const signatureInput = `${headerB64}.${payloadB64}`;
2634
- const nodeAlgorithm = jwtAlgToNodeAlg(alg);
2635
- const signature = rsaSign(
2636
- nodeAlgorithm,
2637
- Buffer.from(signatureInput),
2638
- privateKey,
2639
- isRsaPssAlg(alg) ? {
2640
- padding: crypto2.constants.RSA_PKCS1_PSS_PADDING,
2641
- saltLength: crypto2.constants.RSA_PSS_SALTLEN_DIGEST
2642
- } : void 0
2643
- );
2644
- const signatureB64 = signature.toString("base64url");
2645
- return `${headerB64}.${payloadB64}.${signatureB64}`;
2646
- }
2647
- var nodeCrypto;
2648
- var init_node = __esm({
2649
- "libs/utils/src/crypto/node.ts"() {
2650
- "use strict";
2651
- init_jwt_alg();
2652
- init_jwt_alg();
2653
- nodeCrypto = {
2654
- randomUUID() {
2655
- return crypto2.randomUUID();
2656
- },
2657
- randomBytes(length) {
2658
- return toUint8Array(crypto2.randomBytes(length));
2659
- },
2660
- sha256(data) {
2661
- const hash = crypto2.createHash("sha256").update(toBuffer(data)).digest();
2662
- return toUint8Array(hash);
2663
- },
2664
- sha256Hex(data) {
2665
- return crypto2.createHash("sha256").update(toBuffer(data)).digest("hex");
2666
- },
2667
- hmacSha256(key, data) {
2668
- const hmac2 = crypto2.createHmac("sha256", Buffer.from(key)).update(Buffer.from(data)).digest();
2669
- return toUint8Array(hmac2);
2670
- },
2671
- hkdfSha256(ikm, salt, info, length) {
2672
- const ikmBuf = Buffer.from(ikm);
2673
- const saltBuf = salt.length > 0 ? Buffer.from(salt) : Buffer.alloc(32);
2674
- const prk = crypto2.createHmac("sha256", saltBuf).update(ikmBuf).digest();
2675
- const hashLen = 32;
2676
- const n = Math.ceil(length / hashLen);
2677
- const chunks = [];
2678
- let prev = Buffer.alloc(0);
2679
- for (let i = 1; i <= n; i++) {
2680
- prev = crypto2.createHmac("sha256", prk).update(Buffer.concat([prev, Buffer.from(info), Buffer.from([i])])).digest();
2681
- chunks.push(prev);
2682
- }
2683
- return toUint8Array(Buffer.concat(chunks).subarray(0, length));
2684
- },
2685
- encryptAesGcm(key, plaintext, iv) {
2686
- const cipher = crypto2.createCipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
2687
- const encrypted = Buffer.concat([cipher.update(Buffer.from(plaintext)), cipher.final()]);
2688
- const tag = cipher.getAuthTag();
2689
- return {
2690
- ciphertext: toUint8Array(encrypted),
2691
- tag: toUint8Array(tag)
2692
- };
2693
- },
2694
- decryptAesGcm(key, ciphertext, iv, tag) {
2695
- const decipher = crypto2.createDecipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
2696
- decipher.setAuthTag(Buffer.from(tag));
2697
- const decrypted = Buffer.concat([decipher.update(Buffer.from(ciphertext)), decipher.final()]);
2698
- return toUint8Array(decrypted);
2699
- },
2700
- timingSafeEqual(a, b) {
2701
- if (a.length !== b.length) return false;
2702
- return crypto2.timingSafeEqual(Buffer.from(a), Buffer.from(b));
2703
- }
2704
- };
2705
- }
2706
- });
2707
-
2708
2542
  // libs/utils/src/crypto/browser.ts
2709
2543
  var browser_exports = {};
2710
2544
  __export(browser_exports, {
2711
2545
  browserCrypto: () => browserCrypto,
2712
2546
  cryptoProvider: () => browserCrypto,
2547
+ generateRsaKeyPair: () => generateRsaKeyPair,
2713
2548
  isRsaPssAlg: () => isRsaPssAlg,
2714
2549
  jwtAlgToWebCryptoAlg: () => jwtAlgToWebCryptoAlg,
2715
- rsaVerifyBrowser: () => rsaVerifyBrowser
2550
+ pemToPublicJwk: () => pemToPublicJwk,
2551
+ rsaSignBase64Url: () => rsaSignBase64Url,
2552
+ rsaVerify: () => rsaVerify,
2553
+ rsaVerifyBrowser: () => rsaVerifyBrowser,
2554
+ rsaVerifySync: () => rsaVerifySync
2716
2555
  });
2717
- import { sha256 as sha256Hash } from "@noble/hashes/sha2.js";
2718
- import { hmac } from "@noble/hashes/hmac.js";
2556
+ import { gcm } from "@noble/ciphers/aes.js";
2719
2557
  import { hkdf } from "@noble/hashes/hkdf.js";
2558
+ import { hmac } from "@noble/hashes/hmac.js";
2559
+ import { sha256 as sha256Hash } from "@noble/hashes/sha2.js";
2720
2560
  import { randomBytes as nobleRandomBytes } from "@noble/hashes/utils.js";
2721
- import { gcm } from "@noble/ciphers/aes.js";
2722
2561
  function toBytes(data) {
2723
2562
  if (typeof data === "string") {
2724
2563
  return new TextEncoder().encode(data);
@@ -2746,6 +2585,24 @@ function constantTimeEqual(a, b) {
2746
2585
  }
2747
2586
  return result === 0;
2748
2587
  }
2588
+ function nodeOnly(name) {
2589
+ throw new Error(`${name} is only available in Node.js runtimes`);
2590
+ }
2591
+ function rsaVerify() {
2592
+ return nodeOnly("rsaVerify");
2593
+ }
2594
+ function rsaSignBase64Url() {
2595
+ return nodeOnly("rsaSignBase64Url");
2596
+ }
2597
+ function rsaVerifySync() {
2598
+ return nodeOnly("rsaVerifySync");
2599
+ }
2600
+ function pemToPublicJwk() {
2601
+ return nodeOnly("pemToPublicJwk");
2602
+ }
2603
+ function generateRsaKeyPair() {
2604
+ return nodeOnly("generateRsaKeyPair");
2605
+ }
2749
2606
  async function rsaVerifyBrowser(jwtAlg, data, publicJwk, signature) {
2750
2607
  if (typeof globalThis.crypto?.subtle === "undefined") {
2751
2608
  throw new Error("WebCrypto API (crypto.subtle) is not available in this environment");
@@ -2825,40 +2682,40 @@ var init_browser = __esm({
2825
2682
  });
2826
2683
 
2827
2684
  // libs/utils/src/crypto/index.ts
2828
- import { cryptoProvider as cryptoProvider2 } from "#crypto-provider";
2685
+ import * as platformCrypto from "#crypto-provider";
2829
2686
  function getCrypto() {
2830
- return cryptoProvider2;
2687
+ return platformCrypto.cryptoProvider;
2831
2688
  }
2832
- function rsaVerify2(jwtAlg, data, publicJwk, signature) {
2689
+ function rsaVerify3(jwtAlg, data, publicJwk, signature) {
2833
2690
  if (isNode()) {
2834
- return Promise.resolve((init_node(), __toCommonJS(node_exports)).rsaVerify(jwtAlg, data, publicJwk, signature));
2691
+ return Promise.resolve(platformCrypto.rsaVerify(jwtAlg, data, publicJwk, signature));
2835
2692
  }
2836
2693
  const { rsaVerifyBrowser: rsaVerifyBrowser2 } = (init_browser(), __toCommonJS(browser_exports));
2837
2694
  return rsaVerifyBrowser2(jwtAlg, data, publicJwk, signature);
2838
2695
  }
2839
- function rsaSignBase64Url2(jwtAlg, data, privateJwk) {
2696
+ function rsaSignBase64Url3(jwtAlg, data, privateJwk) {
2840
2697
  if (!isNode()) {
2841
2698
  throw new Error("rsaSignBase64Url is only available in Node.js runtimes");
2842
2699
  }
2843
- return (init_node(), __toCommonJS(node_exports)).rsaSignBase64Url(jwtAlg, data, privateJwk);
2700
+ return platformCrypto.rsaSignBase64Url(jwtAlg, data, privateJwk);
2844
2701
  }
2845
- function rsaVerifySync2(jwtAlg, data, publicJwk, signature) {
2702
+ function rsaVerifySync3(jwtAlg, data, publicJwk, signature) {
2846
2703
  if (!isNode()) {
2847
2704
  throw new Error("rsaVerifySync is only available in Node.js runtimes; use the async rsaVerify in browsers");
2848
2705
  }
2849
- return (init_node(), __toCommonJS(node_exports)).rsaVerifySync(jwtAlg, data, publicJwk, signature);
2706
+ return platformCrypto.rsaVerifySync(jwtAlg, data, publicJwk, signature);
2850
2707
  }
2851
- function pemToPublicJwk2(pem) {
2708
+ function pemToPublicJwk3(pem) {
2852
2709
  if (!isNode()) {
2853
2710
  throw new Error("pemToPublicJwk is only available in Node.js runtimes");
2854
2711
  }
2855
- return (init_node(), __toCommonJS(node_exports)).pemToPublicJwk(pem);
2712
+ return platformCrypto.pemToPublicJwk(pem);
2856
2713
  }
2857
- function generateRsaKeyPair2(modulusLength, alg) {
2714
+ function generateRsaKeyPair3(modulusLength, alg) {
2858
2715
  if (!isNode()) {
2859
2716
  throw new Error("generateRsaKeyPair is only available in Node.js runtimes");
2860
2717
  }
2861
- return (init_node(), __toCommonJS(node_exports)).generateRsaKeyPair(modulusLength, alg);
2718
+ return platformCrypto.generateRsaKeyPair(modulusLength, alg);
2862
2719
  }
2863
2720
  function randomUUID() {
2864
2721
  return getCrypto().randomUUID();
@@ -3023,37 +2880,55 @@ function decodeOrRaw(encoded) {
3023
2880
  return encoded;
3024
2881
  }
3025
2882
  }
3026
- function mergeRedisUrlFields(rawUrl, fields) {
2883
+ function integerOrUndefined(text) {
2884
+ const value = Number.parseInt(text ?? "", 10);
2885
+ return Number.isNaN(value) ? void 0 : value;
2886
+ }
2887
+ function readRedisUrl(rawUrl) {
3027
2888
  let url;
3028
2889
  try {
3029
2890
  url = new URL(rawUrl);
3030
2891
  } catch {
3031
2892
  return void 0;
3032
2893
  }
3033
- const urlHost = url.hostname.replace(/^\[(.*)\]$/, "$1");
3034
- const urlPort = url.port ? Number(url.port) : void 0;
3035
- const urlPassword = url.password ? decodeOrRaw(url.password) : void 0;
3036
- const urlDbText = url.pathname.replace(/^\/+/, "") || url.searchParams.get("db") || void 0;
3037
- const urlDb = urlDbText === void 0 ? void 0 : Number(urlDbText);
3038
- const urlUsesTls = url.protocol === "rediss:";
2894
+ if (url.protocol !== "redis:" && url.protocol !== "rediss:") return void 0;
2895
+ const query = Object.fromEntries(url.searchParams);
2896
+ const { host, port, username, password, db, ...otherOptions } = query;
2897
+ const queryOptions = {};
2898
+ for (const [name, value = ""] of Object.entries(otherOptions)) {
2899
+ queryOptions[name] = name === "family" ? integerOrUndefined(value) ?? value : value;
2900
+ }
2901
+ return {
2902
+ host: url.hostname.replace(/^\[(.*)\]$/, "$1") || host,
2903
+ port: integerOrUndefined(url.port || port),
2904
+ username: decodeOrRaw(url.username) || username || void 0,
2905
+ password: decodeOrRaw(url.password) || password || void 0,
2906
+ db: integerOrUndefined(url.pathname.replace(/^\/+/, "") || db),
2907
+ tls: url.protocol === "rediss:",
2908
+ queryOptions
2909
+ };
2910
+ }
2911
+ function mergeRedisUrlFields(rawUrl, fields) {
2912
+ const connection = readRedisUrl(rawUrl);
2913
+ if (!connection) return void 0;
3039
2914
  const fillIns = {};
3040
2915
  const conflicts = [];
3041
- if (fields.host !== void 0 && fields.host !== urlHost) conflicts.push("host");
2916
+ if (fields.host !== void 0 && fields.host !== connection.host) conflicts.push("host");
3042
2917
  if (fields.port !== void 0) {
3043
- if (urlPort === void 0) fillIns.port = fields.port;
3044
- else if (fields.port !== urlPort) conflicts.push("port");
2918
+ if (connection.port === void 0) fillIns.port = fields.port;
2919
+ else if (fields.port !== connection.port) conflicts.push("port");
3045
2920
  }
3046
2921
  if (fields.password !== void 0) {
3047
- if (urlPassword === void 0) fillIns.password = fields.password;
3048
- else if (fields.password !== urlPassword) conflicts.push("password");
2922
+ if (connection.password === void 0) fillIns.password = fields.password;
2923
+ else if (fields.password !== connection.password) conflicts.push("password");
3049
2924
  }
3050
2925
  if (fields.db !== void 0) {
3051
- if (urlDb === void 0) fillIns.db = fields.db;
3052
- else if (fields.db !== urlDb) conflicts.push("db");
2926
+ if (connection.db === void 0) fillIns.db = fields.db;
2927
+ else if (fields.db !== connection.db) conflicts.push("db");
3053
2928
  }
3054
- if (fields.tls === true && !urlUsesTls) fillIns.tls = true;
3055
- if (fields.tls === false && urlUsesTls) conflicts.push("tls");
3056
- return { fillIns, conflicts };
2929
+ if (fields.tls === true && !connection.tls) fillIns.tls = true;
2930
+ if (fields.tls === false && connection.tls) conflicts.push("tls");
2931
+ return { connection, fillIns, conflicts };
3057
2932
  }
3058
2933
  function describeRedisUrlConflicts(conflicts) {
3059
2934
  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.`;
@@ -3103,28 +2978,49 @@ function getRedisClass() {
3103
2978
  });
3104
2979
  }
3105
2980
  }
3106
- function resolveUrlFillIns(connectionUrl, options) {
2981
+ function resolveUrlOptions(connectionUrl, options) {
3107
2982
  const { url: configUrl, ...siblingFields } = { url: void 0, ...options.config };
3108
2983
  if (options.url && configUrl && configUrl !== options.url) {
3109
2984
  throw new StorageConfigError("redis", "redis.url and redis.config.url name different servers; set one of them.");
3110
2985
  }
3111
- const merge = mergeRedisUrlFields(connectionUrl, siblingFields);
3112
- if (merge && merge.conflicts.length > 0) {
2986
+ return urlConnectionOptions(connectionUrl, siblingFields);
2987
+ }
2988
+ function urlConnectionOptions(url, fields) {
2989
+ const merge = mergeRedisUrlFields(url, fields);
2990
+ if (!merge) return void 0;
2991
+ if (merge.conflicts.length > 0) {
3113
2992
  throw new StorageConfigError("redis", describeRedisUrlConflicts(merge.conflicts));
3114
2993
  }
3115
- return merge?.fillIns ?? {};
2994
+ const { queryOptions, tls, ...stated } = merge.connection;
2995
+ return { ...queryOptions, ...stated, ...toFillInOptions(merge.fillIns), ...tls ? { tls: {} } : {} };
2996
+ }
2997
+ function toFillInOptions({ port, password, db, tls }) {
2998
+ return {
2999
+ ...port !== void 0 ? { port } : {},
3000
+ ...password !== void 0 ? { password } : {},
3001
+ ...db !== void 0 ? { db } : {},
3002
+ ...tls ? { tls: {} } : {}
3003
+ };
3116
3004
  }
3117
3005
  function createRedisClient(options) {
3006
+ const urlOptions = options.url ? urlConnectionOptions(options.url, options) : void 0;
3118
3007
  const RedisClass = getRedisClass();
3119
3008
  const baseOptions = { lazyConnect: false, maxRetriesPerRequest: 3 };
3120
- const client = options.url ? new RedisClass(options.url, baseOptions) : new RedisClass({
3121
- ...baseOptions,
3122
- host: options.host ?? "localhost",
3123
- port: options.port ?? 6379,
3124
- password: options.password,
3125
- db: options.db ?? 0,
3126
- tls: options.tls ? {} : void 0
3127
- });
3009
+ let client;
3010
+ if (urlOptions) {
3011
+ client = new RedisClass({ ...baseOptions, ...urlOptions });
3012
+ } else if (options.url) {
3013
+ client = new RedisClass(options.url, baseOptions);
3014
+ } else {
3015
+ client = new RedisClass({
3016
+ ...baseOptions,
3017
+ host: options.host ?? "localhost",
3018
+ port: options.port ?? 6379,
3019
+ password: options.password,
3020
+ db: options.db ?? 0,
3021
+ tls: options.tls ? {} : void 0
3022
+ });
3023
+ }
3128
3024
  attachRedisErrorListener(client, { label: options.label ?? "redis", logger: options.logger });
3129
3025
  return client;
3130
3026
  }
@@ -3149,7 +3045,8 @@ var init_redis = __esm({
3149
3045
  keyPrefix;
3150
3046
  subscriptionHandlers = /* @__PURE__ */ new Map();
3151
3047
  connectionUrl;
3152
- urlFillIns;
3048
+ /** The connection options of a URL this adapter reads itself; undefined for one only ioredis reads. */
3049
+ urlOptions;
3153
3050
  constructor(options = {}) {
3154
3051
  super();
3155
3052
  const hasClient = options.client !== void 0;
@@ -3173,7 +3070,7 @@ var init_redis = __esm({
3173
3070
  this.keyPrefix = options.keyPrefix ?? "";
3174
3071
  const configUrl = options.config?.url;
3175
3072
  this.connectionUrl = options.url ?? (typeof configUrl === "string" ? configUrl : void 0);
3176
- this.urlFillIns = this.connectionUrl ? resolveUrlFillIns(this.connectionUrl, options) : {};
3073
+ this.urlOptions = this.connectionUrl ? resolveUrlOptions(this.connectionUrl, options) : void 0;
3177
3074
  }
3178
3075
  // ============================================
3179
3076
  // Connection Lifecycle
@@ -3195,12 +3092,7 @@ var init_redis = __esm({
3195
3092
  if (this.options.client) {
3196
3093
  client = this.options.client;
3197
3094
  } else {
3198
- const RedisClass = getRedisClass();
3199
- if (this.connectionUrl) {
3200
- client = new RedisClass(this.connectionUrl, this.buildRedisOptions());
3201
- } else {
3202
- client = new RedisClass(this.buildRedisOptions());
3203
- }
3095
+ client = this.newClient();
3204
3096
  client.on("error", recordSocketError);
3205
3097
  }
3206
3098
  await client.ping();
@@ -3434,15 +3326,7 @@ var init_redis = __esm({
3434
3326
  */
3435
3327
  buildRedisOptions() {
3436
3328
  if (this.connectionUrl) {
3437
- const { port, password, db, tls } = this.urlFillIns;
3438
- return {
3439
- ...port !== void 0 ? { port } : {},
3440
- ...password !== void 0 ? { password } : {},
3441
- ...db !== void 0 ? { db } : {},
3442
- ...tls ? { tls: {} } : {},
3443
- lazyConnect: false,
3444
- maxRetriesPerRequest: 3
3445
- };
3329
+ return { lazyConnect: false, maxRetriesPerRequest: 3, ...this.urlOptions };
3446
3330
  }
3447
3331
  const config = this.options.config;
3448
3332
  if (!config || typeof config.url === "string" || config.host === void 0) {
@@ -3458,16 +3342,23 @@ var init_redis = __esm({
3458
3342
  maxRetriesPerRequest: 3
3459
3343
  };
3460
3344
  }
3345
+ /**
3346
+ * Create a client this adapter owns. A URL only ioredis reads goes to it as is.
3347
+ */
3348
+ newClient() {
3349
+ const RedisClass = getRedisClass();
3350
+ if (this.connectionUrl && !this.urlOptions) {
3351
+ return new RedisClass(this.connectionUrl, this.buildRedisOptions());
3352
+ }
3353
+ return new RedisClass(this.buildRedisOptions());
3354
+ }
3461
3355
  /**
3462
3356
  * Create subscriber connection.
3463
3357
  */
3464
3358
  async createSubscriber() {
3465
- const RedisClass = getRedisClass();
3466
3359
  let subscriber;
3467
- if (this.connectionUrl) {
3468
- subscriber = new RedisClass(this.connectionUrl, this.buildRedisOptions());
3469
- } else if (this.options.config) {
3470
- subscriber = new RedisClass(this.buildRedisOptions());
3360
+ if (this.connectionUrl || this.options.config) {
3361
+ subscriber = this.newClient();
3471
3362
  } else if (this.options.client) {
3472
3363
  subscriber = this.options.client.duplicate();
3473
3364
  } else {
@@ -6070,7 +5961,7 @@ export {
6070
5961
  generateCodeChallenge,
6071
5962
  generateCodeVerifier,
6072
5963
  generatePkcePair,
6073
- generateRsaKeyPair2 as generateRsaKeyPair,
5964
+ generateRsaKeyPair3 as generateRsaKeyPair,
6074
5965
  generateSecret,
6075
5966
  getAsyncContextMode,
6076
5967
  getCookie,
@@ -6135,7 +6026,7 @@ export {
6135
6026
  join as pathJoin,
6136
6027
  resolve as pathResolve,
6137
6028
  pathToFileURL,
6138
- pemToPublicJwk2 as pemToPublicJwk,
6029
+ pemToPublicJwk3 as pemToPublicJwk,
6139
6030
  processPlatformToolCalls,
6140
6031
  randomBytes,
6141
6032
  randomUUID,
@@ -6150,9 +6041,9 @@ export {
6150
6041
  resetRuntimeContext,
6151
6042
  resolveSecretPath,
6152
6043
  rm,
6153
- rsaSignBase64Url2 as rsaSignBase64Url,
6154
- rsaVerify2 as rsaVerify,
6155
- rsaVerifySync2 as rsaVerifySync,
6044
+ rsaSignBase64Url3 as rsaSignBase64Url,
6045
+ rsaVerify3 as rsaVerify,
6046
+ rsaVerifySync3 as rsaVerifySync,
6156
6047
  runCmd,
6157
6048
  runRequestExclusive,
6158
6049
  safeExec,
package/index.d.ts CHANGED
@@ -27,7 +27,7 @@ export { getEnv, getNodeEnv, getCwd, isProduction, isDevelopment, getEnvFlag, is
27
27
  export type { RuntimeContext, EntryAvailability } from './env';
28
28
  export { analyzePattern, isPatternSafe, createSafeRegExp, safeTest, safeMatch, safeReplace, safeExec, isInputLengthSafe, DEFAULT_MAX_INPUT_LENGTH, REDOS_THRESHOLDS, trimLeading, trimTrailing, trimBoth, trimChars, extractBracedParams, expandTemplate, hasTemplatePlaceholders, collapseChar, collapseWhitespace, type SafeRegexOptions, type PatternAnalysisResult, } from './regex';
29
29
  export { createStorage, createMemoryStorage, getDetectedStorageType, NamespacedStorageImpl, createRootStorage, createNamespacedStorage, buildPrefix, NAMESPACE_SEPARATOR, StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError, EncryptedStorageError, BaseStorageAdapter, MemoryStorageAdapter, RedisStorageAdapter, createRedisClient, VercelKvStorageAdapter, createVercelKvClient, type VercelKvConnection, UpstashStorageAdapter, FileSystemStorageAdapter, type FileSystemAdapterOptions, TypedStorage, type TypedStorageOptions, type TypedSetOptions, type TypedSetEntry, EncryptedTypedStorage, type EncryptedTypedStorageOptions, type EncryptedSetOptions, type EncryptedSetEntry, type EncryptionKey, type StoredEncryptedBlob, type ClientKeyBinding, globToRegex, matchesPattern, validatePattern, escapeGlob, MAX_TTL_SECONDS, validateTTL, validateOptionalTTL, ttlToExpiresAt, expiresAtToTTL, isExpired, normalizeTTL, attachRedisErrorListener, DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS, mergeRedisUrlFields, describeRedisUrlConflicts, } from './storage';
30
- export type { StorageAdapter, NamespacedStorage, RootStorage, SetOptions, SetEntry, MessageHandler, Unsubscribe, MemoryAdapterOptions, RedisAdapterOptions, RedisHostConnectionConfig, RedisUrlConnectionConfig, RedisUrlFillInConfig, VercelKvAdapterOptions, UpstashAdapterOptions, StorageType, StorageConfig, RedisErrorListenerOptions, RedisUrlMerge, RedisUrlSiblingFields, CreateRedisClientOptions, ErrorEmitterClient, } from './storage';
30
+ export type { StorageAdapter, NamespacedStorage, RootStorage, SetOptions, SetEntry, MessageHandler, Unsubscribe, MemoryAdapterOptions, RedisAdapterOptions, RedisHostConnectionConfig, RedisUrlConnectionConfig, RedisUrlFillInConfig, VercelKvAdapterOptions, UpstashAdapterOptions, StorageType, StorageConfig, RedisErrorListenerOptions, RedisUrlConnection, RedisUrlMerge, RedisUrlSiblingFields, CreateRedisClientOptions, ErrorEmitterClient, } from './storage';
31
31
  export { processPlatformToolCalls } from './llm';
32
32
  export type { CallToolFn, OpenAIToolCallItem, OpenAIToolResponse, ClaudeToolUseBlock, ClaudeToolResultBlock, VercelToolCallInfo, PlatformToolCallsInput, PlatformToolCallsOutput, SupportedPlatform, } from './llm';
33
33
  export { getMachineId, setMachineIdOverride } from './machine-id';
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
- rsaVerifyBrowser: () => rsaVerifyBrowser
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 import_sha2, import_hmac, import_hkdf, import_utils, import_aes, browserCrypto;
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
- import_sha2 = require("@noble/hashes/sha2.js");
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 import_crypto_provider2.cryptoProvider;
2688
+ return platformCrypto.cryptoProvider;
2832
2689
  }
2833
- function rsaVerify2(jwtAlg, data, publicJwk, signature) {
2690
+ function rsaVerify3(jwtAlg, data, publicJwk, signature) {
2834
2691
  if (isNode()) {
2835
- return Promise.resolve((init_node(), __toCommonJS(node_exports)).rsaVerify(jwtAlg, data, publicJwk, signature));
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 rsaSignBase64Url2(jwtAlg, data, privateJwk) {
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 (init_node(), __toCommonJS(node_exports)).rsaSignBase64Url(jwtAlg, data, privateJwk);
2701
+ return platformCrypto.rsaSignBase64Url(jwtAlg, data, privateJwk);
2845
2702
  }
2846
- function rsaVerifySync2(jwtAlg, data, publicJwk, signature) {
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 (init_node(), __toCommonJS(node_exports)).rsaVerifySync(jwtAlg, data, publicJwk, signature);
2707
+ return platformCrypto.rsaVerifySync(jwtAlg, data, publicJwk, signature);
2851
2708
  }
2852
- function pemToPublicJwk2(pem) {
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 (init_node(), __toCommonJS(node_exports)).pemToPublicJwk(pem);
2713
+ return platformCrypto.pemToPublicJwk(pem);
2857
2714
  }
2858
- function generateRsaKeyPair2(modulusLength, alg) {
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 (init_node(), __toCommonJS(node_exports)).generateRsaKeyPair(modulusLength, alg);
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 import_crypto_provider2, HKDF_SHA256_MAX_LENGTH;
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
- import_crypto_provider2 = require("#crypto-provider");
2829
+ platformCrypto = __toESM(require("#crypto-provider"));
2973
2830
  init_runtime();
2974
2831
  init_jwt_alg();
2975
2832
  init_runtime();
@@ -3025,37 +2882,55 @@ function decodeOrRaw(encoded) {
3025
2882
  return encoded;
3026
2883
  }
3027
2884
  }
3028
- function mergeRedisUrlFields(rawUrl, fields) {
2885
+ function integerOrUndefined(text) {
2886
+ const value = Number.parseInt(text ?? "", 10);
2887
+ return Number.isNaN(value) ? void 0 : value;
2888
+ }
2889
+ function readRedisUrl(rawUrl) {
3029
2890
  let url;
3030
2891
  try {
3031
2892
  url = new URL(rawUrl);
3032
2893
  } catch {
3033
2894
  return void 0;
3034
2895
  }
3035
- const urlHost = url.hostname.replace(/^\[(.*)\]$/, "$1");
3036
- const urlPort = url.port ? Number(url.port) : void 0;
3037
- const urlPassword = url.password ? decodeOrRaw(url.password) : void 0;
3038
- const urlDbText = url.pathname.replace(/^\/+/, "") || url.searchParams.get("db") || void 0;
3039
- const urlDb = urlDbText === void 0 ? void 0 : Number(urlDbText);
3040
- const urlUsesTls = url.protocol === "rediss:";
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;
3041
2916
  const fillIns = {};
3042
2917
  const conflicts = [];
3043
- if (fields.host !== void 0 && fields.host !== urlHost) conflicts.push("host");
2918
+ if (fields.host !== void 0 && fields.host !== connection.host) conflicts.push("host");
3044
2919
  if (fields.port !== void 0) {
3045
- if (urlPort === void 0) fillIns.port = fields.port;
3046
- else if (fields.port !== urlPort) conflicts.push("port");
2920
+ if (connection.port === void 0) fillIns.port = fields.port;
2921
+ else if (fields.port !== connection.port) conflicts.push("port");
3047
2922
  }
3048
2923
  if (fields.password !== void 0) {
3049
- if (urlPassword === void 0) fillIns.password = fields.password;
3050
- else if (fields.password !== urlPassword) conflicts.push("password");
2924
+ if (connection.password === void 0) fillIns.password = fields.password;
2925
+ else if (fields.password !== connection.password) conflicts.push("password");
3051
2926
  }
3052
2927
  if (fields.db !== void 0) {
3053
- if (urlDb === void 0) fillIns.db = fields.db;
3054
- else if (fields.db !== urlDb) conflicts.push("db");
2928
+ if (connection.db === void 0) fillIns.db = fields.db;
2929
+ else if (fields.db !== connection.db) conflicts.push("db");
3055
2930
  }
3056
- if (fields.tls === true && !urlUsesTls) fillIns.tls = true;
3057
- if (fields.tls === false && urlUsesTls) conflicts.push("tls");
3058
- return { fillIns, conflicts };
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 };
3059
2934
  }
3060
2935
  function describeRedisUrlConflicts(conflicts) {
3061
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.`;
@@ -3105,28 +2980,49 @@ function getRedisClass() {
3105
2980
  });
3106
2981
  }
3107
2982
  }
3108
- function resolveUrlFillIns(connectionUrl, options) {
2983
+ function resolveUrlOptions(connectionUrl, options) {
3109
2984
  const { url: configUrl, ...siblingFields } = { url: void 0, ...options.config };
3110
2985
  if (options.url && configUrl && configUrl !== options.url) {
3111
2986
  throw new StorageConfigError("redis", "redis.url and redis.config.url name different servers; set one of them.");
3112
2987
  }
3113
- const merge = mergeRedisUrlFields(connectionUrl, siblingFields);
3114
- if (merge && merge.conflicts.length > 0) {
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) {
3115
2994
  throw new StorageConfigError("redis", describeRedisUrlConflicts(merge.conflicts));
3116
2995
  }
3117
- return merge?.fillIns ?? {};
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
+ };
3118
3006
  }
3119
3007
  function createRedisClient(options) {
3008
+ const urlOptions = options.url ? urlConnectionOptions(options.url, options) : void 0;
3120
3009
  const RedisClass = getRedisClass();
3121
3010
  const baseOptions = { lazyConnect: false, maxRetriesPerRequest: 3 };
3122
- const client = options.url ? new RedisClass(options.url, baseOptions) : new RedisClass({
3123
- ...baseOptions,
3124
- host: options.host ?? "localhost",
3125
- port: options.port ?? 6379,
3126
- password: options.password,
3127
- db: options.db ?? 0,
3128
- tls: options.tls ? {} : void 0
3129
- });
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
+ }
3130
3026
  attachRedisErrorListener(client, { label: options.label ?? "redis", logger: options.logger });
3131
3027
  return client;
3132
3028
  }
@@ -3151,7 +3047,8 @@ var init_redis = __esm({
3151
3047
  keyPrefix;
3152
3048
  subscriptionHandlers = /* @__PURE__ */ new Map();
3153
3049
  connectionUrl;
3154
- urlFillIns;
3050
+ /** The connection options of a URL this adapter reads itself; undefined for one only ioredis reads. */
3051
+ urlOptions;
3155
3052
  constructor(options = {}) {
3156
3053
  super();
3157
3054
  const hasClient = options.client !== void 0;
@@ -3175,7 +3072,7 @@ var init_redis = __esm({
3175
3072
  this.keyPrefix = options.keyPrefix ?? "";
3176
3073
  const configUrl = options.config?.url;
3177
3074
  this.connectionUrl = options.url ?? (typeof configUrl === "string" ? configUrl : void 0);
3178
- this.urlFillIns = this.connectionUrl ? resolveUrlFillIns(this.connectionUrl, options) : {};
3075
+ this.urlOptions = this.connectionUrl ? resolveUrlOptions(this.connectionUrl, options) : void 0;
3179
3076
  }
3180
3077
  // ============================================
3181
3078
  // Connection Lifecycle
@@ -3197,12 +3094,7 @@ var init_redis = __esm({
3197
3094
  if (this.options.client) {
3198
3095
  client = this.options.client;
3199
3096
  } else {
3200
- const RedisClass = getRedisClass();
3201
- if (this.connectionUrl) {
3202
- client = new RedisClass(this.connectionUrl, this.buildRedisOptions());
3203
- } else {
3204
- client = new RedisClass(this.buildRedisOptions());
3205
- }
3097
+ client = this.newClient();
3206
3098
  client.on("error", recordSocketError);
3207
3099
  }
3208
3100
  await client.ping();
@@ -3436,15 +3328,7 @@ var init_redis = __esm({
3436
3328
  */
3437
3329
  buildRedisOptions() {
3438
3330
  if (this.connectionUrl) {
3439
- const { port, password, db, tls } = this.urlFillIns;
3440
- return {
3441
- ...port !== void 0 ? { port } : {},
3442
- ...password !== void 0 ? { password } : {},
3443
- ...db !== void 0 ? { db } : {},
3444
- ...tls ? { tls: {} } : {},
3445
- lazyConnect: false,
3446
- maxRetriesPerRequest: 3
3447
- };
3331
+ return { lazyConnect: false, maxRetriesPerRequest: 3, ...this.urlOptions };
3448
3332
  }
3449
3333
  const config = this.options.config;
3450
3334
  if (!config || typeof config.url === "string" || config.host === void 0) {
@@ -3460,16 +3344,23 @@ var init_redis = __esm({
3460
3344
  maxRetriesPerRequest: 3
3461
3345
  };
3462
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
+ }
3463
3357
  /**
3464
3358
  * Create subscriber connection.
3465
3359
  */
3466
3360
  async createSubscriber() {
3467
- const RedisClass = getRedisClass();
3468
3361
  let subscriber;
3469
- if (this.connectionUrl) {
3470
- subscriber = new RedisClass(this.connectionUrl, this.buildRedisOptions());
3471
- } else if (this.options.config) {
3472
- subscriber = new RedisClass(this.buildRedisOptions());
3362
+ if (this.connectionUrl || this.options.config) {
3363
+ subscriber = this.newClient();
3473
3364
  } else if (this.options.client) {
3474
3365
  subscriber = this.options.client.duplicate();
3475
3366
  } else {
@@ -4297,7 +4188,7 @@ __export(index_exports, {
4297
4188
  generateCodeChallenge: () => generateCodeChallenge,
4298
4189
  generateCodeVerifier: () => generateCodeVerifier,
4299
4190
  generatePkcePair: () => generatePkcePair,
4300
- generateRsaKeyPair: () => generateRsaKeyPair2,
4191
+ generateRsaKeyPair: () => generateRsaKeyPair3,
4301
4192
  generateSecret: () => generateSecret,
4302
4193
  getAsyncContextMode: () => import_async_context.getAsyncContextMode,
4303
4194
  getCookie: () => getCookie,
@@ -4362,7 +4253,7 @@ __export(index_exports, {
4362
4253
  pathJoin: () => import_path2.join,
4363
4254
  pathResolve: () => import_path2.resolve,
4364
4255
  pathToFileURL: () => import_path2.pathToFileURL,
4365
- pemToPublicJwk: () => pemToPublicJwk2,
4256
+ pemToPublicJwk: () => pemToPublicJwk3,
4366
4257
  processPlatformToolCalls: () => processPlatformToolCalls,
4367
4258
  randomBytes: () => randomBytes,
4368
4259
  randomUUID: () => randomUUID,
@@ -4377,9 +4268,9 @@ __export(index_exports, {
4377
4268
  resetRuntimeContext: () => import_runtime_context.resetRuntimeContext,
4378
4269
  resolveSecretPath: () => resolveSecretPath,
4379
4270
  rm: () => rm,
4380
- rsaSignBase64Url: () => rsaSignBase64Url2,
4381
- rsaVerify: () => rsaVerify2,
4382
- rsaVerifySync: () => rsaVerifySync2,
4271
+ rsaSignBase64Url: () => rsaSignBase64Url3,
4272
+ rsaVerify: () => rsaVerify3,
4273
+ rsaVerifySync: () => rsaVerifySync3,
4383
4274
  runCmd: () => runCmd,
4384
4275
  runRequestExclusive: () => import_async_context.runRequestExclusive,
4385
4276
  safeExec: () => safeExec,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/utils",
3
- "version": "1.9.2",
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.2",
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
  },
@@ -40,7 +40,8 @@ export declare class RedisStorageAdapter extends BaseStorageAdapter {
40
40
  private readonly keyPrefix;
41
41
  private readonly subscriptionHandlers;
42
42
  private readonly connectionUrl?;
43
- private readonly urlFillIns;
43
+ /** The connection options of a URL this adapter reads itself; undefined for one only ioredis reads. */
44
+ private readonly urlOptions?;
44
45
  constructor(options?: RedisAdapterOptions);
45
46
  connect(): Promise<void>;
46
47
  private openConnection;
@@ -75,6 +76,10 @@ export declare class RedisStorageAdapter extends BaseStorageAdapter {
75
76
  * Build Redis options from config.
76
77
  */
77
78
  private buildRedisOptions;
79
+ /**
80
+ * Create a client this adapter owns. A URL only ioredis reads goes to it as is.
81
+ */
82
+ private newClient;
78
83
  /**
79
84
  * Create subscriber connection.
80
85
  */
@@ -106,6 +111,9 @@ export interface CreateRedisClientOptions {
106
111
  /**
107
112
  * Create a plain ioredis client that reconnects on its own and never emits an unhandled
108
113
  * 'error' event. The caller owns the client and must `quit()`/`disconnect()` it.
114
+ *
115
+ * With a `url`, the other connection fields fill in only what the URL leaves out (port,
116
+ * password, db, tls), and one that contradicts it throws a `StorageConfigError`.
109
117
  */
110
118
  export declare function createRedisClient(options: CreateRedisClientOptions): Redis;
111
119
  export {};
@@ -9,7 +9,7 @@ export { createStorage, createMemoryStorage, getDetectedStorageType } from './fa
9
9
  export { attachRedisErrorListener, DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS } from './redis-error-listener';
10
10
  export type { RedisErrorListenerOptions, ErrorEmitterClient } from './redis-error-listener';
11
11
  export { mergeRedisUrlFields, describeRedisUrlConflicts } from './redis-url';
12
- export type { RedisUrlMerge, RedisUrlSiblingFields } from './redis-url';
12
+ export type { RedisUrlConnection, RedisUrlMerge, RedisUrlSiblingFields } from './redis-url';
13
13
  export { NamespacedStorageImpl, createRootStorage, createNamespacedStorage, buildPrefix, NAMESPACE_SEPARATOR, } from './namespace';
14
14
  export { StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError, EncryptedStorageError, } from './errors';
15
15
  export { TypedStorage } from './typed-storage';
@@ -8,20 +8,42 @@ export interface RedisUrlSiblingFields {
8
8
  db?: number;
9
9
  tls?: boolean;
10
10
  }
11
+ /**
12
+ * What a `redis://` or `rediss://` URL states, read as ioredis reads it: the userinfo, port and path
13
+ * first, then the same names in its query. An empty username or password counts as left out.
14
+ */
15
+ export interface RedisUrlConnection {
16
+ host?: string;
17
+ port?: number;
18
+ username?: string;
19
+ password?: string;
20
+ db?: number;
21
+ tls: boolean;
22
+ /** The query's other options, which ioredis takes as they are (`family` as a number). */
23
+ queryOptions: Record<string, string | number>;
24
+ }
11
25
  /**
12
26
  * Outcome of combining a Redis URL with the fields written beside it.
13
27
  */
14
28
  export interface RedisUrlMerge {
29
+ /** What the URL states. */
30
+ connection: RedisUrlConnection;
15
31
  /** Fields the URL leaves out that the sibling fields supply. */
16
32
  fillIns: Pick<RedisUrlSiblingFields, 'port' | 'password' | 'db' | 'tls'>;
17
33
  /** Sibling fields whose value contradicts the URL. */
18
34
  conflicts: Array<keyof RedisUrlSiblingFields>;
19
35
  }
36
+ /**
37
+ * Read a `redis://` or `rediss://` URL. Returns `undefined` for an unparseable URL or another
38
+ * scheme (such as a socket path), which only ioredis reads.
39
+ */
40
+ export declare function readRedisUrl(rawUrl: string): RedisUrlConnection | undefined;
20
41
  /**
21
42
  * The URL is the base: fields beside it fill in only what it leaves out (a
22
- * port, a password, a database, TLS), and a field that contradicts it is a
23
- * conflict the caller must reject (#768). Returns `undefined` for an
24
- * unparseable URL, which the caller reports on its own terms.
43
+ * port, a password, a database, TLS), and a field that contradicts what it
44
+ * states, in its userinfo, path or query, is a conflict the caller must reject
45
+ * (#768). Returns `undefined` for a URL {@link readRedisUrl} cannot read, which
46
+ * the caller reports on its own terms.
25
47
  */
26
48
  export declare function mergeRedisUrlFields(rawUrl: string, fields: RedisUrlSiblingFields): RedisUrlMerge | undefined;
27
49
  /** Human-readable reason for {@link RedisUrlMerge.conflicts}. */