@frontmcp/utils 1.9.2 → 1.9.4

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/index.js CHANGED
@@ -119,6 +119,9 @@ async function fileExists(p) {
119
119
  return false;
120
120
  }
121
121
  }
122
+ function fileExistsSync(p) {
123
+ return getFs().existsSync(p);
124
+ }
122
125
  async function readJSON(jsonPath) {
123
126
  try {
124
127
  const fsp = getFsp();
@@ -2541,180 +2544,19 @@ var init_key_persistence2 = __esm({
2541
2544
  }
2542
2545
  });
2543
2546
 
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
2547
  // libs/utils/src/crypto/browser.ts
2711
2548
  var browser_exports = {};
2712
2549
  __export(browser_exports, {
2713
2550
  browserCrypto: () => browserCrypto,
2714
2551
  cryptoProvider: () => browserCrypto,
2552
+ generateRsaKeyPair: () => generateRsaKeyPair,
2715
2553
  isRsaPssAlg: () => isRsaPssAlg,
2716
2554
  jwtAlgToWebCryptoAlg: () => jwtAlgToWebCryptoAlg,
2717
- rsaVerifyBrowser: () => rsaVerifyBrowser
2555
+ pemToPublicJwk: () => pemToPublicJwk,
2556
+ rsaSignBase64Url: () => rsaSignBase64Url,
2557
+ rsaVerify: () => rsaVerify,
2558
+ rsaVerifyBrowser: () => rsaVerifyBrowser,
2559
+ rsaVerifySync: () => rsaVerifySync
2718
2560
  });
2719
2561
  function toBytes(data) {
2720
2562
  if (typeof data === "string") {
@@ -2743,6 +2585,24 @@ function constantTimeEqual(a, b) {
2743
2585
  }
2744
2586
  return result === 0;
2745
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
+ }
2746
2606
  async function rsaVerifyBrowser(jwtAlg, data, publicJwk, signature) {
2747
2607
  if (typeof globalThis.crypto?.subtle === "undefined") {
2748
2608
  throw new Error("WebCrypto API (crypto.subtle) is not available in this environment");
@@ -2774,15 +2634,15 @@ function getSaltLength(jwtAlg) {
2774
2634
  return 32;
2775
2635
  }
2776
2636
  }
2777
- var import_sha2, import_hmac, import_hkdf, import_utils, import_aes, browserCrypto;
2637
+ var import_aes, import_hkdf, import_hmac, import_sha2, import_utils, browserCrypto;
2778
2638
  var init_browser = __esm({
2779
2639
  "libs/utils/src/crypto/browser.ts"() {
2780
2640
  "use strict";
2781
- import_sha2 = require("@noble/hashes/sha2.js");
2782
- import_hmac = require("@noble/hashes/hmac.js");
2641
+ import_aes = require("@noble/ciphers/aes.js");
2783
2642
  import_hkdf = require("@noble/hashes/hkdf.js");
2643
+ import_hmac = require("@noble/hashes/hmac.js");
2644
+ import_sha2 = require("@noble/hashes/sha2.js");
2784
2645
  import_utils = require("@noble/hashes/utils.js");
2785
- import_aes = require("@noble/ciphers/aes.js");
2786
2646
  init_jwt_alg();
2787
2647
  init_jwt_alg();
2788
2648
  browserCrypto = {
@@ -2828,38 +2688,38 @@ var init_browser = __esm({
2828
2688
 
2829
2689
  // libs/utils/src/crypto/index.ts
2830
2690
  function getCrypto() {
2831
- return import_crypto_provider2.cryptoProvider;
2691
+ return platformCrypto.cryptoProvider;
2832
2692
  }
2833
- function rsaVerify2(jwtAlg, data, publicJwk, signature) {
2693
+ function rsaVerify3(jwtAlg, data, publicJwk, signature) {
2834
2694
  if (isNode()) {
2835
- return Promise.resolve((init_node(), __toCommonJS(node_exports)).rsaVerify(jwtAlg, data, publicJwk, signature));
2695
+ return Promise.resolve(platformCrypto.rsaVerify(jwtAlg, data, publicJwk, signature));
2836
2696
  }
2837
2697
  const { rsaVerifyBrowser: rsaVerifyBrowser2 } = (init_browser(), __toCommonJS(browser_exports));
2838
2698
  return rsaVerifyBrowser2(jwtAlg, data, publicJwk, signature);
2839
2699
  }
2840
- function rsaSignBase64Url2(jwtAlg, data, privateJwk) {
2700
+ function rsaSignBase64Url3(jwtAlg, data, privateJwk) {
2841
2701
  if (!isNode()) {
2842
2702
  throw new Error("rsaSignBase64Url is only available in Node.js runtimes");
2843
2703
  }
2844
- return (init_node(), __toCommonJS(node_exports)).rsaSignBase64Url(jwtAlg, data, privateJwk);
2704
+ return platformCrypto.rsaSignBase64Url(jwtAlg, data, privateJwk);
2845
2705
  }
2846
- function rsaVerifySync2(jwtAlg, data, publicJwk, signature) {
2706
+ function rsaVerifySync3(jwtAlg, data, publicJwk, signature) {
2847
2707
  if (!isNode()) {
2848
2708
  throw new Error("rsaVerifySync is only available in Node.js runtimes; use the async rsaVerify in browsers");
2849
2709
  }
2850
- return (init_node(), __toCommonJS(node_exports)).rsaVerifySync(jwtAlg, data, publicJwk, signature);
2710
+ return platformCrypto.rsaVerifySync(jwtAlg, data, publicJwk, signature);
2851
2711
  }
2852
- function pemToPublicJwk2(pem) {
2712
+ function pemToPublicJwk3(pem) {
2853
2713
  if (!isNode()) {
2854
2714
  throw new Error("pemToPublicJwk is only available in Node.js runtimes");
2855
2715
  }
2856
- return (init_node(), __toCommonJS(node_exports)).pemToPublicJwk(pem);
2716
+ return platformCrypto.pemToPublicJwk(pem);
2857
2717
  }
2858
- function generateRsaKeyPair2(modulusLength, alg) {
2718
+ function generateRsaKeyPair3(modulusLength, alg) {
2859
2719
  if (!isNode()) {
2860
2720
  throw new Error("generateRsaKeyPair is only available in Node.js runtimes");
2861
2721
  }
2862
- return (init_node(), __toCommonJS(node_exports)).generateRsaKeyPair(modulusLength, alg);
2722
+ return platformCrypto.generateRsaKeyPair(modulusLength, alg);
2863
2723
  }
2864
2724
  function randomUUID() {
2865
2725
  return getCrypto().randomUUID();
@@ -2965,11 +2825,11 @@ function base64Decode(data) {
2965
2825
  function sha256Base64url(data) {
2966
2826
  return base64urlEncode(sha256(data));
2967
2827
  }
2968
- var import_crypto_provider2, HKDF_SHA256_MAX_LENGTH;
2828
+ var platformCrypto, HKDF_SHA256_MAX_LENGTH;
2969
2829
  var init_crypto = __esm({
2970
2830
  "libs/utils/src/crypto/index.ts"() {
2971
2831
  "use strict";
2972
- import_crypto_provider2 = require("#crypto-provider");
2832
+ platformCrypto = __toESM(require("#crypto-provider"));
2973
2833
  init_runtime();
2974
2834
  init_jwt_alg();
2975
2835
  init_runtime();
@@ -3025,37 +2885,55 @@ function decodeOrRaw(encoded) {
3025
2885
  return encoded;
3026
2886
  }
3027
2887
  }
3028
- function mergeRedisUrlFields(rawUrl, fields) {
2888
+ function integerOrUndefined(text) {
2889
+ const value = Number.parseInt(text ?? "", 10);
2890
+ return Number.isNaN(value) ? void 0 : value;
2891
+ }
2892
+ function readRedisUrl(rawUrl) {
3029
2893
  let url;
3030
2894
  try {
3031
2895
  url = new URL(rawUrl);
3032
2896
  } catch {
3033
2897
  return void 0;
3034
2898
  }
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:";
2899
+ if (url.protocol !== "redis:" && url.protocol !== "rediss:") return void 0;
2900
+ const query = Object.fromEntries(url.searchParams);
2901
+ const { host, port, username, password, db, ...otherOptions } = query;
2902
+ const queryOptions = {};
2903
+ for (const [name, value = ""] of Object.entries(otherOptions)) {
2904
+ queryOptions[name] = name === "family" ? integerOrUndefined(value) ?? value : value;
2905
+ }
2906
+ return {
2907
+ host: url.hostname.replace(/^\[(.*)\]$/, "$1") || host,
2908
+ port: integerOrUndefined(url.port || port),
2909
+ username: decodeOrRaw(url.username) || username || void 0,
2910
+ password: decodeOrRaw(url.password) || password || void 0,
2911
+ db: integerOrUndefined(url.pathname.replace(/^\/+/, "") || db),
2912
+ tls: url.protocol === "rediss:",
2913
+ queryOptions
2914
+ };
2915
+ }
2916
+ function mergeRedisUrlFields(rawUrl, fields) {
2917
+ const connection = readRedisUrl(rawUrl);
2918
+ if (!connection) return void 0;
3041
2919
  const fillIns = {};
3042
2920
  const conflicts = [];
3043
- if (fields.host !== void 0 && fields.host !== urlHost) conflicts.push("host");
2921
+ if (fields.host !== void 0 && fields.host !== connection.host) conflicts.push("host");
3044
2922
  if (fields.port !== void 0) {
3045
- if (urlPort === void 0) fillIns.port = fields.port;
3046
- else if (fields.port !== urlPort) conflicts.push("port");
2923
+ if (connection.port === void 0) fillIns.port = fields.port;
2924
+ else if (fields.port !== connection.port) conflicts.push("port");
3047
2925
  }
3048
2926
  if (fields.password !== void 0) {
3049
- if (urlPassword === void 0) fillIns.password = fields.password;
3050
- else if (fields.password !== urlPassword) conflicts.push("password");
2927
+ if (connection.password === void 0) fillIns.password = fields.password;
2928
+ else if (fields.password !== connection.password) conflicts.push("password");
3051
2929
  }
3052
2930
  if (fields.db !== void 0) {
3053
- if (urlDb === void 0) fillIns.db = fields.db;
3054
- else if (fields.db !== urlDb) conflicts.push("db");
2931
+ if (connection.db === void 0) fillIns.db = fields.db;
2932
+ else if (fields.db !== connection.db) conflicts.push("db");
3055
2933
  }
3056
- if (fields.tls === true && !urlUsesTls) fillIns.tls = true;
3057
- if (fields.tls === false && urlUsesTls) conflicts.push("tls");
3058
- return { fillIns, conflicts };
2934
+ if (fields.tls === true && !connection.tls) fillIns.tls = true;
2935
+ if (fields.tls === false && connection.tls) conflicts.push("tls");
2936
+ return { connection, fillIns, conflicts };
3059
2937
  }
3060
2938
  function describeRedisUrlConflicts(conflicts) {
3061
2939
  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 +2983,49 @@ function getRedisClass() {
3105
2983
  });
3106
2984
  }
3107
2985
  }
3108
- function resolveUrlFillIns(connectionUrl, options) {
2986
+ function resolveUrlOptions(connectionUrl, options) {
3109
2987
  const { url: configUrl, ...siblingFields } = { url: void 0, ...options.config };
3110
2988
  if (options.url && configUrl && configUrl !== options.url) {
3111
2989
  throw new StorageConfigError("redis", "redis.url and redis.config.url name different servers; set one of them.");
3112
2990
  }
3113
- const merge = mergeRedisUrlFields(connectionUrl, siblingFields);
3114
- if (merge && merge.conflicts.length > 0) {
2991
+ return urlConnectionOptions(connectionUrl, siblingFields);
2992
+ }
2993
+ function urlConnectionOptions(url, fields) {
2994
+ const merge = mergeRedisUrlFields(url, fields);
2995
+ if (!merge) return void 0;
2996
+ if (merge.conflicts.length > 0) {
3115
2997
  throw new StorageConfigError("redis", describeRedisUrlConflicts(merge.conflicts));
3116
2998
  }
3117
- return merge?.fillIns ?? {};
2999
+ const { queryOptions, tls, ...stated } = merge.connection;
3000
+ return { ...queryOptions, ...stated, ...toFillInOptions(merge.fillIns), ...tls ? { tls: {} } : {} };
3001
+ }
3002
+ function toFillInOptions({ port, password, db, tls }) {
3003
+ return {
3004
+ ...port !== void 0 ? { port } : {},
3005
+ ...password !== void 0 ? { password } : {},
3006
+ ...db !== void 0 ? { db } : {},
3007
+ ...tls ? { tls: {} } : {}
3008
+ };
3118
3009
  }
3119
3010
  function createRedisClient(options) {
3011
+ const urlOptions = options.url ? urlConnectionOptions(options.url, options) : void 0;
3120
3012
  const RedisClass = getRedisClass();
3121
3013
  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
- });
3014
+ let client;
3015
+ if (urlOptions) {
3016
+ client = new RedisClass({ ...baseOptions, ...urlOptions });
3017
+ } else if (options.url) {
3018
+ client = new RedisClass(options.url, baseOptions);
3019
+ } else {
3020
+ client = new RedisClass({
3021
+ ...baseOptions,
3022
+ host: options.host ?? "localhost",
3023
+ port: options.port ?? 6379,
3024
+ password: options.password,
3025
+ db: options.db ?? 0,
3026
+ tls: options.tls ? {} : void 0
3027
+ });
3028
+ }
3130
3029
  attachRedisErrorListener(client, { label: options.label ?? "redis", logger: options.logger });
3131
3030
  return client;
3132
3031
  }
@@ -3151,7 +3050,8 @@ var init_redis = __esm({
3151
3050
  keyPrefix;
3152
3051
  subscriptionHandlers = /* @__PURE__ */ new Map();
3153
3052
  connectionUrl;
3154
- urlFillIns;
3053
+ /** The connection options of a URL this adapter reads itself; undefined for one only ioredis reads. */
3054
+ urlOptions;
3155
3055
  constructor(options = {}) {
3156
3056
  super();
3157
3057
  const hasClient = options.client !== void 0;
@@ -3175,7 +3075,7 @@ var init_redis = __esm({
3175
3075
  this.keyPrefix = options.keyPrefix ?? "";
3176
3076
  const configUrl = options.config?.url;
3177
3077
  this.connectionUrl = options.url ?? (typeof configUrl === "string" ? configUrl : void 0);
3178
- this.urlFillIns = this.connectionUrl ? resolveUrlFillIns(this.connectionUrl, options) : {};
3078
+ this.urlOptions = this.connectionUrl ? resolveUrlOptions(this.connectionUrl, options) : void 0;
3179
3079
  }
3180
3080
  // ============================================
3181
3081
  // Connection Lifecycle
@@ -3197,12 +3097,7 @@ var init_redis = __esm({
3197
3097
  if (this.options.client) {
3198
3098
  client = this.options.client;
3199
3099
  } 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
- }
3100
+ client = this.newClient();
3206
3101
  client.on("error", recordSocketError);
3207
3102
  }
3208
3103
  await client.ping();
@@ -3436,15 +3331,7 @@ var init_redis = __esm({
3436
3331
  */
3437
3332
  buildRedisOptions() {
3438
3333
  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
- };
3334
+ return { lazyConnect: false, maxRetriesPerRequest: 3, ...this.urlOptions };
3448
3335
  }
3449
3336
  const config = this.options.config;
3450
3337
  if (!config || typeof config.url === "string" || config.host === void 0) {
@@ -3460,16 +3347,23 @@ var init_redis = __esm({
3460
3347
  maxRetriesPerRequest: 3
3461
3348
  };
3462
3349
  }
3350
+ /**
3351
+ * Create a client this adapter owns. A URL only ioredis reads goes to it as is.
3352
+ */
3353
+ newClient() {
3354
+ const RedisClass = getRedisClass();
3355
+ if (this.connectionUrl && !this.urlOptions) {
3356
+ return new RedisClass(this.connectionUrl, this.buildRedisOptions());
3357
+ }
3358
+ return new RedisClass(this.buildRedisOptions());
3359
+ }
3463
3360
  /**
3464
3361
  * Create subscriber connection.
3465
3362
  */
3466
3363
  async createSubscriber() {
3467
- const RedisClass = getRedisClass();
3468
3364
  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());
3365
+ if (this.connectionUrl || this.options.config) {
3366
+ subscriber = this.newClient();
3473
3367
  } else if (this.options.client) {
3474
3368
  subscriber = this.options.client.duplicate();
3475
3369
  } else {
@@ -4243,6 +4137,7 @@ __export(index_exports, {
4243
4137
  base64urlDecode: () => base64urlDecode,
4244
4138
  base64urlEncode: () => base64urlEncode,
4245
4139
  basename: () => import_path2.basename,
4140
+ brandClass: () => brandClass,
4246
4141
  buildPrefix: () => buildPrefix,
4247
4142
  buildSetCookie: () => buildSetCookie,
4248
4143
  bytesToHex: () => bytesToHex,
@@ -4293,11 +4188,12 @@ __export(index_exports, {
4293
4188
  extractTemplateParams: () => extractTemplateParams,
4294
4189
  extractUriScheme: () => extractUriScheme,
4295
4190
  fileExists: () => fileExists,
4191
+ fileExistsSync: () => fileExistsSync,
4296
4192
  findNonFiniteNumber: () => findNonFiniteNumber,
4297
4193
  generateCodeChallenge: () => generateCodeChallenge,
4298
4194
  generateCodeVerifier: () => generateCodeVerifier,
4299
4195
  generatePkcePair: () => generatePkcePair,
4300
- generateRsaKeyPair: () => generateRsaKeyPair2,
4196
+ generateRsaKeyPair: () => generateRsaKeyPair3,
4301
4197
  generateSecret: () => generateSecret,
4302
4198
  getAsyncContextMode: () => import_async_context.getAsyncContextMode,
4303
4199
  getCookie: () => getCookie,
@@ -4319,6 +4215,7 @@ __export(index_exports, {
4319
4215
  inferMimeType: () => inferMimeType,
4320
4216
  isAbsolute: () => import_path2.isAbsolute,
4321
4217
  isAsymmetricKeyData: () => isAsymmetricKeyData,
4218
+ isBrandedInstance: () => isBrandedInstance,
4322
4219
  isBrowser: () => isBrowser,
4323
4220
  isDebug: () => import_env2.isDebug,
4324
4221
  isDevelopment: () => import_env2.isDevelopment,
@@ -4362,7 +4259,7 @@ __export(index_exports, {
4362
4259
  pathJoin: () => import_path2.join,
4363
4260
  pathResolve: () => import_path2.resolve,
4364
4261
  pathToFileURL: () => import_path2.pathToFileURL,
4365
- pemToPublicJwk: () => pemToPublicJwk2,
4262
+ pemToPublicJwk: () => pemToPublicJwk3,
4366
4263
  processPlatformToolCalls: () => processPlatformToolCalls,
4367
4264
  randomBytes: () => randomBytes,
4368
4265
  randomUUID: () => randomUUID,
@@ -4377,9 +4274,9 @@ __export(index_exports, {
4377
4274
  resetRuntimeContext: () => import_runtime_context.resetRuntimeContext,
4378
4275
  resolveSecretPath: () => resolveSecretPath,
4379
4276
  rm: () => rm,
4380
- rsaSignBase64Url: () => rsaSignBase64Url2,
4381
- rsaVerify: () => rsaVerify2,
4382
- rsaVerifySync: () => rsaVerifySync2,
4277
+ rsaSignBase64Url: () => rsaSignBase64Url3,
4278
+ rsaVerify: () => rsaVerify3,
4279
+ rsaVerifySync: () => rsaVerifySync3,
4383
4280
  runCmd: () => runCmd,
4384
4281
  runRequestExclusive: () => import_async_context.runRequestExclusive,
4385
4282
  safeExec: () => safeExec,
@@ -5223,6 +5120,30 @@ function safeJsonForScript(value) {
5223
5120
  }
5224
5121
  }
5225
5122
 
5123
+ // libs/utils/src/class-brand/class-brand.ts
5124
+ var CLASS_BRAND = /* @__PURE__ */ Symbol.for("@frontmcp/class-brand");
5125
+ var ordinaryHasInstance = Function.prototype[Symbol.hasInstance];
5126
+ function ownBrandOf(klass) {
5127
+ if (typeof klass !== "function" || !Object.prototype.hasOwnProperty.call(klass, CLASS_BRAND)) return void 0;
5128
+ return klass[CLASS_BRAND];
5129
+ }
5130
+ function classOfPrototype(proto) {
5131
+ return Object.getOwnPropertyDescriptor(proto, "constructor")?.value;
5132
+ }
5133
+ function brandClass(klass, brand) {
5134
+ Object.defineProperty(klass, CLASS_BRAND, { value: brand, enumerable: false, writable: false, configurable: false });
5135
+ }
5136
+ function isBrandedInstance(klass, value) {
5137
+ if (ordinaryHasInstance.call(klass, value)) return true;
5138
+ if (typeof value !== "object" || value === null) return false;
5139
+ const brand = ownBrandOf(klass);
5140
+ if (brand === void 0) return false;
5141
+ for (let proto = Object.getPrototypeOf(value); proto !== null; proto = Object.getPrototypeOf(proto)) {
5142
+ if (ownBrandOf(classOfPrototype(proto)) === brand) return true;
5143
+ }
5144
+ return false;
5145
+ }
5146
+
5226
5147
  // libs/utils/src/serialization/serialization.ts
5227
5148
  function safeStringify(value, space) {
5228
5149
  const seen = /* @__PURE__ */ new WeakSet();
@@ -6223,6 +6144,7 @@ function setMachineIdOverride(id) {
6223
6144
  base64urlDecode,
6224
6145
  base64urlEncode,
6225
6146
  basename,
6147
+ brandClass,
6226
6148
  buildPrefix,
6227
6149
  buildSetCookie,
6228
6150
  bytesToHex,
@@ -6273,6 +6195,7 @@ function setMachineIdOverride(id) {
6273
6195
  extractTemplateParams,
6274
6196
  extractUriScheme,
6275
6197
  fileExists,
6198
+ fileExistsSync,
6276
6199
  findNonFiniteNumber,
6277
6200
  generateCodeChallenge,
6278
6201
  generateCodeVerifier,
@@ -6299,6 +6222,7 @@ function setMachineIdOverride(id) {
6299
6222
  inferMimeType,
6300
6223
  isAbsolute,
6301
6224
  isAsymmetricKeyData,
6225
+ isBrandedInstance,
6302
6226
  isBrowser,
6303
6227
  isDebug,
6304
6228
  isDevelopment,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/utils",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
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.4",
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 {};