@frontmcp/utils 0.0.1 → 0.7.1

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +110 -0
  3. package/content/content.d.ts +43 -0
  4. package/content/index.d.ts +1 -0
  5. package/crypto/browser.d.ts +11 -0
  6. package/crypto/encrypted-blob.d.ts +157 -0
  7. package/crypto/index.d.ts +98 -0
  8. package/crypto/jwt-alg.d.ts +8 -0
  9. package/crypto/node.d.ts +59 -0
  10. package/crypto/pkce/index.d.ts +9 -0
  11. package/crypto/pkce/pkce.d.ts +140 -0
  12. package/crypto/runtime.d.ts +18 -0
  13. package/crypto/secret-persistence/index.d.ts +25 -0
  14. package/crypto/secret-persistence/persistence.d.ts +97 -0
  15. package/crypto/secret-persistence/schema.d.ts +34 -0
  16. package/crypto/secret-persistence/types.d.ts +65 -0
  17. package/crypto/types.d.ts +61 -0
  18. package/escape/escape.d.ts +101 -0
  19. package/escape/index.d.ts +1 -0
  20. package/esm/index.mjs +3264 -0
  21. package/esm/package.json +53 -0
  22. package/fs/fs.d.ts +254 -0
  23. package/fs/index.d.ts +1 -0
  24. package/http/http.d.ts +20 -0
  25. package/http/index.d.ts +1 -0
  26. package/index.d.ts +18 -0
  27. package/index.js +3425 -0
  28. package/naming/index.d.ts +1 -0
  29. package/naming/naming.d.ts +79 -0
  30. package/package.json +3 -2
  31. package/path/index.d.ts +1 -0
  32. package/path/path.d.ts +34 -0
  33. package/regex/index.d.ts +24 -0
  34. package/regex/patterns.d.ts +155 -0
  35. package/regex/safe-regex.d.ts +179 -0
  36. package/serialization/index.d.ts +1 -0
  37. package/serialization/serialization.d.ts +33 -0
  38. package/storage/adapters/base.d.ts +90 -0
  39. package/storage/adapters/index.d.ts +10 -0
  40. package/storage/adapters/memory.d.ts +99 -0
  41. package/storage/adapters/redis.d.ts +88 -0
  42. package/storage/adapters/upstash.d.ts +81 -0
  43. package/storage/adapters/vercel-kv.d.ts +69 -0
  44. package/storage/errors.d.ts +117 -0
  45. package/storage/factory.d.ts +70 -0
  46. package/storage/index.d.ts +13 -0
  47. package/storage/namespace.d.ts +88 -0
  48. package/storage/types.d.ts +428 -0
  49. package/storage/utils/index.d.ts +5 -0
  50. package/storage/utils/pattern.d.ts +71 -0
  51. package/storage/utils/ttl.d.ts +54 -0
  52. package/uri/index.d.ts +2 -0
  53. package/uri/uri-template.d.ts +92 -0
  54. package/uri/uri-validation.d.ts +46 -0
package/esm/index.mjs ADDED
@@ -0,0 +1,3264 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
11
+ var __esm = (fn, res) => function __init() {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+
28
+ // libs/utils/src/crypto/jwt-alg.ts
29
+ function jwtAlgToNodeAlg(jwtAlg) {
30
+ const nodeAlg = JWT_ALG_TO_NODE_DIGEST[jwtAlg];
31
+ if (!nodeAlg) {
32
+ throw new Error(`Unsupported JWT algorithm: ${jwtAlg}`);
33
+ }
34
+ return nodeAlg;
35
+ }
36
+ function isRsaPssAlg(jwtAlg) {
37
+ return jwtAlg.startsWith("PS");
38
+ }
39
+ var JWT_ALG_TO_NODE_DIGEST;
40
+ var init_jwt_alg = __esm({
41
+ "libs/utils/src/crypto/jwt-alg.ts"() {
42
+ "use strict";
43
+ JWT_ALG_TO_NODE_DIGEST = {
44
+ RS256: "RSA-SHA256",
45
+ RS384: "RSA-SHA384",
46
+ RS512: "RSA-SHA512",
47
+ // For RSA-PSS, Node's crypto.sign/verify uses the digest algorithm + explicit PSS padding options.
48
+ PS256: "RSA-SHA256",
49
+ PS384: "RSA-SHA384",
50
+ PS512: "RSA-SHA512"
51
+ };
52
+ }
53
+ });
54
+
55
+ // libs/utils/src/crypto/node.ts
56
+ var node_exports = {};
57
+ __export(node_exports, {
58
+ createSignedJwt: () => createSignedJwt,
59
+ generateRsaKeyPair: () => generateRsaKeyPair,
60
+ isRsaPssAlg: () => isRsaPssAlg,
61
+ jwtAlgToNodeAlg: () => jwtAlgToNodeAlg,
62
+ nodeCrypto: () => nodeCrypto,
63
+ rsaSign: () => rsaSign,
64
+ rsaVerify: () => rsaVerify
65
+ });
66
+ import crypto2 from "node:crypto";
67
+ function toUint8Array(buf) {
68
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
69
+ }
70
+ function toBuffer(data) {
71
+ if (typeof data === "string") {
72
+ return Buffer.from(data, "utf8");
73
+ }
74
+ return Buffer.from(data);
75
+ }
76
+ function generateRsaKeyPair(modulusLength = 2048, alg = "RS256") {
77
+ const kid = `rsa-key-${Date.now()}-${crypto2.randomBytes(8).toString("hex")}`;
78
+ const { privateKey, publicKey } = crypto2.generateKeyPairSync("rsa", {
79
+ modulusLength
80
+ });
81
+ const exported = publicKey.export({ format: "jwk" });
82
+ const publicJwk = {
83
+ ...exported,
84
+ kid,
85
+ alg,
86
+ use: "sig",
87
+ kty: "RSA"
88
+ };
89
+ return { privateKey, publicKey, publicJwk };
90
+ }
91
+ function rsaSign(algorithm, data, privateKey, options) {
92
+ const signingKey = options ? { key: privateKey, ...options } : privateKey;
93
+ return crypto2.sign(algorithm, data, signingKey);
94
+ }
95
+ function rsaVerify(jwtAlg, data, publicJwk, signature) {
96
+ const publicKey = crypto2.createPublicKey({ key: publicJwk, format: "jwk" });
97
+ const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
98
+ const verifyKey = isRsaPssAlg(jwtAlg) ? {
99
+ key: publicKey,
100
+ padding: crypto2.constants.RSA_PKCS1_PSS_PADDING,
101
+ saltLength: crypto2.constants.RSA_PSS_SALTLEN_DIGEST
102
+ } : publicKey;
103
+ return crypto2.verify(nodeAlgorithm, data, verifyKey, signature);
104
+ }
105
+ function createSignedJwt(payload, privateKey, kid, alg = "RS256") {
106
+ const header = { alg, typ: "JWT", kid };
107
+ const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
108
+ const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
109
+ const signatureInput = `${headerB64}.${payloadB64}`;
110
+ const nodeAlgorithm = jwtAlgToNodeAlg(alg);
111
+ const signature = rsaSign(
112
+ nodeAlgorithm,
113
+ Buffer.from(signatureInput),
114
+ privateKey,
115
+ isRsaPssAlg(alg) ? {
116
+ padding: crypto2.constants.RSA_PKCS1_PSS_PADDING,
117
+ saltLength: crypto2.constants.RSA_PSS_SALTLEN_DIGEST
118
+ } : void 0
119
+ );
120
+ const signatureB64 = signature.toString("base64url");
121
+ return `${headerB64}.${payloadB64}.${signatureB64}`;
122
+ }
123
+ var nodeCrypto;
124
+ var init_node = __esm({
125
+ "libs/utils/src/crypto/node.ts"() {
126
+ "use strict";
127
+ init_jwt_alg();
128
+ init_jwt_alg();
129
+ nodeCrypto = {
130
+ randomUUID() {
131
+ return crypto2.randomUUID();
132
+ },
133
+ randomBytes(length) {
134
+ return toUint8Array(crypto2.randomBytes(length));
135
+ },
136
+ sha256(data) {
137
+ const hash = crypto2.createHash("sha256").update(toBuffer(data)).digest();
138
+ return toUint8Array(hash);
139
+ },
140
+ sha256Hex(data) {
141
+ return crypto2.createHash("sha256").update(toBuffer(data)).digest("hex");
142
+ },
143
+ hmacSha256(key, data) {
144
+ const hmac2 = crypto2.createHmac("sha256", Buffer.from(key)).update(Buffer.from(data)).digest();
145
+ return toUint8Array(hmac2);
146
+ },
147
+ hkdfSha256(ikm, salt, info, length) {
148
+ const ikmBuf = Buffer.from(ikm);
149
+ const saltBuf = salt.length > 0 ? Buffer.from(salt) : Buffer.alloc(32);
150
+ const prk = crypto2.createHmac("sha256", saltBuf).update(ikmBuf).digest();
151
+ const hashLen = 32;
152
+ const n = Math.ceil(length / hashLen);
153
+ const chunks = [];
154
+ let prev = Buffer.alloc(0);
155
+ for (let i = 1; i <= n; i++) {
156
+ prev = crypto2.createHmac("sha256", prk).update(Buffer.concat([prev, Buffer.from(info), Buffer.from([i])])).digest();
157
+ chunks.push(prev);
158
+ }
159
+ return toUint8Array(Buffer.concat(chunks).subarray(0, length));
160
+ },
161
+ encryptAesGcm(key, plaintext, iv) {
162
+ const cipher = crypto2.createCipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
163
+ const encrypted = Buffer.concat([cipher.update(Buffer.from(plaintext)), cipher.final()]);
164
+ const tag = cipher.getAuthTag();
165
+ return {
166
+ ciphertext: toUint8Array(encrypted),
167
+ tag: toUint8Array(tag)
168
+ };
169
+ },
170
+ decryptAesGcm(key, ciphertext, iv, tag) {
171
+ const decipher = crypto2.createDecipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
172
+ decipher.setAuthTag(Buffer.from(tag));
173
+ const decrypted = Buffer.concat([decipher.update(Buffer.from(ciphertext)), decipher.final()]);
174
+ return toUint8Array(decrypted);
175
+ },
176
+ timingSafeEqual(a, b) {
177
+ if (a.length !== b.length) return false;
178
+ return crypto2.timingSafeEqual(Buffer.from(a), Buffer.from(b));
179
+ }
180
+ };
181
+ }
182
+ });
183
+
184
+ // libs/utils/src/crypto/browser.ts
185
+ var browser_exports = {};
186
+ __export(browser_exports, {
187
+ browserCrypto: () => browserCrypto
188
+ });
189
+ import { sha256 as sha256Hash } from "@noble/hashes/sha2.js";
190
+ import { hmac } from "@noble/hashes/hmac.js";
191
+ import { hkdf } from "@noble/hashes/hkdf.js";
192
+ import { randomBytes as nobleRandomBytes } from "@noble/hashes/utils.js";
193
+ import { gcm } from "@noble/ciphers/aes.js";
194
+ function toBytes(data) {
195
+ if (typeof data === "string") {
196
+ return new TextEncoder().encode(data);
197
+ }
198
+ return data;
199
+ }
200
+ function toHex(bytes) {
201
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
202
+ }
203
+ function generateUUID() {
204
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
205
+ return crypto.randomUUID();
206
+ }
207
+ const bytes = nobleRandomBytes(16);
208
+ bytes[6] = bytes[6] & 15 | 64;
209
+ bytes[8] = bytes[8] & 63 | 128;
210
+ const hex = toHex(bytes);
211
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
212
+ }
213
+ function constantTimeEqual(a, b) {
214
+ if (a.length !== b.length) return false;
215
+ let result = 0;
216
+ for (let i = 0; i < a.length; i++) {
217
+ result |= a[i] ^ b[i];
218
+ }
219
+ return result === 0;
220
+ }
221
+ var browserCrypto;
222
+ var init_browser = __esm({
223
+ "libs/utils/src/crypto/browser.ts"() {
224
+ "use strict";
225
+ browserCrypto = {
226
+ randomUUID() {
227
+ return generateUUID();
228
+ },
229
+ randomBytes(length) {
230
+ return nobleRandomBytes(length);
231
+ },
232
+ sha256(data) {
233
+ return sha256Hash(toBytes(data));
234
+ },
235
+ sha256Hex(data) {
236
+ return toHex(sha256Hash(toBytes(data)));
237
+ },
238
+ hmacSha256(key, data) {
239
+ return hmac(sha256Hash, key, data);
240
+ },
241
+ hkdfSha256(ikm, salt, info, length) {
242
+ const effectiveSalt = salt.length > 0 ? salt : new Uint8Array(32);
243
+ return hkdf(sha256Hash, ikm, effectiveSalt, info, length);
244
+ },
245
+ encryptAesGcm(key, plaintext, iv) {
246
+ const cipher = gcm(key, iv);
247
+ const sealed = cipher.encrypt(plaintext);
248
+ const ciphertext = sealed.slice(0, -16);
249
+ const tag = sealed.slice(-16);
250
+ return { ciphertext, tag };
251
+ },
252
+ decryptAesGcm(key, ciphertext, iv, tag) {
253
+ const cipher = gcm(key, iv);
254
+ const sealed = new Uint8Array(ciphertext.length + tag.length);
255
+ sealed.set(ciphertext);
256
+ sealed.set(tag, ciphertext.length);
257
+ return cipher.decrypt(sealed);
258
+ },
259
+ timingSafeEqual(a, b) {
260
+ return constantTimeEqual(a, b);
261
+ }
262
+ };
263
+ }
264
+ });
265
+
266
+ // libs/utils/src/storage/errors.ts
267
+ var StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError;
268
+ var init_errors = __esm({
269
+ "libs/utils/src/storage/errors.ts"() {
270
+ "use strict";
271
+ StorageError = class extends Error {
272
+ cause;
273
+ constructor(message, cause) {
274
+ super(message);
275
+ this.cause = cause;
276
+ this.name = "StorageError";
277
+ Object.setPrototypeOf(this, new.target.prototype);
278
+ if (Error.captureStackTrace) {
279
+ Error.captureStackTrace(this, this.constructor);
280
+ }
281
+ }
282
+ };
283
+ StorageConnectionError = class extends StorageError {
284
+ constructor(message, cause, backend) {
285
+ super(message, cause);
286
+ this.backend = backend;
287
+ this.name = "StorageConnectionError";
288
+ }
289
+ };
290
+ StorageOperationError = class extends StorageError {
291
+ constructor(operation, key, message, cause) {
292
+ super(`${operation} failed for key "${key}": ${message}`, cause);
293
+ this.operation = operation;
294
+ this.key = key;
295
+ this.name = "StorageOperationError";
296
+ }
297
+ };
298
+ StorageNotSupportedError = class extends StorageError {
299
+ constructor(operation, backend, suggestion) {
300
+ const msg = suggestion ? `${operation} is not supported by ${backend}. ${suggestion}` : `${operation} is not supported by ${backend}`;
301
+ super(msg);
302
+ this.operation = operation;
303
+ this.backend = backend;
304
+ this.name = "StorageNotSupportedError";
305
+ }
306
+ };
307
+ StorageConfigError = class extends StorageError {
308
+ constructor(backend, message) {
309
+ super(`Invalid ${backend} configuration: ${message}`);
310
+ this.backend = backend;
311
+ this.name = "StorageConfigError";
312
+ }
313
+ };
314
+ StorageTTLError = class extends StorageError {
315
+ constructor(ttl, message) {
316
+ super(message ?? `Invalid TTL value: ${ttl}. TTL must be a positive integer.`);
317
+ this.ttl = ttl;
318
+ this.name = "StorageTTLError";
319
+ }
320
+ };
321
+ StoragePatternError = class extends StorageError {
322
+ constructor(pattern, message) {
323
+ super(`Invalid pattern "${pattern}": ${message}`);
324
+ this.pattern = pattern;
325
+ this.name = "StoragePatternError";
326
+ }
327
+ };
328
+ StorageNotConnectedError = class extends StorageError {
329
+ constructor(backend) {
330
+ super(`Storage backend "${backend}" is not connected. Call connect() first.`);
331
+ this.backend = backend;
332
+ this.name = "StorageNotConnectedError";
333
+ }
334
+ };
335
+ }
336
+ });
337
+
338
+ // libs/utils/src/storage/utils/ttl.ts
339
+ function validateTTL(ttlSeconds) {
340
+ if (typeof ttlSeconds !== "number") {
341
+ throw new StorageTTLError(ttlSeconds, `TTL must be a number, got ${typeof ttlSeconds}`);
342
+ }
343
+ if (!Number.isFinite(ttlSeconds)) {
344
+ throw new StorageTTLError(ttlSeconds, "TTL must be a finite number");
345
+ }
346
+ if (!Number.isInteger(ttlSeconds)) {
347
+ throw new StorageTTLError(ttlSeconds, `TTL must be an integer, got ${ttlSeconds}`);
348
+ }
349
+ if (ttlSeconds <= 0) {
350
+ throw new StorageTTLError(ttlSeconds, `TTL must be positive, got ${ttlSeconds}`);
351
+ }
352
+ if (ttlSeconds > MAX_TTL_SECONDS) {
353
+ throw new StorageTTLError(ttlSeconds, `TTL exceeds maximum of ${MAX_TTL_SECONDS} seconds`);
354
+ }
355
+ }
356
+ function validateOptionalTTL(ttlSeconds) {
357
+ if (ttlSeconds !== void 0) {
358
+ validateTTL(ttlSeconds);
359
+ }
360
+ }
361
+ function ttlToExpiresAt(ttlSeconds) {
362
+ return Date.now() + ttlSeconds * 1e3;
363
+ }
364
+ function expiresAtToTTL(expiresAt) {
365
+ const remaining = Math.ceil((expiresAt - Date.now()) / 1e3);
366
+ return Math.max(0, remaining);
367
+ }
368
+ function isExpired(expiresAt) {
369
+ if (expiresAt === void 0) return false;
370
+ return Date.now() >= expiresAt;
371
+ }
372
+ function normalizeTTL(ttl, unit) {
373
+ return unit === "milliseconds" ? Math.ceil(ttl / 1e3) : ttl;
374
+ }
375
+ var MAX_TTL_SECONDS;
376
+ var init_ttl = __esm({
377
+ "libs/utils/src/storage/utils/ttl.ts"() {
378
+ "use strict";
379
+ init_errors();
380
+ MAX_TTL_SECONDS = 31536e5;
381
+ }
382
+ });
383
+
384
+ // libs/utils/src/storage/adapters/base.ts
385
+ var BaseStorageAdapter;
386
+ var init_base = __esm({
387
+ "libs/utils/src/storage/adapters/base.ts"() {
388
+ "use strict";
389
+ init_errors();
390
+ init_ttl();
391
+ BaseStorageAdapter = class {
392
+ /**
393
+ * Whether the adapter is currently connected.
394
+ */
395
+ connected = false;
396
+ /**
397
+ * Ensure adapter is connected before operations.
398
+ * @throws StorageNotConnectedError if not connected
399
+ */
400
+ ensureConnected() {
401
+ if (!this.connected) {
402
+ throw new StorageNotConnectedError(this.backendName);
403
+ }
404
+ }
405
+ /**
406
+ * Set with validation.
407
+ */
408
+ async set(key, value, options) {
409
+ this.ensureConnected();
410
+ this.validateSetOptions(options);
411
+ return this.doSet(key, value, options);
412
+ }
413
+ // ============================================
414
+ // Batch Operations (default implementations)
415
+ // ============================================
416
+ /**
417
+ * Default mget: sequential gets.
418
+ * Override for more efficient implementations.
419
+ */
420
+ async mget(keys) {
421
+ this.ensureConnected();
422
+ return Promise.all(keys.map((k) => this.get(k)));
423
+ }
424
+ /**
425
+ * Default mset: sequential sets.
426
+ * Override for atomic/pipelined implementations.
427
+ */
428
+ async mset(entries) {
429
+ this.ensureConnected();
430
+ for (const entry of entries) {
431
+ this.validateSetOptions(entry.options);
432
+ }
433
+ await Promise.all(entries.map((e) => this.doSet(e.key, e.value, e.options)));
434
+ }
435
+ /**
436
+ * Default mdelete: sequential deletes.
437
+ * Override for more efficient implementations.
438
+ */
439
+ async mdelete(keys) {
440
+ this.ensureConnected();
441
+ const results = await Promise.all(keys.map((k) => this.delete(k)));
442
+ return results.filter(Boolean).length;
443
+ }
444
+ /**
445
+ * Default count: keys().length.
446
+ * Override for more efficient implementations.
447
+ */
448
+ async count(pattern = "*") {
449
+ const matchedKeys = await this.keys(pattern);
450
+ return matchedKeys.length;
451
+ }
452
+ // ============================================
453
+ // Pub/Sub (default: not supported)
454
+ // ============================================
455
+ /**
456
+ * Default: pub/sub not supported.
457
+ * Override in adapters that support it.
458
+ */
459
+ supportsPubSub() {
460
+ return false;
461
+ }
462
+ /**
463
+ * Default: throw not supported error.
464
+ * Override in adapters that support pub/sub.
465
+ */
466
+ async publish(_channel, _message) {
467
+ throw new StorageNotSupportedError("publish", this.backendName, this.getPubSubSuggestion());
468
+ }
469
+ /**
470
+ * Default: throw not supported error.
471
+ * Override in adapters that support pub/sub.
472
+ */
473
+ async subscribe(_channel, _handler) {
474
+ throw new StorageNotSupportedError("subscribe", this.backendName, this.getPubSubSuggestion());
475
+ }
476
+ /**
477
+ * Get suggestion message for pub/sub not supported error.
478
+ * Override in specific adapters.
479
+ */
480
+ getPubSubSuggestion() {
481
+ return "Use Redis or Upstash adapter for pub/sub support.";
482
+ }
483
+ // ============================================
484
+ // Validation Helpers
485
+ // ============================================
486
+ /**
487
+ * Validate set options.
488
+ */
489
+ validateSetOptions(options) {
490
+ if (!options) return;
491
+ validateOptionalTTL(options.ttlSeconds);
492
+ if (options.ifNotExists && options.ifExists) {
493
+ throw new Error("ifNotExists and ifExists are mutually exclusive");
494
+ }
495
+ }
496
+ };
497
+ }
498
+ });
499
+
500
+ // libs/utils/src/storage/adapters/redis.ts
501
+ var redis_exports = {};
502
+ __export(redis_exports, {
503
+ RedisStorageAdapter: () => RedisStorageAdapter
504
+ });
505
+ function getRedisClass() {
506
+ try {
507
+ return __require("ioredis").default || __require("ioredis");
508
+ } catch {
509
+ throw new Error("ioredis is required for Redis storage adapter. Install it with: npm install ioredis");
510
+ }
511
+ }
512
+ var RedisStorageAdapter;
513
+ var init_redis = __esm({
514
+ "libs/utils/src/storage/adapters/redis.ts"() {
515
+ "use strict";
516
+ init_base();
517
+ init_errors();
518
+ init_ttl();
519
+ RedisStorageAdapter = class extends BaseStorageAdapter {
520
+ backendName = "redis";
521
+ client;
522
+ subscriber;
523
+ options;
524
+ ownsClient;
525
+ keyPrefix;
526
+ subscriptionHandlers = /* @__PURE__ */ new Map();
527
+ constructor(options = {}) {
528
+ super();
529
+ const hasClient = options.client !== void 0;
530
+ const hasConfig = options.config !== void 0 || options.url !== void 0;
531
+ if (hasClient && hasConfig) {
532
+ throw new StorageConfigError("redis", 'Cannot specify both "client" and "config"/"url". Use one or the other.');
533
+ }
534
+ if (!hasClient && !hasConfig) {
535
+ const envUrl = process.env["REDIS_URL"] || process.env["REDIS_HOST"];
536
+ if (envUrl) {
537
+ options = { ...options, url: envUrl };
538
+ } else {
539
+ throw new StorageConfigError(
540
+ "redis",
541
+ 'Either "client", "config", "url", or REDIS_URL environment variable must be provided.'
542
+ );
543
+ }
544
+ }
545
+ this.options = options;
546
+ this.ownsClient = !hasClient;
547
+ this.keyPrefix = options.keyPrefix ?? "";
548
+ }
549
+ // ============================================
550
+ // Connection Lifecycle
551
+ // ============================================
552
+ async connect() {
553
+ if (this.connected) return;
554
+ try {
555
+ if (this.options.client) {
556
+ this.client = this.options.client;
557
+ } else {
558
+ const RedisClass = getRedisClass();
559
+ if (this.options.url) {
560
+ this.client = new RedisClass(this.options.url, this.buildRedisOptions());
561
+ } else {
562
+ this.client = new RedisClass(this.buildRedisOptions());
563
+ }
564
+ }
565
+ await this.client.ping();
566
+ this.connected = true;
567
+ } catch (e) {
568
+ throw new StorageConnectionError("Failed to connect to Redis", e instanceof Error ? e : void 0, "redis");
569
+ }
570
+ }
571
+ async disconnect() {
572
+ if (!this.connected) return;
573
+ if (this.subscriber) {
574
+ await this.subscriber.quit();
575
+ this.subscriber = void 0;
576
+ }
577
+ if (this.ownsClient && this.client) {
578
+ await this.client.quit();
579
+ }
580
+ this.client = void 0;
581
+ this.connected = false;
582
+ this.subscriptionHandlers.clear();
583
+ }
584
+ async ping() {
585
+ if (!this.client) return false;
586
+ try {
587
+ const result = await this.client.ping();
588
+ return result === "PONG";
589
+ } catch {
590
+ return false;
591
+ }
592
+ }
593
+ // ============================================
594
+ // Connection Helpers
595
+ // ============================================
596
+ /**
597
+ * Get the connected Redis client, throwing if not connected.
598
+ */
599
+ getConnectedClient() {
600
+ this.ensureConnected();
601
+ if (!this.client) {
602
+ throw new StorageConnectionError("Redis client not connected", void 0, "redis");
603
+ }
604
+ return this.client;
605
+ }
606
+ /**
607
+ * Get the connected Redis subscriber, throwing if not created.
608
+ */
609
+ getConnectedSubscriber() {
610
+ if (!this.subscriber) {
611
+ throw new StorageConnectionError("Redis subscriber not created", void 0, "redis");
612
+ }
613
+ return this.subscriber;
614
+ }
615
+ // ============================================
616
+ // Core Operations
617
+ // ============================================
618
+ async get(key) {
619
+ return this.getConnectedClient().get(this.prefixKey(key));
620
+ }
621
+ async doSet(key, value, options) {
622
+ const client = this.getConnectedClient();
623
+ const prefixedKey = this.prefixKey(key);
624
+ if (options?.ttlSeconds) {
625
+ if (options.ifNotExists) {
626
+ await client.set(prefixedKey, value, "EX", options.ttlSeconds, "NX");
627
+ } else if (options.ifExists) {
628
+ await client.set(prefixedKey, value, "EX", options.ttlSeconds, "XX");
629
+ } else {
630
+ await client.set(prefixedKey, value, "EX", options.ttlSeconds);
631
+ }
632
+ } else if (options?.ifNotExists) {
633
+ await client.set(prefixedKey, value, "NX");
634
+ } else if (options?.ifExists) {
635
+ await client.set(prefixedKey, value, "XX");
636
+ } else {
637
+ await client.set(prefixedKey, value);
638
+ }
639
+ }
640
+ async delete(key) {
641
+ const result = await this.getConnectedClient().del(this.prefixKey(key));
642
+ return result > 0;
643
+ }
644
+ async exists(key) {
645
+ const result = await this.getConnectedClient().exists(this.prefixKey(key));
646
+ return result > 0;
647
+ }
648
+ // ============================================
649
+ // Batch Operations (pipelined)
650
+ // ============================================
651
+ async mget(keys) {
652
+ if (keys.length === 0) return [];
653
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
654
+ return this.getConnectedClient().mget(...prefixedKeys);
655
+ }
656
+ async mset(entries) {
657
+ if (entries.length === 0) return;
658
+ for (const entry of entries) {
659
+ this.validateSetOptions(entry.options);
660
+ }
661
+ const pipeline = this.getConnectedClient().pipeline();
662
+ for (const entry of entries) {
663
+ const prefixedKey = this.prefixKey(entry.key);
664
+ if (entry.options?.ttlSeconds) {
665
+ if (entry.options.ifNotExists) {
666
+ pipeline.set(prefixedKey, entry.value, "EX", entry.options.ttlSeconds, "NX");
667
+ } else if (entry.options.ifExists) {
668
+ pipeline.set(prefixedKey, entry.value, "EX", entry.options.ttlSeconds, "XX");
669
+ } else {
670
+ pipeline.set(prefixedKey, entry.value, "EX", entry.options.ttlSeconds);
671
+ }
672
+ } else if (entry.options?.ifNotExists) {
673
+ pipeline.set(prefixedKey, entry.value, "NX");
674
+ } else if (entry.options?.ifExists) {
675
+ pipeline.set(prefixedKey, entry.value, "XX");
676
+ } else {
677
+ pipeline.set(prefixedKey, entry.value);
678
+ }
679
+ }
680
+ await pipeline.exec();
681
+ }
682
+ async mdelete(keys) {
683
+ if (keys.length === 0) return 0;
684
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
685
+ return this.getConnectedClient().del(...prefixedKeys);
686
+ }
687
+ // ============================================
688
+ // TTL Operations
689
+ // ============================================
690
+ async expire(key, ttlSeconds) {
691
+ validateTTL(ttlSeconds);
692
+ const result = await this.getConnectedClient().expire(this.prefixKey(key), ttlSeconds);
693
+ return result === 1;
694
+ }
695
+ async ttl(key) {
696
+ const result = await this.getConnectedClient().ttl(this.prefixKey(key));
697
+ if (result === -2) return null;
698
+ return result;
699
+ }
700
+ // ============================================
701
+ // Key Enumeration (SCAN)
702
+ // ============================================
703
+ async keys(pattern = "*") {
704
+ const client = this.getConnectedClient();
705
+ const prefixedPattern = this.prefixKey(pattern);
706
+ const result = [];
707
+ let cursor = "0";
708
+ do {
709
+ const [nextCursor, keys] = await client.scan(cursor, "MATCH", prefixedPattern, "COUNT", 100);
710
+ cursor = nextCursor;
711
+ for (const key of keys) {
712
+ result.push(this.unprefixKey(key));
713
+ }
714
+ } while (cursor !== "0");
715
+ return result;
716
+ }
717
+ // ============================================
718
+ // Atomic Operations
719
+ // ============================================
720
+ async incr(key) {
721
+ return this.getConnectedClient().incr(this.prefixKey(key));
722
+ }
723
+ async decr(key) {
724
+ return this.getConnectedClient().decr(this.prefixKey(key));
725
+ }
726
+ async incrBy(key, amount) {
727
+ return this.getConnectedClient().incrby(this.prefixKey(key), amount);
728
+ }
729
+ // ============================================
730
+ // Pub/Sub
731
+ // ============================================
732
+ supportsPubSub() {
733
+ return true;
734
+ }
735
+ async publish(channel, message) {
736
+ const prefixedChannel = this.prefixKey(channel);
737
+ return this.getConnectedClient().publish(prefixedChannel, message);
738
+ }
739
+ async subscribe(channel, handler) {
740
+ this.ensureConnected();
741
+ const prefixedChannel = this.prefixKey(channel);
742
+ if (!this.subscriber) {
743
+ await this.createSubscriber();
744
+ }
745
+ const subscriber = this.getConnectedSubscriber();
746
+ if (!this.subscriptionHandlers.has(prefixedChannel)) {
747
+ this.subscriptionHandlers.set(prefixedChannel, /* @__PURE__ */ new Set());
748
+ await subscriber.subscribe(prefixedChannel);
749
+ }
750
+ const handlers = this.subscriptionHandlers.get(prefixedChannel);
751
+ if (handlers) {
752
+ handlers.add(handler);
753
+ }
754
+ return async () => {
755
+ const handlers2 = this.subscriptionHandlers.get(prefixedChannel);
756
+ if (handlers2) {
757
+ handlers2.delete(handler);
758
+ if (handlers2.size === 0) {
759
+ this.subscriptionHandlers.delete(prefixedChannel);
760
+ await this.subscriber?.unsubscribe(prefixedChannel);
761
+ }
762
+ }
763
+ };
764
+ }
765
+ // ============================================
766
+ // Internal Helpers
767
+ // ============================================
768
+ /**
769
+ * Build Redis options from config.
770
+ */
771
+ buildRedisOptions() {
772
+ if (this.options.url) {
773
+ return {
774
+ lazyConnect: false,
775
+ maxRetriesPerRequest: 3
776
+ };
777
+ }
778
+ const config = this.options.config;
779
+ if (!config) {
780
+ throw new StorageConfigError("redis", "Redis config is required when URL is not provided");
781
+ }
782
+ return {
783
+ host: config.host,
784
+ port: config.port ?? 6379,
785
+ password: config.password,
786
+ db: config.db ?? 0,
787
+ tls: config.tls ? {} : void 0,
788
+ lazyConnect: false,
789
+ maxRetriesPerRequest: 3
790
+ };
791
+ }
792
+ /**
793
+ * Create subscriber connection.
794
+ */
795
+ async createSubscriber() {
796
+ const RedisClass = getRedisClass();
797
+ let subscriber;
798
+ if (this.options.url) {
799
+ subscriber = new RedisClass(this.options.url);
800
+ } else if (this.options.config) {
801
+ subscriber = new RedisClass(this.buildRedisOptions());
802
+ } else if (this.options.client) {
803
+ subscriber = this.options.client.duplicate();
804
+ } else {
805
+ throw new StorageConfigError("redis", "Cannot create subscriber without url, config, or client");
806
+ }
807
+ subscriber.on("message", (channel, message) => {
808
+ const handlers = this.subscriptionHandlers.get(channel);
809
+ if (handlers) {
810
+ const unprefixedChannel = this.unprefixKey(channel);
811
+ for (const handler of handlers) {
812
+ try {
813
+ handler(message, unprefixedChannel);
814
+ } catch {
815
+ }
816
+ }
817
+ }
818
+ });
819
+ this.subscriber = subscriber;
820
+ }
821
+ /**
822
+ * Add prefix to a key.
823
+ */
824
+ prefixKey(key) {
825
+ return this.keyPrefix + key;
826
+ }
827
+ /**
828
+ * Remove prefix from a key.
829
+ */
830
+ unprefixKey(key) {
831
+ if (this.keyPrefix && key.startsWith(this.keyPrefix)) {
832
+ return key.slice(this.keyPrefix.length);
833
+ }
834
+ return key;
835
+ }
836
+ /**
837
+ * Get the underlying Redis client (for advanced use).
838
+ */
839
+ getClient() {
840
+ return this.client;
841
+ }
842
+ };
843
+ }
844
+ });
845
+
846
+ // libs/utils/src/storage/adapters/vercel-kv.ts
847
+ var vercel_kv_exports = {};
848
+ __export(vercel_kv_exports, {
849
+ VercelKvStorageAdapter: () => VercelKvStorageAdapter
850
+ });
851
+ function getVercelKv() {
852
+ try {
853
+ return __require("@vercel/kv");
854
+ } catch {
855
+ throw new Error("@vercel/kv is required for Vercel KV storage adapter. Install it with: npm install @vercel/kv");
856
+ }
857
+ }
858
+ var VercelKvStorageAdapter;
859
+ var init_vercel_kv = __esm({
860
+ "libs/utils/src/storage/adapters/vercel-kv.ts"() {
861
+ "use strict";
862
+ init_base();
863
+ init_errors();
864
+ init_ttl();
865
+ VercelKvStorageAdapter = class extends BaseStorageAdapter {
866
+ backendName = "vercel-kv";
867
+ client;
868
+ options;
869
+ keyPrefix;
870
+ constructor(options = {}) {
871
+ super();
872
+ const url = options.url ?? process.env["KV_REST_API_URL"];
873
+ const token = options.token ?? process.env["KV_REST_API_TOKEN"];
874
+ if (!url || !token) {
875
+ throw new StorageConfigError(
876
+ "vercel-kv",
877
+ "KV_REST_API_URL and KV_REST_API_TOKEN must be provided via options or environment variables."
878
+ );
879
+ }
880
+ this.options = { ...options, url, token };
881
+ this.keyPrefix = options.keyPrefix ?? "";
882
+ }
883
+ // ============================================
884
+ // Connection Lifecycle
885
+ // ============================================
886
+ async connect() {
887
+ if (this.connected) return;
888
+ try {
889
+ const { createClient, kv } = getVercelKv();
890
+ if (this.options.url === process.env["KV_REST_API_URL"]) {
891
+ this.client = kv;
892
+ } else {
893
+ const url = this.options.url;
894
+ const token = this.options.token;
895
+ if (!url || !token) {
896
+ throw new StorageConfigError("vercel-kv", "URL and token are required");
897
+ }
898
+ this.client = createClient({ url, token });
899
+ }
900
+ await this.client.exists("__healthcheck__");
901
+ this.connected = true;
902
+ } catch (e) {
903
+ throw new StorageConnectionError(
904
+ "Failed to connect to Vercel KV",
905
+ e instanceof Error ? e : void 0,
906
+ "vercel-kv"
907
+ );
908
+ }
909
+ }
910
+ async disconnect() {
911
+ this.client = void 0;
912
+ this.connected = false;
913
+ }
914
+ async ping() {
915
+ if (!this.client) return false;
916
+ try {
917
+ await this.client.exists("__healthcheck__");
918
+ return true;
919
+ } catch {
920
+ return false;
921
+ }
922
+ }
923
+ // ============================================
924
+ // Connection Helpers
925
+ // ============================================
926
+ /**
927
+ * Get the connected Vercel KV client, throwing if not connected.
928
+ */
929
+ getConnectedClient() {
930
+ this.ensureConnected();
931
+ if (!this.client) {
932
+ throw new StorageConnectionError("Vercel KV client not connected", void 0, "vercel-kv");
933
+ }
934
+ return this.client;
935
+ }
936
+ // ============================================
937
+ // Core Operations
938
+ // ============================================
939
+ async get(key) {
940
+ const result = await this.getConnectedClient().get(this.prefixKey(key));
941
+ return result;
942
+ }
943
+ async doSet(key, value, options) {
944
+ const client = this.getConnectedClient();
945
+ const prefixedKey = this.prefixKey(key);
946
+ const setOptions = {};
947
+ if (options?.ttlSeconds) {
948
+ setOptions.ex = options.ttlSeconds;
949
+ }
950
+ if (options?.ifNotExists) {
951
+ setOptions.nx = true;
952
+ } else if (options?.ifExists) {
953
+ setOptions.xx = true;
954
+ }
955
+ await client.set(prefixedKey, value, Object.keys(setOptions).length > 0 ? setOptions : void 0);
956
+ }
957
+ async delete(key) {
958
+ const result = await this.getConnectedClient().del(this.prefixKey(key));
959
+ return result > 0;
960
+ }
961
+ async exists(key) {
962
+ const result = await this.getConnectedClient().exists(this.prefixKey(key));
963
+ return result > 0;
964
+ }
965
+ // ============================================
966
+ // Batch Operations
967
+ // ============================================
968
+ async mget(keys) {
969
+ if (keys.length === 0) return [];
970
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
971
+ return this.getConnectedClient().mget(...prefixedKeys);
972
+ }
973
+ async mdelete(keys) {
974
+ if (keys.length === 0) return 0;
975
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
976
+ return this.getConnectedClient().del(...prefixedKeys);
977
+ }
978
+ // ============================================
979
+ // TTL Operations
980
+ // ============================================
981
+ async expire(key, ttlSeconds) {
982
+ validateTTL(ttlSeconds);
983
+ const result = await this.getConnectedClient().expire(this.prefixKey(key), ttlSeconds);
984
+ return result === 1;
985
+ }
986
+ async ttl(key) {
987
+ const result = await this.getConnectedClient().ttl(this.prefixKey(key));
988
+ if (result === -2) return null;
989
+ return result;
990
+ }
991
+ // ============================================
992
+ // Key Enumeration
993
+ // ============================================
994
+ async keys(pattern = "*") {
995
+ const client = this.getConnectedClient();
996
+ const prefixedPattern = this.prefixKey(pattern);
997
+ try {
998
+ const result = [];
999
+ let cursor = 0;
1000
+ do {
1001
+ const [nextCursor, keys] = await client.scan(cursor, {
1002
+ match: prefixedPattern,
1003
+ count: 100
1004
+ });
1005
+ const parsedCursor = typeof nextCursor === "string" ? parseInt(nextCursor, 10) : nextCursor;
1006
+ cursor = Number.isNaN(parsedCursor) ? 0 : parsedCursor;
1007
+ for (const key of keys) {
1008
+ result.push(this.unprefixKey(key));
1009
+ }
1010
+ } while (cursor !== 0);
1011
+ return result;
1012
+ } catch {
1013
+ const allKeys = await client.keys(prefixedPattern);
1014
+ return allKeys.map((k) => this.unprefixKey(k));
1015
+ }
1016
+ }
1017
+ // ============================================
1018
+ // Atomic Operations
1019
+ // ============================================
1020
+ async incr(key) {
1021
+ return this.getConnectedClient().incr(this.prefixKey(key));
1022
+ }
1023
+ async decr(key) {
1024
+ return this.getConnectedClient().decr(this.prefixKey(key));
1025
+ }
1026
+ async incrBy(key, amount) {
1027
+ return this.getConnectedClient().incrby(this.prefixKey(key), amount);
1028
+ }
1029
+ // ============================================
1030
+ // Pub/Sub (NOT SUPPORTED)
1031
+ // ============================================
1032
+ supportsPubSub() {
1033
+ return false;
1034
+ }
1035
+ getPubSubSuggestion() {
1036
+ return "Vercel KV is REST-based and does not support pub/sub. Use Upstash adapter for pub/sub support.";
1037
+ }
1038
+ // ============================================
1039
+ // Internal Helpers
1040
+ // ============================================
1041
+ /**
1042
+ * Add prefix to a key.
1043
+ */
1044
+ prefixKey(key) {
1045
+ return this.keyPrefix + key;
1046
+ }
1047
+ /**
1048
+ * Remove prefix from a key.
1049
+ */
1050
+ unprefixKey(key) {
1051
+ if (this.keyPrefix && key.startsWith(this.keyPrefix)) {
1052
+ return key.slice(this.keyPrefix.length);
1053
+ }
1054
+ return key;
1055
+ }
1056
+ };
1057
+ }
1058
+ });
1059
+
1060
+ // libs/utils/src/storage/adapters/upstash.ts
1061
+ var upstash_exports = {};
1062
+ __export(upstash_exports, {
1063
+ UpstashStorageAdapter: () => UpstashStorageAdapter
1064
+ });
1065
+ function getUpstashRedis() {
1066
+ try {
1067
+ return __require("@upstash/redis");
1068
+ } catch {
1069
+ throw new Error(
1070
+ "@upstash/redis is required for Upstash storage adapter. Install it with: npm install @upstash/redis"
1071
+ );
1072
+ }
1073
+ }
1074
+ var PUBSUB_POLL_INTERVAL_MS, UpstashStorageAdapter;
1075
+ var init_upstash = __esm({
1076
+ "libs/utils/src/storage/adapters/upstash.ts"() {
1077
+ "use strict";
1078
+ init_base();
1079
+ init_errors();
1080
+ init_ttl();
1081
+ PUBSUB_POLL_INTERVAL_MS = 100;
1082
+ UpstashStorageAdapter = class extends BaseStorageAdapter {
1083
+ backendName = "upstash";
1084
+ client;
1085
+ options;
1086
+ keyPrefix;
1087
+ pubSubEnabled;
1088
+ // Pub/sub state
1089
+ subscriptionHandlers = /* @__PURE__ */ new Map();
1090
+ pollingIntervals = /* @__PURE__ */ new Map();
1091
+ constructor(options = {}) {
1092
+ super();
1093
+ const url = options.url ?? process.env["UPSTASH_REDIS_REST_URL"];
1094
+ const token = options.token ?? process.env["UPSTASH_REDIS_REST_TOKEN"];
1095
+ if (!url || !token) {
1096
+ throw new StorageConfigError(
1097
+ "upstash",
1098
+ "UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN must be provided via options or environment variables."
1099
+ );
1100
+ }
1101
+ this.options = { ...options, url, token };
1102
+ this.keyPrefix = options.keyPrefix ?? "";
1103
+ this.pubSubEnabled = options.enablePubSub ?? false;
1104
+ }
1105
+ // ============================================
1106
+ // Connection Lifecycle
1107
+ // ============================================
1108
+ async connect() {
1109
+ if (this.connected) return;
1110
+ try {
1111
+ const { Redis } = getUpstashRedis();
1112
+ const url = this.options.url;
1113
+ const token = this.options.token;
1114
+ if (!url || !token) {
1115
+ throw new StorageConfigError("upstash", "URL and token are required");
1116
+ }
1117
+ this.client = new Redis({ url, token });
1118
+ await this.client.exists("__healthcheck__");
1119
+ this.connected = true;
1120
+ } catch (e) {
1121
+ throw new StorageConnectionError(
1122
+ "Failed to connect to Upstash Redis",
1123
+ e instanceof Error ? e : void 0,
1124
+ "upstash"
1125
+ );
1126
+ }
1127
+ }
1128
+ async disconnect() {
1129
+ if (!this.connected) return;
1130
+ for (const interval of this.pollingIntervals.values()) {
1131
+ clearInterval(interval);
1132
+ }
1133
+ this.pollingIntervals.clear();
1134
+ this.subscriptionHandlers.clear();
1135
+ this.client = void 0;
1136
+ this.connected = false;
1137
+ }
1138
+ async ping() {
1139
+ if (!this.client) return false;
1140
+ try {
1141
+ await this.client.exists("__healthcheck__");
1142
+ return true;
1143
+ } catch {
1144
+ return false;
1145
+ }
1146
+ }
1147
+ // ============================================
1148
+ // Connection Helpers
1149
+ // ============================================
1150
+ /**
1151
+ * Get the connected Upstash client, throwing if not connected.
1152
+ */
1153
+ getConnectedClient() {
1154
+ this.ensureConnected();
1155
+ if (!this.client) {
1156
+ throw new StorageConnectionError("Upstash client not connected", void 0, "upstash");
1157
+ }
1158
+ return this.client;
1159
+ }
1160
+ // ============================================
1161
+ // Core Operations
1162
+ // ============================================
1163
+ async get(key) {
1164
+ const result = await this.getConnectedClient().get(this.prefixKey(key));
1165
+ return result;
1166
+ }
1167
+ async doSet(key, value, options) {
1168
+ const client = this.getConnectedClient();
1169
+ const prefixedKey = this.prefixKey(key);
1170
+ const setOptions = {};
1171
+ if (options?.ttlSeconds) {
1172
+ setOptions.ex = options.ttlSeconds;
1173
+ }
1174
+ if (options?.ifNotExists) {
1175
+ setOptions.nx = true;
1176
+ } else if (options?.ifExists) {
1177
+ setOptions.xx = true;
1178
+ }
1179
+ await client.set(prefixedKey, value, Object.keys(setOptions).length > 0 ? setOptions : void 0);
1180
+ }
1181
+ async delete(key) {
1182
+ const result = await this.getConnectedClient().del(this.prefixKey(key));
1183
+ return result > 0;
1184
+ }
1185
+ async exists(key) {
1186
+ const result = await this.getConnectedClient().exists(this.prefixKey(key));
1187
+ return result > 0;
1188
+ }
1189
+ // ============================================
1190
+ // Batch Operations
1191
+ // ============================================
1192
+ async mget(keys) {
1193
+ if (keys.length === 0) return [];
1194
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
1195
+ return this.getConnectedClient().mget(...prefixedKeys);
1196
+ }
1197
+ async mdelete(keys) {
1198
+ if (keys.length === 0) return 0;
1199
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
1200
+ return this.getConnectedClient().del(...prefixedKeys);
1201
+ }
1202
+ // ============================================
1203
+ // TTL Operations
1204
+ // ============================================
1205
+ async expire(key, ttlSeconds) {
1206
+ validateTTL(ttlSeconds);
1207
+ const result = await this.getConnectedClient().expire(this.prefixKey(key), ttlSeconds);
1208
+ return result === 1;
1209
+ }
1210
+ async ttl(key) {
1211
+ const result = await this.getConnectedClient().ttl(this.prefixKey(key));
1212
+ if (result === -2) return null;
1213
+ return result;
1214
+ }
1215
+ // ============================================
1216
+ // Key Enumeration
1217
+ // ============================================
1218
+ async keys(pattern = "*") {
1219
+ const client = this.getConnectedClient();
1220
+ const prefixedPattern = this.prefixKey(pattern);
1221
+ const result = [];
1222
+ let cursor = 0;
1223
+ do {
1224
+ const [nextCursor, keys] = await client.scan(cursor, {
1225
+ match: prefixedPattern,
1226
+ count: 100
1227
+ });
1228
+ const parsedCursor = typeof nextCursor === "string" ? parseInt(nextCursor, 10) : nextCursor;
1229
+ cursor = Number.isNaN(parsedCursor) ? 0 : parsedCursor;
1230
+ for (const key of keys) {
1231
+ result.push(this.unprefixKey(key));
1232
+ }
1233
+ } while (cursor !== 0);
1234
+ return result;
1235
+ }
1236
+ // ============================================
1237
+ // Atomic Operations
1238
+ // ============================================
1239
+ async incr(key) {
1240
+ return this.getConnectedClient().incr(this.prefixKey(key));
1241
+ }
1242
+ async decr(key) {
1243
+ return this.getConnectedClient().decr(this.prefixKey(key));
1244
+ }
1245
+ async incrBy(key, amount) {
1246
+ return this.getConnectedClient().incrby(this.prefixKey(key), amount);
1247
+ }
1248
+ // ============================================
1249
+ // Pub/Sub (via list-based polling)
1250
+ // ============================================
1251
+ supportsPubSub() {
1252
+ return this.pubSubEnabled;
1253
+ }
1254
+ async publish(channel, message) {
1255
+ if (!this.pubSubEnabled) {
1256
+ return super.publish(channel, message);
1257
+ }
1258
+ const prefixedChannel = this.prefixKey(`__pubsub__:${channel}`);
1259
+ const listKey = `${prefixedChannel}:queue`;
1260
+ await this.getConnectedClient().lpush(listKey, message);
1261
+ return 1;
1262
+ }
1263
+ async subscribe(channel, handler) {
1264
+ const client = this.getConnectedClient();
1265
+ if (!this.pubSubEnabled) {
1266
+ return super.subscribe(channel, handler);
1267
+ }
1268
+ const prefixedChannel = this.prefixKey(`__pubsub__:${channel}`);
1269
+ if (!this.subscriptionHandlers.has(prefixedChannel)) {
1270
+ this.subscriptionHandlers.set(prefixedChannel, /* @__PURE__ */ new Set());
1271
+ const listKey = `${prefixedChannel}:queue`;
1272
+ const interval = setInterval(async () => {
1273
+ try {
1274
+ const message = await client.rpop(listKey);
1275
+ if (message) {
1276
+ const handlers2 = this.subscriptionHandlers.get(prefixedChannel);
1277
+ if (handlers2) {
1278
+ for (const h of handlers2) {
1279
+ try {
1280
+ h(message, channel);
1281
+ } catch {
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+ } catch {
1287
+ }
1288
+ }, PUBSUB_POLL_INTERVAL_MS);
1289
+ if (interval.unref) {
1290
+ interval.unref();
1291
+ }
1292
+ this.pollingIntervals.set(prefixedChannel, interval);
1293
+ }
1294
+ const handlers = this.subscriptionHandlers.get(prefixedChannel);
1295
+ if (handlers) {
1296
+ handlers.add(handler);
1297
+ }
1298
+ return async () => {
1299
+ const handlers2 = this.subscriptionHandlers.get(prefixedChannel);
1300
+ if (handlers2) {
1301
+ handlers2.delete(handler);
1302
+ if (handlers2.size === 0) {
1303
+ this.subscriptionHandlers.delete(prefixedChannel);
1304
+ const interval = this.pollingIntervals.get(prefixedChannel);
1305
+ if (interval) {
1306
+ clearInterval(interval);
1307
+ this.pollingIntervals.delete(prefixedChannel);
1308
+ }
1309
+ }
1310
+ }
1311
+ };
1312
+ }
1313
+ /**
1314
+ * Publish using list-based approach (for polling subscribers).
1315
+ * This is an alternative to native PUBLISH when subscribers are polling.
1316
+ */
1317
+ async publishToQueue(channel, message) {
1318
+ const prefixedChannel = this.prefixKey(`__pubsub__:${channel}`);
1319
+ const listKey = `${prefixedChannel}:queue`;
1320
+ await this.getConnectedClient().lpush(listKey, message);
1321
+ }
1322
+ getPubSubSuggestion() {
1323
+ return "Enable pub/sub by setting enablePubSub: true in the adapter options.";
1324
+ }
1325
+ // ============================================
1326
+ // Internal Helpers
1327
+ // ============================================
1328
+ /**
1329
+ * Add prefix to a key.
1330
+ */
1331
+ prefixKey(key) {
1332
+ return this.keyPrefix + key;
1333
+ }
1334
+ /**
1335
+ * Remove prefix from a key.
1336
+ */
1337
+ unprefixKey(key) {
1338
+ if (this.keyPrefix && key.startsWith(this.keyPrefix)) {
1339
+ return key.slice(this.keyPrefix.length);
1340
+ }
1341
+ return key;
1342
+ }
1343
+ };
1344
+ }
1345
+ });
1346
+
1347
+ // libs/utils/src/regex/safe-regex.ts
1348
+ import { analyzeForReDoS, REDOS_THRESHOLDS } from "ast-guard";
1349
+ var DEFAULT_MAX_INPUT_LENGTH = 5e4;
1350
+ function analyzePattern(pattern, level = "polynomial") {
1351
+ const patternStr = typeof pattern === "string" ? pattern : pattern.source;
1352
+ try {
1353
+ new RegExp(patternStr);
1354
+ } catch {
1355
+ return {
1356
+ safe: false,
1357
+ score: 100,
1358
+ vulnerabilityType: "invalid_syntax",
1359
+ explanation: "Pattern has invalid regex syntax"
1360
+ };
1361
+ }
1362
+ try {
1363
+ const result = analyzeForReDoS(patternStr, level);
1364
+ return {
1365
+ safe: !result.vulnerable,
1366
+ score: result.score,
1367
+ vulnerabilityType: result.vulnerabilityType,
1368
+ explanation: result.explanation
1369
+ };
1370
+ } catch {
1371
+ return {
1372
+ safe: false,
1373
+ score: 100,
1374
+ vulnerabilityType: "analysis_failed",
1375
+ explanation: "Pattern analysis failed - treating as potentially unsafe"
1376
+ };
1377
+ }
1378
+ }
1379
+ function isPatternSafe(pattern, level = "polynomial") {
1380
+ return analyzePattern(pattern, level).safe;
1381
+ }
1382
+ function createSafeRegExp(pattern, flags, options = {}) {
1383
+ const { level = "polynomial", throwOnVulnerable = false } = options;
1384
+ const analysis = analyzePattern(pattern, level);
1385
+ if (!analysis.safe) {
1386
+ if (throwOnVulnerable) {
1387
+ throw new Error(`Vulnerable regex pattern detected: ${analysis.explanation || analysis.vulnerabilityType}`);
1388
+ }
1389
+ return null;
1390
+ }
1391
+ try {
1392
+ return new RegExp(pattern, flags);
1393
+ } catch {
1394
+ return null;
1395
+ }
1396
+ }
1397
+ function safeTest(pattern, input, options = {}) {
1398
+ const { maxInputLength = DEFAULT_MAX_INPUT_LENGTH } = options;
1399
+ if (input.length > maxInputLength) {
1400
+ return null;
1401
+ }
1402
+ return pattern.test(input);
1403
+ }
1404
+ function safeMatch(pattern, input, options = {}) {
1405
+ const { maxInputLength = DEFAULT_MAX_INPUT_LENGTH } = options;
1406
+ if (input.length > maxInputLength) {
1407
+ return null;
1408
+ }
1409
+ return input.match(pattern);
1410
+ }
1411
+ function safeReplace(input, pattern, replacement, options = {}) {
1412
+ const { maxInputLength = DEFAULT_MAX_INPUT_LENGTH } = options;
1413
+ if (input.length > maxInputLength) {
1414
+ return input;
1415
+ }
1416
+ return input.replace(pattern, replacement);
1417
+ }
1418
+ function safeExec(pattern, input, options = {}) {
1419
+ const { maxInputLength = DEFAULT_MAX_INPUT_LENGTH } = options;
1420
+ if (input.length > maxInputLength) {
1421
+ return null;
1422
+ }
1423
+ return pattern.exec(input);
1424
+ }
1425
+ function isInputLengthSafe(input, maxLength = DEFAULT_MAX_INPUT_LENGTH) {
1426
+ return input.length <= maxLength;
1427
+ }
1428
+
1429
+ // libs/utils/src/regex/patterns.ts
1430
+ function trimLeading(input, char) {
1431
+ if (!input || char.length !== 1) {
1432
+ return input ?? "";
1433
+ }
1434
+ let start = 0;
1435
+ while (start < input.length && input[start] === char) {
1436
+ start++;
1437
+ }
1438
+ return start === 0 ? input : input.slice(start);
1439
+ }
1440
+ function trimTrailing(input, char) {
1441
+ if (!input || char.length !== 1) {
1442
+ return input ?? "";
1443
+ }
1444
+ let end = input.length;
1445
+ while (end > 0 && input[end - 1] === char) {
1446
+ end--;
1447
+ }
1448
+ return end === input.length ? input : input.slice(0, end);
1449
+ }
1450
+ function trimBoth(input, char) {
1451
+ return trimTrailing(trimLeading(input, char), char);
1452
+ }
1453
+ function trimChars(input, chars) {
1454
+ if (!input || chars.size === 0) {
1455
+ return input ?? "";
1456
+ }
1457
+ let start = 0;
1458
+ while (start < input.length && chars.has(input[start])) {
1459
+ start++;
1460
+ }
1461
+ let end = input.length;
1462
+ while (end > start && chars.has(input[end - 1])) {
1463
+ end--;
1464
+ }
1465
+ return input.slice(start, end);
1466
+ }
1467
+ function extractBracedParams(template, maxLength = DEFAULT_MAX_INPUT_LENGTH) {
1468
+ if (!template || template.length > maxLength) {
1469
+ return [];
1470
+ }
1471
+ const params = [];
1472
+ let i = 0;
1473
+ while (i < template.length) {
1474
+ const openBrace = template.indexOf("{", i);
1475
+ if (openBrace === -1) {
1476
+ break;
1477
+ }
1478
+ const closeBrace = template.indexOf("}", openBrace + 1);
1479
+ if (closeBrace === -1) {
1480
+ break;
1481
+ }
1482
+ const nestedOpen = template.indexOf("{", openBrace + 1);
1483
+ if (nestedOpen !== -1 && nestedOpen < closeBrace) {
1484
+ i = nestedOpen + 1;
1485
+ continue;
1486
+ }
1487
+ const paramName = template.slice(openBrace + 1, closeBrace).trim();
1488
+ if (paramName && !paramName.includes("{") && !paramName.includes("}")) {
1489
+ params.push(paramName);
1490
+ }
1491
+ i = closeBrace + 1;
1492
+ }
1493
+ return params;
1494
+ }
1495
+ function expandTemplate(template, values, maxLength = DEFAULT_MAX_INPUT_LENGTH) {
1496
+ if (!template || template.length > maxLength) {
1497
+ return template ?? "";
1498
+ }
1499
+ let result = "";
1500
+ let i = 0;
1501
+ while (i < template.length) {
1502
+ const openBrace = template.indexOf("{", i);
1503
+ if (openBrace === -1) {
1504
+ result += template.slice(i);
1505
+ break;
1506
+ }
1507
+ result += template.slice(i, openBrace);
1508
+ const closeBrace = template.indexOf("}", openBrace + 1);
1509
+ if (closeBrace === -1) {
1510
+ result += template.slice(openBrace);
1511
+ break;
1512
+ }
1513
+ const nestedOpen = template.indexOf("{", openBrace + 1);
1514
+ if (nestedOpen !== -1 && nestedOpen < closeBrace) {
1515
+ result += "{";
1516
+ i = openBrace + 1;
1517
+ continue;
1518
+ }
1519
+ const paramName = template.slice(openBrace + 1, closeBrace).trim();
1520
+ if (paramName in values) {
1521
+ result += values[paramName];
1522
+ } else {
1523
+ result += template.slice(openBrace, closeBrace + 1);
1524
+ }
1525
+ i = closeBrace + 1;
1526
+ }
1527
+ return result;
1528
+ }
1529
+ function hasTemplatePlaceholders(str, maxLength = DEFAULT_MAX_INPUT_LENGTH) {
1530
+ if (!str || str.length > maxLength) {
1531
+ return false;
1532
+ }
1533
+ const openBrace = str.indexOf("{");
1534
+ if (openBrace === -1) {
1535
+ return false;
1536
+ }
1537
+ const closeBrace = str.indexOf("}", openBrace + 1);
1538
+ return closeBrace > openBrace + 1;
1539
+ }
1540
+ function collapseChar(input, char) {
1541
+ if (!input || char.length !== 1) {
1542
+ return input ?? "";
1543
+ }
1544
+ let result = "";
1545
+ let lastWasChar = false;
1546
+ for (let i = 0; i < input.length; i++) {
1547
+ const c = input[i];
1548
+ if (c === char) {
1549
+ if (!lastWasChar) {
1550
+ result += c;
1551
+ lastWasChar = true;
1552
+ }
1553
+ } else {
1554
+ result += c;
1555
+ lastWasChar = false;
1556
+ }
1557
+ }
1558
+ return result;
1559
+ }
1560
+ function collapseWhitespace(input, maxLength = DEFAULT_MAX_INPUT_LENGTH) {
1561
+ if (!input) {
1562
+ return "";
1563
+ }
1564
+ if (input.length > maxLength) {
1565
+ return input;
1566
+ }
1567
+ let result = "";
1568
+ let lastWasWhitespace = false;
1569
+ for (let i = 0; i < input.length; i++) {
1570
+ const c = input[i];
1571
+ const isWhitespace = c === " " || c === " " || c === "\n" || c === "\r" || c === "\f" || c === "\v";
1572
+ if (isWhitespace) {
1573
+ if (!lastWasWhitespace) {
1574
+ result += " ";
1575
+ lastWasWhitespace = true;
1576
+ }
1577
+ } else {
1578
+ result += c;
1579
+ lastWasWhitespace = false;
1580
+ }
1581
+ }
1582
+ return result;
1583
+ }
1584
+
1585
+ // libs/utils/src/naming/naming.ts
1586
+ function splitWords(input) {
1587
+ const parts = [];
1588
+ let buff = "";
1589
+ for (let i = 0; i < input.length; i++) {
1590
+ const ch = input[i];
1591
+ const isAlphaNum = /[A-Za-z0-9]/.test(ch);
1592
+ if (!isAlphaNum) {
1593
+ if (buff) {
1594
+ parts.push(buff);
1595
+ buff = "";
1596
+ }
1597
+ continue;
1598
+ }
1599
+ if (buff && /[a-z]/.test(buff[buff.length - 1]) && /[A-Z]/.test(ch)) {
1600
+ parts.push(buff);
1601
+ buff = ch;
1602
+ } else {
1603
+ buff += ch;
1604
+ }
1605
+ }
1606
+ if (buff) parts.push(buff);
1607
+ return parts;
1608
+ }
1609
+ function toCase(words, kind) {
1610
+ const safe = words.filter(Boolean);
1611
+ switch (kind) {
1612
+ case "snake":
1613
+ return safe.map((w) => w.toLowerCase()).join("_");
1614
+ case "kebab":
1615
+ return safe.map((w) => w.toLowerCase()).join("-");
1616
+ case "dot":
1617
+ return safe.map((w) => w.toLowerCase()).join(".");
1618
+ case "camel":
1619
+ if (safe.length === 0) return "";
1620
+ return safe[0].toLowerCase() + safe.slice(1).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
1621
+ }
1622
+ }
1623
+ function sepFor(kind) {
1624
+ return kind === "snake" ? "_" : kind === "kebab" ? "-" : kind === "dot" ? "." : "";
1625
+ }
1626
+ function shortHash(s) {
1627
+ let h = 5381;
1628
+ for (let i = 0; i < s.length; i++) h = (h << 5) + h + s.charCodeAt(i);
1629
+ return (h >>> 0).toString(16).slice(-6).padStart(6, "0");
1630
+ }
1631
+ function ensureMaxLen(name, max) {
1632
+ if (name.length <= max) return name;
1633
+ const hash = shortHash(name);
1634
+ const lastSep = Math.max(name.lastIndexOf("_"), name.lastIndexOf("-"), name.lastIndexOf("."), name.lastIndexOf("/"));
1635
+ const tail = lastSep > 0 ? name.slice(lastSep + 1) : name.slice(-Math.max(3, Math.min(16, Math.floor(max / 4))));
1636
+ const budget = Math.max(1, max - (1 + hash.length + 1 + tail.length));
1637
+ const prefix = name.slice(0, budget);
1638
+ return `${prefix}-${hash}-${tail}`.slice(0, max);
1639
+ }
1640
+ function idFromString(name) {
1641
+ const cleaned = name.replace(/[^A-Za-z0-9_-]+/g, "-");
1642
+ return trimBoth(cleaned, "-").slice(0, 64);
1643
+ }
1644
+
1645
+ // libs/utils/src/uri/uri-validation.ts
1646
+ var RFC_3986_SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//;
1647
+ var SCHEME_EXTRACT_PATTERN = /^([a-zA-Z][a-zA-Z0-9+.-]*):/;
1648
+ function isValidMcpUri(uri) {
1649
+ return RFC_3986_SCHEME_PATTERN.test(uri);
1650
+ }
1651
+ function extractUriScheme(uri) {
1652
+ const match = uri.match(SCHEME_EXTRACT_PATTERN);
1653
+ return match ? match[1].toLowerCase() : null;
1654
+ }
1655
+ function isValidMcpUriTemplate(uriTemplate) {
1656
+ return RFC_3986_SCHEME_PATTERN.test(uriTemplate);
1657
+ }
1658
+
1659
+ // libs/utils/src/uri/uri-template.ts
1660
+ function parseUriTemplate(template) {
1661
+ if (template.length > 1e3) {
1662
+ throw new Error("URI template too long (max 1000 characters)");
1663
+ }
1664
+ const paramCount = (template.match(/\{[^{}]+\}/g) || []).length;
1665
+ if (paramCount > 50) {
1666
+ throw new Error("URI template has too many parameters (max 50)");
1667
+ }
1668
+ const paramNames = [];
1669
+ let regexStr = template.replace(/[.*+?^${}()|[\]\\]/g, (match) => {
1670
+ if (match === "{" || match === "}") return match;
1671
+ return "\\" + match;
1672
+ });
1673
+ regexStr = regexStr.replace(/\{([^{}]+)\}/g, (_, paramName) => {
1674
+ paramNames.push(paramName);
1675
+ return "([^/]+)";
1676
+ });
1677
+ return {
1678
+ pattern: new RegExp(`^${regexStr}$`),
1679
+ paramNames
1680
+ };
1681
+ }
1682
+ function matchUriTemplate(template, uri) {
1683
+ const { pattern, paramNames } = parseUriTemplate(template);
1684
+ const match = uri.match(pattern);
1685
+ if (!match) return null;
1686
+ const params = {};
1687
+ paramNames.forEach((name, index) => {
1688
+ params[name] = decodeURIComponent(match[index + 1]);
1689
+ });
1690
+ return params;
1691
+ }
1692
+ function expandUriTemplate(template, params) {
1693
+ return template.replace(/\{([^{}]+)\}/g, (_, paramName) => {
1694
+ const value = params[paramName];
1695
+ if (value === void 0) {
1696
+ throw new Error(`Missing parameter '${paramName}' for URI template '${template}'`);
1697
+ }
1698
+ return encodeURIComponent(value);
1699
+ });
1700
+ }
1701
+ function extractTemplateParams(template) {
1702
+ const params = [];
1703
+ template.replace(/\{([^{}]+)\}/g, (_, paramName) => {
1704
+ params.push(paramName);
1705
+ return "";
1706
+ });
1707
+ return params;
1708
+ }
1709
+ function isUriTemplate(uri) {
1710
+ return /\{[^{}]+\}/.test(uri);
1711
+ }
1712
+
1713
+ // libs/utils/src/path/path.ts
1714
+ function trimSlashes(s) {
1715
+ return trimBoth(s ?? "", "/");
1716
+ }
1717
+ function joinPath(...parts) {
1718
+ const cleaned = parts.map((p) => trimSlashes(p)).filter(Boolean);
1719
+ return cleaned.length ? `/${cleaned.join("/")}` : "";
1720
+ }
1721
+
1722
+ // libs/utils/src/content/content.ts
1723
+ function sanitizeToJson(value) {
1724
+ const seen = /* @__PURE__ */ new WeakSet();
1725
+ function sanitize(val) {
1726
+ if (typeof val === "function" || typeof val === "symbol") {
1727
+ return void 0;
1728
+ }
1729
+ if (typeof val === "bigint") {
1730
+ return val.toString();
1731
+ }
1732
+ if (val instanceof Date) {
1733
+ return val.toISOString();
1734
+ }
1735
+ if (val instanceof Error) {
1736
+ return {
1737
+ name: val.name,
1738
+ message: val.message,
1739
+ stack: val.stack
1740
+ };
1741
+ }
1742
+ if (val instanceof Map) {
1743
+ const obj = {};
1744
+ for (const [k, v] of val.entries()) {
1745
+ obj[String(k)] = sanitize(v);
1746
+ }
1747
+ return obj;
1748
+ }
1749
+ if (val instanceof Set) {
1750
+ return Array.from(val).map(sanitize);
1751
+ }
1752
+ if (Array.isArray(val)) {
1753
+ if (seen.has(val)) {
1754
+ return void 0;
1755
+ }
1756
+ seen.add(val);
1757
+ return val.map(sanitize);
1758
+ }
1759
+ if (val && typeof val === "object") {
1760
+ if (seen.has(val)) {
1761
+ return void 0;
1762
+ }
1763
+ seen.add(val);
1764
+ const sanitized = {};
1765
+ for (const [key, value2] of Object.entries(val)) {
1766
+ const clean = sanitize(value2);
1767
+ if (clean !== void 0) {
1768
+ sanitized[key] = clean;
1769
+ }
1770
+ }
1771
+ return sanitized;
1772
+ }
1773
+ return val;
1774
+ }
1775
+ return sanitize(value);
1776
+ }
1777
+ var MIME_TYPES = {
1778
+ json: "application/json",
1779
+ xml: "application/xml",
1780
+ html: "text/html",
1781
+ htm: "text/html",
1782
+ css: "text/css",
1783
+ js: "application/javascript",
1784
+ ts: "application/typescript",
1785
+ txt: "text/plain",
1786
+ md: "text/markdown",
1787
+ yaml: "application/yaml",
1788
+ yml: "application/yaml",
1789
+ csv: "text/csv",
1790
+ png: "image/png",
1791
+ jpg: "image/jpeg",
1792
+ jpeg: "image/jpeg",
1793
+ gif: "image/gif",
1794
+ svg: "image/svg+xml",
1795
+ pdf: "application/pdf"
1796
+ };
1797
+ function inferMimeType(uri, content) {
1798
+ const ext = uri.split(".").pop()?.toLowerCase();
1799
+ if (ext && MIME_TYPES[ext]) {
1800
+ return MIME_TYPES[ext];
1801
+ }
1802
+ if (typeof content === "string") {
1803
+ if (content.trim().startsWith("{") || content.trim().startsWith("[")) {
1804
+ return "application/json";
1805
+ }
1806
+ if (content.trim().startsWith("<")) {
1807
+ return content.includes("<!DOCTYPE html") || content.includes("<html") ? "text/html" : "application/xml";
1808
+ }
1809
+ }
1810
+ return "text/plain";
1811
+ }
1812
+
1813
+ // libs/utils/src/http/http.ts
1814
+ function validateBaseUrl(url) {
1815
+ try {
1816
+ const parsed = new URL(url);
1817
+ if (!["http:", "https:"].includes(parsed.protocol)) {
1818
+ throw new Error(`Unsupported protocol: ${parsed.protocol}. Only http: and https: are supported.`);
1819
+ }
1820
+ return parsed;
1821
+ } catch (err) {
1822
+ if (err instanceof Error && err.message.includes("Unsupported protocol")) {
1823
+ throw err;
1824
+ }
1825
+ throw new Error(`Invalid base URL: ${url}`);
1826
+ }
1827
+ }
1828
+
1829
+ // libs/utils/src/crypto/runtime.ts
1830
+ function isNode() {
1831
+ return typeof process !== "undefined" && !!process.versions?.node;
1832
+ }
1833
+ function isBrowser() {
1834
+ return typeof globalThis !== "undefined" && typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.getRandomValues === "function";
1835
+ }
1836
+ function assertNode(feature) {
1837
+ if (!isNode()) {
1838
+ throw new Error(`${feature} is not supported in the browser. Requires Node.js.`);
1839
+ }
1840
+ }
1841
+
1842
+ // libs/utils/src/fs/fs.ts
1843
+ var _fsp = null;
1844
+ var _spawn = null;
1845
+ function getFsp() {
1846
+ if (!_fsp) {
1847
+ assertNode("File system operations");
1848
+ _fsp = __require("fs").promises;
1849
+ }
1850
+ return _fsp;
1851
+ }
1852
+ function getSpawn() {
1853
+ if (!_spawn) {
1854
+ assertNode("Child process operations");
1855
+ _spawn = __require("child_process").spawn;
1856
+ }
1857
+ return _spawn;
1858
+ }
1859
+ var F_OK = 0;
1860
+ async function readFile(p, encoding = "utf8") {
1861
+ const fsp = getFsp();
1862
+ return fsp.readFile(p, encoding);
1863
+ }
1864
+ async function readFileBuffer(p) {
1865
+ const fsp = getFsp();
1866
+ return fsp.readFile(p);
1867
+ }
1868
+ async function writeFile(p, content, options) {
1869
+ const fsp = getFsp();
1870
+ await fsp.writeFile(p, content, { encoding: "utf8", mode: options?.mode });
1871
+ }
1872
+ async function mkdir(p, options) {
1873
+ const fsp = getFsp();
1874
+ await fsp.mkdir(p, options);
1875
+ }
1876
+ async function rename(oldPath, newPath) {
1877
+ const fsp = getFsp();
1878
+ await fsp.rename(oldPath, newPath);
1879
+ }
1880
+ async function unlink(p) {
1881
+ const fsp = getFsp();
1882
+ await fsp.unlink(p);
1883
+ }
1884
+ async function fileExists(p) {
1885
+ try {
1886
+ const fsp = getFsp();
1887
+ await fsp.access(p, F_OK);
1888
+ return true;
1889
+ } catch {
1890
+ return false;
1891
+ }
1892
+ }
1893
+ async function readJSON(jsonPath) {
1894
+ try {
1895
+ const fsp = getFsp();
1896
+ const buf = await fsp.readFile(jsonPath, "utf8");
1897
+ return JSON.parse(buf);
1898
+ } catch {
1899
+ return null;
1900
+ }
1901
+ }
1902
+ async function writeJSON(p, obj) {
1903
+ const fsp = getFsp();
1904
+ await fsp.writeFile(p, JSON.stringify(obj, null, 2) + "\n", "utf8");
1905
+ }
1906
+ async function ensureDir(p) {
1907
+ const fsp = getFsp();
1908
+ await fsp.mkdir(p, { recursive: true });
1909
+ }
1910
+ async function stat(p) {
1911
+ const fsp = getFsp();
1912
+ return fsp.stat(p);
1913
+ }
1914
+ async function copyFile(src, dest) {
1915
+ const fsp = getFsp();
1916
+ await fsp.copyFile(src, dest);
1917
+ }
1918
+ async function cp(src, dest, options) {
1919
+ const fsp = getFsp();
1920
+ await fsp.cp(src, dest, options);
1921
+ }
1922
+ async function readdir(p) {
1923
+ const fsp = getFsp();
1924
+ return fsp.readdir(p);
1925
+ }
1926
+ async function rm(p, options) {
1927
+ const fsp = getFsp();
1928
+ await fsp.rm(p, options);
1929
+ }
1930
+ async function isDirEmpty(dir) {
1931
+ try {
1932
+ const fsp = getFsp();
1933
+ const items = await fsp.readdir(dir);
1934
+ return items.length === 0;
1935
+ } catch (e) {
1936
+ if (e?.code === "ENOENT") return true;
1937
+ throw e;
1938
+ }
1939
+ }
1940
+ async function mkdtemp(prefix) {
1941
+ const fsp = getFsp();
1942
+ return fsp.mkdtemp(prefix);
1943
+ }
1944
+ async function access(p, mode) {
1945
+ const fsp = getFsp();
1946
+ await fsp.access(p, mode ?? F_OK);
1947
+ }
1948
+ function runCmd(cmd, args, opts = {}) {
1949
+ const spawn = getSpawn();
1950
+ return new Promise((resolve2, reject) => {
1951
+ const child = spawn(cmd, args, { stdio: "inherit", shell: false, ...opts });
1952
+ child.on("close", (code) => code === 0 ? resolve2() : reject(new Error(`${cmd} exited with code ${code}`)));
1953
+ child.on("error", reject);
1954
+ });
1955
+ }
1956
+
1957
+ // libs/utils/src/escape/escape.ts
1958
+ function escapeHtml(str) {
1959
+ if (str === null || str === void 0) {
1960
+ return "";
1961
+ }
1962
+ const s = String(str);
1963
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1964
+ }
1965
+ function escapeHtmlAttr(str) {
1966
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
1967
+ }
1968
+ function escapeJsString(str) {
1969
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1970
+ }
1971
+ function escapeScriptClose(jsonString) {
1972
+ return jsonString.replace(/<\//g, "<\\/");
1973
+ }
1974
+ function safeJsonForScript(value) {
1975
+ if (value === void 0) {
1976
+ return "null";
1977
+ }
1978
+ try {
1979
+ const jsonString = JSON.stringify(value, (_key, val) => {
1980
+ if (typeof val === "bigint") {
1981
+ return val.toString();
1982
+ }
1983
+ return val;
1984
+ });
1985
+ if (jsonString === void 0) {
1986
+ return "null";
1987
+ }
1988
+ return escapeScriptClose(jsonString);
1989
+ } catch {
1990
+ return '{"error":"Value could not be serialized"}';
1991
+ }
1992
+ }
1993
+
1994
+ // libs/utils/src/serialization/serialization.ts
1995
+ function safeStringify(value, space) {
1996
+ const seen = /* @__PURE__ */ new WeakSet();
1997
+ try {
1998
+ return JSON.stringify(
1999
+ value,
2000
+ (_key, val) => {
2001
+ if (typeof val === "object" && val !== null) {
2002
+ if (seen.has(val)) return "[Circular]";
2003
+ seen.add(val);
2004
+ }
2005
+ return val;
2006
+ },
2007
+ space
2008
+ );
2009
+ } catch {
2010
+ return JSON.stringify({ error: "Output could not be serialized" });
2011
+ }
2012
+ }
2013
+
2014
+ // libs/utils/src/crypto/index.ts
2015
+ init_jwt_alg();
2016
+
2017
+ // libs/utils/src/crypto/encrypted-blob.ts
2018
+ var textEncoder = new TextEncoder();
2019
+ var textDecoder = new TextDecoder();
2020
+ var EncryptedBlobError = class extends Error {
2021
+ constructor(message) {
2022
+ super(message);
2023
+ this.name = "EncryptedBlobError";
2024
+ }
2025
+ };
2026
+ function encryptValue(data, key) {
2027
+ if (key.length !== 32) {
2028
+ throw new EncryptedBlobError(`Encryption key must be 32 bytes (AES-256), got ${key.length} bytes`);
2029
+ }
2030
+ let jsonString;
2031
+ try {
2032
+ jsonString = JSON.stringify(data);
2033
+ } catch (error) {
2034
+ throw new EncryptedBlobError(`Failed to serialize data: ${error.message}`);
2035
+ }
2036
+ const plaintext = textEncoder.encode(jsonString);
2037
+ const iv = randomBytes(12);
2038
+ const { ciphertext, tag } = encryptAesGcm(key, plaintext, iv);
2039
+ return {
2040
+ alg: "A256GCM",
2041
+ iv: base64urlEncode(iv),
2042
+ tag: base64urlEncode(tag),
2043
+ data: base64urlEncode(ciphertext)
2044
+ };
2045
+ }
2046
+ function decryptValue(blob, key) {
2047
+ if (key.length !== 32) {
2048
+ throw new EncryptedBlobError(`Decryption key must be 32 bytes (AES-256), got ${key.length} bytes`);
2049
+ }
2050
+ if (blob.alg !== "A256GCM") {
2051
+ throw new EncryptedBlobError(`Unsupported algorithm: ${blob.alg}, expected A256GCM`);
2052
+ }
2053
+ let iv;
2054
+ let tag;
2055
+ let ciphertext;
2056
+ try {
2057
+ iv = base64urlDecode(blob.iv);
2058
+ tag = base64urlDecode(blob.tag);
2059
+ ciphertext = base64urlDecode(blob.data);
2060
+ } catch (error) {
2061
+ throw new EncryptedBlobError(`Failed to decode blob fields: ${error.message}`);
2062
+ }
2063
+ let decrypted;
2064
+ try {
2065
+ decrypted = decryptAesGcm(key, ciphertext, iv, tag);
2066
+ } catch (error) {
2067
+ throw new EncryptedBlobError(`Decryption failed: ${error.message}`);
2068
+ }
2069
+ try {
2070
+ return JSON.parse(textDecoder.decode(decrypted));
2071
+ } catch (error) {
2072
+ throw new EncryptedBlobError(`Failed to parse decrypted data: ${error.message}`);
2073
+ }
2074
+ }
2075
+ function tryDecryptValue(blob, key) {
2076
+ try {
2077
+ return decryptValue(blob, key);
2078
+ } catch {
2079
+ return null;
2080
+ }
2081
+ }
2082
+ function serializeBlob(blob) {
2083
+ return JSON.stringify(blob);
2084
+ }
2085
+ function deserializeBlob(str) {
2086
+ let parsed;
2087
+ try {
2088
+ parsed = JSON.parse(str);
2089
+ } catch (error) {
2090
+ throw new EncryptedBlobError(`Failed to parse blob JSON: ${error.message}`);
2091
+ }
2092
+ if (!isValidEncryptedBlob(parsed)) {
2093
+ throw new EncryptedBlobError("Invalid encrypted blob structure");
2094
+ }
2095
+ return parsed;
2096
+ }
2097
+ function tryDeserializeBlob(str) {
2098
+ try {
2099
+ return deserializeBlob(str);
2100
+ } catch {
2101
+ return null;
2102
+ }
2103
+ }
2104
+ function isValidEncryptedBlob(value) {
2105
+ return typeof value === "object" && value !== null && "alg" in value && "iv" in value && "tag" in value && "data" in value && value.alg === "A256GCM" && typeof value.iv === "string" && typeof value.tag === "string" && typeof value.data === "string";
2106
+ }
2107
+ function encryptAndSerialize(data, key) {
2108
+ const blob = encryptValue(data, key);
2109
+ return serializeBlob(blob);
2110
+ }
2111
+ function deserializeAndDecrypt(str, key) {
2112
+ const blob = deserializeBlob(str);
2113
+ return decryptValue(blob, key);
2114
+ }
2115
+ function tryDeserializeAndDecrypt(str, key) {
2116
+ try {
2117
+ return deserializeAndDecrypt(str, key);
2118
+ } catch {
2119
+ return null;
2120
+ }
2121
+ }
2122
+
2123
+ // libs/utils/src/crypto/pkce/pkce.ts
2124
+ var MIN_CODE_VERIFIER_LENGTH = 43;
2125
+ var MAX_CODE_VERIFIER_LENGTH = 128;
2126
+ var DEFAULT_CODE_VERIFIER_LENGTH = 64;
2127
+ var PkceError = class extends Error {
2128
+ constructor(message) {
2129
+ super(message);
2130
+ this.name = "PkceError";
2131
+ }
2132
+ };
2133
+ function generateCodeVerifier(length = DEFAULT_CODE_VERIFIER_LENGTH) {
2134
+ if (length < MIN_CODE_VERIFIER_LENGTH || length > MAX_CODE_VERIFIER_LENGTH) {
2135
+ throw new PkceError(
2136
+ `Code verifier length must be between ${MIN_CODE_VERIFIER_LENGTH} and ${MAX_CODE_VERIFIER_LENGTH} characters (got ${length})`
2137
+ );
2138
+ }
2139
+ const bytesNeeded = Math.ceil(length * 6 / 8);
2140
+ const bytes = randomBytes(bytesNeeded);
2141
+ return base64urlEncode(bytes).slice(0, length);
2142
+ }
2143
+ function generateCodeChallenge(codeVerifier) {
2144
+ const verifierBytes = new TextEncoder().encode(codeVerifier);
2145
+ const hash = sha256(verifierBytes);
2146
+ return base64urlEncode(hash);
2147
+ }
2148
+ function verifyCodeChallenge(codeVerifier, codeChallenge) {
2149
+ const computedChallenge = generateCodeChallenge(codeVerifier);
2150
+ return computedChallenge === codeChallenge;
2151
+ }
2152
+ function generatePkcePair(length = DEFAULT_CODE_VERIFIER_LENGTH) {
2153
+ const codeVerifier = generateCodeVerifier(length);
2154
+ const codeChallenge = generateCodeChallenge(codeVerifier);
2155
+ return { codeVerifier, codeChallenge };
2156
+ }
2157
+ function isValidCodeVerifier(value) {
2158
+ if (value.length < MIN_CODE_VERIFIER_LENGTH || value.length > MAX_CODE_VERIFIER_LENGTH) {
2159
+ return false;
2160
+ }
2161
+ return /^[A-Za-z0-9\-._~]+$/.test(value);
2162
+ }
2163
+ function isValidCodeChallenge(value) {
2164
+ if (value.length !== 43) {
2165
+ return false;
2166
+ }
2167
+ return /^[A-Za-z0-9\-_]+$/.test(value);
2168
+ }
2169
+
2170
+ // libs/utils/src/crypto/secret-persistence/schema.ts
2171
+ import { z } from "zod";
2172
+ var secretDataSchema = z.object({
2173
+ secret: z.string().base64url().length(43),
2174
+ // base64url of 32 bytes is exactly 43 chars
2175
+ createdAt: z.number().positive().int(),
2176
+ version: z.number().positive().int()
2177
+ }).strict();
2178
+ var MAX_CLOCK_DRIFT_MS = 6e4;
2179
+ var MAX_SECRET_AGE_MS = 100 * 365 * 24 * 60 * 60 * 1e3;
2180
+ function validateSecretData(data) {
2181
+ const result = secretDataSchema.safeParse(data);
2182
+ if (!result.success) {
2183
+ return {
2184
+ valid: false,
2185
+ error: result.error.issues[0]?.message ?? "Invalid secret structure"
2186
+ };
2187
+ }
2188
+ const parsed = result.data;
2189
+ const now = Date.now();
2190
+ if (parsed.createdAt > now + MAX_CLOCK_DRIFT_MS) {
2191
+ return { valid: false, error: "createdAt is in the future" };
2192
+ }
2193
+ if (parsed.createdAt < now - MAX_SECRET_AGE_MS) {
2194
+ return { valid: false, error: "createdAt is too old" };
2195
+ }
2196
+ return { valid: true };
2197
+ }
2198
+ function parseSecretData(data) {
2199
+ const validation = validateSecretData(data);
2200
+ if (!validation.valid) {
2201
+ return null;
2202
+ }
2203
+ return data;
2204
+ }
2205
+
2206
+ // libs/utils/src/crypto/secret-persistence/persistence.ts
2207
+ import * as path from "path";
2208
+ var DEFAULT_SECRET_BYTES = 32;
2209
+ var DEFAULT_SECRET_DIR = ".frontmcp";
2210
+ function isSecretPersistenceEnabled(options) {
2211
+ const isProduction2 = process.env["NODE_ENV"] === "production";
2212
+ if (isProduction2) {
2213
+ return options?.forceEnable === true;
2214
+ }
2215
+ return true;
2216
+ }
2217
+ function resolveSecretPath(options) {
2218
+ if (options?.secretPath) {
2219
+ if (path.isAbsolute(options.secretPath)) {
2220
+ return options.secretPath;
2221
+ }
2222
+ return path.resolve(process.cwd(), options.secretPath);
2223
+ }
2224
+ const name = options?.name ?? "default";
2225
+ const defaultPath = `${DEFAULT_SECRET_DIR}/${name}-secret.json`;
2226
+ return path.resolve(process.cwd(), defaultPath);
2227
+ }
2228
+ var noop = () => {
2229
+ };
2230
+ function getLogger(options) {
2231
+ if (options?.enableLogging === false) {
2232
+ return { warn: noop, error: noop };
2233
+ }
2234
+ return options?.logger ?? { warn: console.warn, error: console.error };
2235
+ }
2236
+ function getLogPrefix(options) {
2237
+ const name = options?.name ?? "Secret";
2238
+ return `[${name}Persistence]`;
2239
+ }
2240
+ async function loadSecret(options) {
2241
+ if (!isSecretPersistenceEnabled(options)) {
2242
+ return null;
2243
+ }
2244
+ const secretPath = resolveSecretPath(options);
2245
+ const logger = getLogger(options);
2246
+ const prefix = getLogPrefix(options);
2247
+ try {
2248
+ const content = await readFile(secretPath);
2249
+ const data = JSON.parse(content);
2250
+ const validation = validateSecretData(data);
2251
+ if (!validation.valid) {
2252
+ logger.warn(`${prefix} Invalid secret file format at ${secretPath}: ${validation.error}, will regenerate`);
2253
+ return null;
2254
+ }
2255
+ return data;
2256
+ } catch (error) {
2257
+ if (error.code === "ENOENT") {
2258
+ return null;
2259
+ }
2260
+ logger.warn(`${prefix} Failed to load secret from ${secretPath}: ${error.message}`);
2261
+ return null;
2262
+ }
2263
+ }
2264
+ async function saveSecret(secretData, options) {
2265
+ if (!isSecretPersistenceEnabled(options)) {
2266
+ return true;
2267
+ }
2268
+ const secretPath = resolveSecretPath(options);
2269
+ const dir = path.dirname(secretPath);
2270
+ const tempPath = `${secretPath}.tmp.${Date.now()}.${base64urlEncode(randomBytes(8))}`;
2271
+ const logger = getLogger(options);
2272
+ const prefix = getLogPrefix(options);
2273
+ try {
2274
+ await mkdir(dir, { recursive: true, mode: 448 });
2275
+ const content = JSON.stringify(secretData, null, 2);
2276
+ await writeFile(tempPath, content, { mode: 384 });
2277
+ await rename(tempPath, secretPath);
2278
+ return true;
2279
+ } catch (error) {
2280
+ logger.error(`${prefix} Failed to save secret to ${secretPath}: ${error.message}`);
2281
+ try {
2282
+ await unlink(tempPath);
2283
+ } catch {
2284
+ }
2285
+ return false;
2286
+ }
2287
+ }
2288
+ async function deleteSecret(options) {
2289
+ const secretPath = resolveSecretPath(options);
2290
+ const logger = getLogger(options);
2291
+ const prefix = getLogPrefix(options);
2292
+ try {
2293
+ await unlink(secretPath);
2294
+ return true;
2295
+ } catch (error) {
2296
+ if (error.code === "ENOENT") {
2297
+ return true;
2298
+ }
2299
+ logger.warn(`${prefix} Failed to delete secret at ${secretPath}: ${error.message}`);
2300
+ return false;
2301
+ }
2302
+ }
2303
+ function generateSecret(bytes = DEFAULT_SECRET_BYTES) {
2304
+ return base64urlEncode(randomBytes(bytes));
2305
+ }
2306
+ function createSecretData(options) {
2307
+ const secretBytes = options?.secretBytes ?? DEFAULT_SECRET_BYTES;
2308
+ return {
2309
+ secret: generateSecret(secretBytes),
2310
+ createdAt: Date.now(),
2311
+ version: 1
2312
+ };
2313
+ }
2314
+ var secretCache = /* @__PURE__ */ new Map();
2315
+ var generationPromises = /* @__PURE__ */ new Map();
2316
+ async function getOrCreateSecret(options) {
2317
+ const cacheKey = resolveSecretPath(options);
2318
+ const cached = secretCache.get(cacheKey);
2319
+ if (cached) {
2320
+ return cached;
2321
+ }
2322
+ const existing = generationPromises.get(cacheKey);
2323
+ if (existing) {
2324
+ return existing;
2325
+ }
2326
+ const promise = (async () => {
2327
+ try {
2328
+ const loaded = await loadSecret(options);
2329
+ if (loaded) {
2330
+ secretCache.set(cacheKey, loaded.secret);
2331
+ return loaded.secret;
2332
+ }
2333
+ const secretData = createSecretData(options);
2334
+ if (isSecretPersistenceEnabled(options)) {
2335
+ await saveSecret(secretData, options);
2336
+ }
2337
+ secretCache.set(cacheKey, secretData.secret);
2338
+ return secretData.secret;
2339
+ } finally {
2340
+ generationPromises.delete(cacheKey);
2341
+ }
2342
+ })();
2343
+ generationPromises.set(cacheKey, promise);
2344
+ return promise;
2345
+ }
2346
+ function clearCachedSecret(options) {
2347
+ if (options) {
2348
+ const cacheKey = resolveSecretPath(options);
2349
+ secretCache.delete(cacheKey);
2350
+ } else {
2351
+ secretCache.clear();
2352
+ }
2353
+ }
2354
+ function isSecretCached(options) {
2355
+ const cacheKey = resolveSecretPath(options);
2356
+ return secretCache.has(cacheKey);
2357
+ }
2358
+
2359
+ // libs/utils/src/crypto/index.ts
2360
+ var _provider = null;
2361
+ function getCrypto() {
2362
+ if (!_provider) {
2363
+ if (isNode()) {
2364
+ _provider = (init_node(), __toCommonJS(node_exports)).nodeCrypto;
2365
+ } else {
2366
+ _provider = (init_browser(), __toCommonJS(browser_exports)).browserCrypto;
2367
+ }
2368
+ }
2369
+ return _provider;
2370
+ }
2371
+ function rsaVerify2(jwtAlg, data, publicJwk, signature) {
2372
+ assertNode("rsaVerify");
2373
+ return (init_node(), __toCommonJS(node_exports)).rsaVerify(jwtAlg, data, publicJwk, signature);
2374
+ }
2375
+ function randomUUID() {
2376
+ return getCrypto().randomUUID();
2377
+ }
2378
+ function randomBytes(length) {
2379
+ if (!Number.isInteger(length) || length <= 0) {
2380
+ throw new Error(`randomBytes length must be a positive integer, got ${length}`);
2381
+ }
2382
+ return getCrypto().randomBytes(length);
2383
+ }
2384
+ function sha256(data) {
2385
+ return getCrypto().sha256(data);
2386
+ }
2387
+ function sha256Hex(data) {
2388
+ return getCrypto().sha256Hex(data);
2389
+ }
2390
+ function hmacSha256(key, data) {
2391
+ return getCrypto().hmacSha256(key, data);
2392
+ }
2393
+ var HKDF_SHA256_MAX_LENGTH = 255 * 32;
2394
+ function hkdfSha256(ikm, salt, info, length) {
2395
+ if (!Number.isInteger(length) || length <= 0) {
2396
+ throw new Error(`HKDF length must be a positive integer, got ${length}`);
2397
+ }
2398
+ if (length > HKDF_SHA256_MAX_LENGTH) {
2399
+ throw new Error(`HKDF-SHA256 length cannot exceed ${HKDF_SHA256_MAX_LENGTH} bytes, got ${length}`);
2400
+ }
2401
+ return getCrypto().hkdfSha256(ikm, salt, info, length);
2402
+ }
2403
+ function encryptAesGcm(key, plaintext, iv) {
2404
+ if (key.length !== 32) {
2405
+ throw new Error(`AES-256-GCM requires a 32-byte key, got ${key.length} bytes`);
2406
+ }
2407
+ if (iv.length !== 12) {
2408
+ throw new Error(`AES-GCM requires a 12-byte IV, got ${iv.length} bytes`);
2409
+ }
2410
+ return getCrypto().encryptAesGcm(key, plaintext, iv);
2411
+ }
2412
+ function decryptAesGcm(key, ciphertext, iv, tag) {
2413
+ if (key.length !== 32) {
2414
+ throw new Error(`AES-256-GCM requires a 32-byte key, got ${key.length} bytes`);
2415
+ }
2416
+ if (iv.length !== 12) {
2417
+ throw new Error(`AES-GCM requires a 12-byte IV, got ${iv.length} bytes`);
2418
+ }
2419
+ if (tag.length !== 16) {
2420
+ throw new Error(`AES-GCM requires a 16-byte authentication tag, got ${tag.length} bytes`);
2421
+ }
2422
+ return getCrypto().decryptAesGcm(key, ciphertext, iv, tag);
2423
+ }
2424
+ function timingSafeEqual(a, b) {
2425
+ if (a.length !== b.length) {
2426
+ throw new Error(`timingSafeEqual requires equal-length arrays, got ${a.length} and ${b.length} bytes`);
2427
+ }
2428
+ return getCrypto().timingSafeEqual(a, b);
2429
+ }
2430
+ function bytesToHex(data) {
2431
+ return Array.from(data).map((b) => b.toString(16).padStart(2, "0")).join("");
2432
+ }
2433
+ function base64urlEncode(data) {
2434
+ let base64;
2435
+ if (typeof Buffer !== "undefined") {
2436
+ base64 = Buffer.from(data).toString("base64");
2437
+ } else {
2438
+ const binString = Array.from(data, (byte) => String.fromCodePoint(byte)).join("");
2439
+ base64 = btoa(binString);
2440
+ }
2441
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2442
+ }
2443
+ function base64urlDecode(data) {
2444
+ let base64 = data.replace(/-/g, "+").replace(/_/g, "/");
2445
+ const pad = base64.length % 4;
2446
+ if (pad) {
2447
+ base64 += "=".repeat(4 - pad);
2448
+ }
2449
+ if (typeof Buffer !== "undefined") {
2450
+ return new Uint8Array(Buffer.from(base64, "base64"));
2451
+ } else {
2452
+ const binString = atob(base64);
2453
+ return Uint8Array.from(binString, (c) => c.codePointAt(0) ?? 0);
2454
+ }
2455
+ }
2456
+ function sha256Base64url(data) {
2457
+ return base64urlEncode(sha256(data));
2458
+ }
2459
+
2460
+ // libs/utils/src/storage/factory.ts
2461
+ init_errors();
2462
+
2463
+ // libs/utils/src/storage/namespace.ts
2464
+ var NAMESPACE_SEPARATOR = ":";
2465
+ function buildPrefix(name, id) {
2466
+ if (id) {
2467
+ return `${name}${NAMESPACE_SEPARATOR}${id}${NAMESPACE_SEPARATOR}`;
2468
+ }
2469
+ return `${name}${NAMESPACE_SEPARATOR}`;
2470
+ }
2471
+ var NamespacedStorageImpl = class _NamespacedStorageImpl {
2472
+ constructor(adapter, prefix = "", root = adapter) {
2473
+ this.adapter = adapter;
2474
+ this.prefix = prefix;
2475
+ this.root = root;
2476
+ }
2477
+ // ============================================
2478
+ // Key Prefixing Helpers
2479
+ // ============================================
2480
+ /**
2481
+ * Add prefix to a key.
2482
+ */
2483
+ prefixKey(key) {
2484
+ return this.prefix + key;
2485
+ }
2486
+ /**
2487
+ * Remove prefix from a key.
2488
+ */
2489
+ unprefixKey(key) {
2490
+ if (this.prefix && key.startsWith(this.prefix)) {
2491
+ return key.slice(this.prefix.length);
2492
+ }
2493
+ return key;
2494
+ }
2495
+ /**
2496
+ * Add prefix to a pattern (for keys() operation).
2497
+ */
2498
+ prefixPattern(pattern) {
2499
+ return this.prefix + pattern;
2500
+ }
2501
+ /**
2502
+ * Add prefix to a channel (for pub/sub).
2503
+ */
2504
+ prefixChannel(channel) {
2505
+ return this.prefix + channel;
2506
+ }
2507
+ // ============================================
2508
+ // Namespace API
2509
+ // ============================================
2510
+ namespace(name, id) {
2511
+ const newPrefix = this.prefix + buildPrefix(name, id);
2512
+ return new _NamespacedStorageImpl(this.adapter, newPrefix, this.root);
2513
+ }
2514
+ // ============================================
2515
+ // Connection Lifecycle (delegated to root)
2516
+ // ============================================
2517
+ async connect() {
2518
+ return this.adapter.connect();
2519
+ }
2520
+ async disconnect() {
2521
+ return this.adapter.disconnect();
2522
+ }
2523
+ async ping() {
2524
+ return this.adapter.ping();
2525
+ }
2526
+ // ============================================
2527
+ // Core Operations (with prefixing)
2528
+ // ============================================
2529
+ async get(key) {
2530
+ return this.adapter.get(this.prefixKey(key));
2531
+ }
2532
+ async set(key, value, options) {
2533
+ return this.adapter.set(this.prefixKey(key), value, options);
2534
+ }
2535
+ async delete(key) {
2536
+ return this.adapter.delete(this.prefixKey(key));
2537
+ }
2538
+ async exists(key) {
2539
+ return this.adapter.exists(this.prefixKey(key));
2540
+ }
2541
+ // ============================================
2542
+ // Batch Operations (with prefixing)
2543
+ // ============================================
2544
+ async mget(keys) {
2545
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
2546
+ return this.adapter.mget(prefixedKeys);
2547
+ }
2548
+ async mset(entries) {
2549
+ const prefixedEntries = entries.map((e) => ({
2550
+ ...e,
2551
+ key: this.prefixKey(e.key)
2552
+ }));
2553
+ return this.adapter.mset(prefixedEntries);
2554
+ }
2555
+ async mdelete(keys) {
2556
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
2557
+ return this.adapter.mdelete(prefixedKeys);
2558
+ }
2559
+ // ============================================
2560
+ // TTL Operations (with prefixing)
2561
+ // ============================================
2562
+ async expire(key, ttlSeconds) {
2563
+ return this.adapter.expire(this.prefixKey(key), ttlSeconds);
2564
+ }
2565
+ async ttl(key) {
2566
+ return this.adapter.ttl(this.prefixKey(key));
2567
+ }
2568
+ // ============================================
2569
+ // Key Enumeration (with prefixing)
2570
+ // ============================================
2571
+ async keys(pattern = "*") {
2572
+ const prefixedPattern = this.prefixPattern(pattern);
2573
+ const keys = await this.adapter.keys(prefixedPattern);
2574
+ return keys.map((k) => this.unprefixKey(k));
2575
+ }
2576
+ async count(pattern = "*") {
2577
+ const prefixedPattern = this.prefixPattern(pattern);
2578
+ return this.adapter.count(prefixedPattern);
2579
+ }
2580
+ // ============================================
2581
+ // Atomic Operations (with prefixing)
2582
+ // ============================================
2583
+ async incr(key) {
2584
+ return this.adapter.incr(this.prefixKey(key));
2585
+ }
2586
+ async decr(key) {
2587
+ return this.adapter.decr(this.prefixKey(key));
2588
+ }
2589
+ async incrBy(key, amount) {
2590
+ return this.adapter.incrBy(this.prefixKey(key), amount);
2591
+ }
2592
+ // ============================================
2593
+ // Pub/Sub (with channel prefixing)
2594
+ // ============================================
2595
+ async publish(channel, message) {
2596
+ return this.adapter.publish(this.prefixChannel(channel), message);
2597
+ }
2598
+ async subscribe(channel, handler) {
2599
+ const prefixedChannel = this.prefixChannel(channel);
2600
+ const wrappedHandler = (message, ch) => {
2601
+ const unprefixedChannel = ch.startsWith(this.prefix) ? ch.slice(this.prefix.length) : ch;
2602
+ handler(message, unprefixedChannel);
2603
+ };
2604
+ return this.adapter.subscribe(prefixedChannel, wrappedHandler);
2605
+ }
2606
+ supportsPubSub() {
2607
+ return this.adapter.supportsPubSub();
2608
+ }
2609
+ };
2610
+ function createRootStorage(adapter) {
2611
+ return new NamespacedStorageImpl(adapter, "", adapter);
2612
+ }
2613
+ function createNamespacedStorage(adapter, prefix) {
2614
+ const normalizedPrefix = prefix && !prefix.endsWith(NAMESPACE_SEPARATOR) ? prefix + NAMESPACE_SEPARATOR : prefix;
2615
+ return new NamespacedStorageImpl(adapter, normalizedPrefix, adapter);
2616
+ }
2617
+
2618
+ // libs/utils/src/storage/adapters/memory.ts
2619
+ init_base();
2620
+ init_errors();
2621
+ import { EventEmitter } from "events";
2622
+
2623
+ // libs/utils/src/storage/utils/pattern.ts
2624
+ init_errors();
2625
+ var MAX_PATTERN_LENGTH = 500;
2626
+ var MAX_WILDCARDS = 20;
2627
+ var REGEX_SPECIAL_CHARS = /[.+^${}()|[\]\\]/g;
2628
+ function globToRegex(pattern) {
2629
+ if (pattern.length > MAX_PATTERN_LENGTH) {
2630
+ throw new StoragePatternError(
2631
+ pattern.substring(0, 50) + "...",
2632
+ `Pattern exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`
2633
+ );
2634
+ }
2635
+ const wildcardCount = (pattern.match(/[*?]/g) || []).length;
2636
+ if (wildcardCount > MAX_WILDCARDS) {
2637
+ throw new StoragePatternError(pattern, `Pattern has too many wildcards (max: ${MAX_WILDCARDS})`);
2638
+ }
2639
+ if (pattern === "" || pattern === "*") {
2640
+ return /^.*$/;
2641
+ }
2642
+ let regexStr = "^";
2643
+ let prevChar = "";
2644
+ for (let i = 0; i < pattern.length; i++) {
2645
+ const char = pattern[i];
2646
+ switch (char) {
2647
+ case "*":
2648
+ if (prevChar !== "*") {
2649
+ regexStr += ".*";
2650
+ }
2651
+ break;
2652
+ case "?":
2653
+ regexStr += ".";
2654
+ break;
2655
+ default:
2656
+ regexStr += char.replace(REGEX_SPECIAL_CHARS, "\\$&");
2657
+ }
2658
+ prevChar = char;
2659
+ }
2660
+ regexStr += "$";
2661
+ try {
2662
+ return new RegExp(regexStr);
2663
+ } catch {
2664
+ throw new StoragePatternError(pattern, "Failed to compile pattern to regex");
2665
+ }
2666
+ }
2667
+ function matchesPattern(key, pattern) {
2668
+ const regex = globToRegex(pattern);
2669
+ return regex.test(key);
2670
+ }
2671
+ function validatePattern(pattern) {
2672
+ try {
2673
+ globToRegex(pattern);
2674
+ return { valid: true };
2675
+ } catch (e) {
2676
+ return {
2677
+ valid: false,
2678
+ error: e instanceof Error ? e.message : "Invalid pattern"
2679
+ };
2680
+ }
2681
+ }
2682
+ function escapeGlob(literal) {
2683
+ return literal.replace(/[*?\\]/g, "\\$&");
2684
+ }
2685
+
2686
+ // libs/utils/src/storage/adapters/memory.ts
2687
+ init_ttl();
2688
+ var DEFAULT_SWEEP_INTERVAL_SECONDS = 60;
2689
+ var MAX_TIMEOUT_MS = 2147483647;
2690
+ var MemoryStorageAdapter = class extends BaseStorageAdapter {
2691
+ backendName = "memory";
2692
+ store = /* @__PURE__ */ new Map();
2693
+ emitter = new EventEmitter();
2694
+ sweepInterval;
2695
+ options;
2696
+ // LRU tracking (simple linked list via insertion order in Map)
2697
+ accessOrder = [];
2698
+ constructor(options = {}) {
2699
+ super();
2700
+ this.options = {
2701
+ enableSweeper: options.enableSweeper ?? true,
2702
+ sweepIntervalSeconds: options.sweepIntervalSeconds ?? DEFAULT_SWEEP_INTERVAL_SECONDS,
2703
+ maxEntries: options.maxEntries ?? 0
2704
+ // 0 = unlimited
2705
+ };
2706
+ this.emitter.setMaxListeners(1e3);
2707
+ }
2708
+ // ============================================
2709
+ // Connection Lifecycle
2710
+ // ============================================
2711
+ async connect() {
2712
+ if (this.connected) return;
2713
+ this.connected = true;
2714
+ if (this.options.enableSweeper && this.options.sweepIntervalSeconds > 0) {
2715
+ this.startSweeper();
2716
+ }
2717
+ }
2718
+ async disconnect() {
2719
+ if (!this.connected) return;
2720
+ this.stopSweeper();
2721
+ this.clearAllTimeouts();
2722
+ this.store.clear();
2723
+ this.accessOrder = [];
2724
+ this.emitter.removeAllListeners();
2725
+ this.connected = false;
2726
+ }
2727
+ async ping() {
2728
+ return this.connected;
2729
+ }
2730
+ // ============================================
2731
+ // Core Operations
2732
+ // ============================================
2733
+ async get(key) {
2734
+ this.ensureConnected();
2735
+ const entry = this.store.get(key);
2736
+ if (!entry) return null;
2737
+ if (isExpired(entry.expiresAt)) {
2738
+ this.deleteEntry(key);
2739
+ return null;
2740
+ }
2741
+ this.touchLRU(key);
2742
+ return entry.value;
2743
+ }
2744
+ async doSet(key, value, options) {
2745
+ const existingEntry = this.store.get(key);
2746
+ const exists = existingEntry !== void 0 && !isExpired(existingEntry.expiresAt);
2747
+ if (options?.ifNotExists && exists) {
2748
+ return;
2749
+ }
2750
+ if (options?.ifExists && !exists) {
2751
+ return;
2752
+ }
2753
+ this.clearEntryTimeout(key);
2754
+ const entry = { value };
2755
+ if (options?.ttlSeconds) {
2756
+ entry.expiresAt = ttlToExpiresAt(options.ttlSeconds);
2757
+ const ttlMs = options.ttlSeconds * 1e3;
2758
+ if (ttlMs < MAX_TIMEOUT_MS) {
2759
+ entry.timeout = setTimeout(() => {
2760
+ this.deleteEntry(key);
2761
+ }, ttlMs);
2762
+ if (entry.timeout.unref) {
2763
+ entry.timeout.unref();
2764
+ }
2765
+ }
2766
+ }
2767
+ if (this.options.maxEntries > 0 && !this.store.has(key)) {
2768
+ while (this.store.size >= this.options.maxEntries) {
2769
+ this.evictOldest();
2770
+ }
2771
+ }
2772
+ this.store.set(key, entry);
2773
+ this.touchLRU(key);
2774
+ }
2775
+ async delete(key) {
2776
+ this.ensureConnected();
2777
+ return this.deleteEntry(key);
2778
+ }
2779
+ async exists(key) {
2780
+ this.ensureConnected();
2781
+ const entry = this.store.get(key);
2782
+ if (!entry) return false;
2783
+ if (isExpired(entry.expiresAt)) {
2784
+ this.deleteEntry(key);
2785
+ return false;
2786
+ }
2787
+ return true;
2788
+ }
2789
+ // ============================================
2790
+ // TTL Operations
2791
+ // ============================================
2792
+ async expire(key, ttlSeconds) {
2793
+ this.ensureConnected();
2794
+ validateTTL(ttlSeconds);
2795
+ const entry = this.store.get(key);
2796
+ if (!entry || isExpired(entry.expiresAt)) {
2797
+ return false;
2798
+ }
2799
+ this.clearEntryTimeout(key);
2800
+ entry.expiresAt = ttlToExpiresAt(ttlSeconds);
2801
+ const ttlMs = ttlSeconds * 1e3;
2802
+ if (ttlMs < MAX_TIMEOUT_MS) {
2803
+ entry.timeout = setTimeout(() => {
2804
+ this.deleteEntry(key);
2805
+ }, ttlMs);
2806
+ if (entry.timeout.unref) {
2807
+ entry.timeout.unref();
2808
+ }
2809
+ }
2810
+ return true;
2811
+ }
2812
+ async ttl(key) {
2813
+ this.ensureConnected();
2814
+ const entry = this.store.get(key);
2815
+ if (!entry) return null;
2816
+ if (isExpired(entry.expiresAt)) {
2817
+ this.deleteEntry(key);
2818
+ return null;
2819
+ }
2820
+ if (entry.expiresAt === void 0) {
2821
+ return -1;
2822
+ }
2823
+ return expiresAtToTTL(entry.expiresAt);
2824
+ }
2825
+ // ============================================
2826
+ // Key Enumeration
2827
+ // ============================================
2828
+ async keys(pattern = "*") {
2829
+ this.ensureConnected();
2830
+ const regex = globToRegex(pattern);
2831
+ const result = [];
2832
+ for (const [key, entry] of this.store) {
2833
+ if (isExpired(entry.expiresAt)) {
2834
+ this.deleteEntry(key);
2835
+ continue;
2836
+ }
2837
+ if (regex.test(key)) {
2838
+ result.push(key);
2839
+ }
2840
+ }
2841
+ return result;
2842
+ }
2843
+ // ============================================
2844
+ // Atomic Operations
2845
+ // ============================================
2846
+ async incr(key) {
2847
+ return this.incrBy(key, 1);
2848
+ }
2849
+ async decr(key) {
2850
+ return this.incrBy(key, -1);
2851
+ }
2852
+ async incrBy(key, amount) {
2853
+ this.ensureConnected();
2854
+ const entry = this.store.get(key);
2855
+ let currentValue = 0;
2856
+ if (entry && !isExpired(entry.expiresAt)) {
2857
+ const parsed = parseInt(entry.value, 10);
2858
+ if (isNaN(parsed)) {
2859
+ throw new StorageOperationError("incrBy", key, "Value is not an integer");
2860
+ }
2861
+ currentValue = parsed;
2862
+ }
2863
+ const newValue = currentValue + amount;
2864
+ const newEntry = { value: String(newValue) };
2865
+ if (entry?.expiresAt && !isExpired(entry.expiresAt)) {
2866
+ newEntry.expiresAt = entry.expiresAt;
2867
+ const remainingMs = entry.expiresAt - Date.now();
2868
+ if (remainingMs > 0 && remainingMs < MAX_TIMEOUT_MS) {
2869
+ this.clearEntryTimeout(key);
2870
+ newEntry.timeout = setTimeout(() => {
2871
+ this.deleteEntry(key);
2872
+ }, remainingMs);
2873
+ if (newEntry.timeout.unref) {
2874
+ newEntry.timeout.unref();
2875
+ }
2876
+ }
2877
+ }
2878
+ this.store.set(key, newEntry);
2879
+ this.touchLRU(key);
2880
+ return newValue;
2881
+ }
2882
+ // ============================================
2883
+ // Pub/Sub
2884
+ // ============================================
2885
+ supportsPubSub() {
2886
+ return true;
2887
+ }
2888
+ async publish(channel, message) {
2889
+ this.ensureConnected();
2890
+ return this.emitter.listenerCount(channel) > 0 ? (this.emitter.emit(channel, message, channel), this.emitter.listenerCount(channel)) : 0;
2891
+ }
2892
+ async subscribe(channel, handler) {
2893
+ this.ensureConnected();
2894
+ const wrappedHandler = (message, ch) => {
2895
+ handler(message, ch);
2896
+ };
2897
+ this.emitter.on(channel, wrappedHandler);
2898
+ return async () => {
2899
+ this.emitter.off(channel, wrappedHandler);
2900
+ };
2901
+ }
2902
+ // ============================================
2903
+ // Internal Helpers
2904
+ // ============================================
2905
+ /**
2906
+ * Delete an entry and clear its timeout.
2907
+ */
2908
+ deleteEntry(key) {
2909
+ const entry = this.store.get(key);
2910
+ if (!entry) return false;
2911
+ this.clearEntryTimeout(key);
2912
+ this.store.delete(key);
2913
+ this.removeLRU(key);
2914
+ return true;
2915
+ }
2916
+ /**
2917
+ * Clear timeout for an entry.
2918
+ */
2919
+ clearEntryTimeout(key) {
2920
+ const entry = this.store.get(key);
2921
+ if (entry?.timeout) {
2922
+ clearTimeout(entry.timeout);
2923
+ entry.timeout = void 0;
2924
+ }
2925
+ }
2926
+ /**
2927
+ * Clear all timeouts.
2928
+ */
2929
+ clearAllTimeouts() {
2930
+ for (const [key] of this.store) {
2931
+ this.clearEntryTimeout(key);
2932
+ }
2933
+ }
2934
+ /**
2935
+ * Update LRU access order.
2936
+ */
2937
+ touchLRU(key) {
2938
+ if (this.options.maxEntries <= 0) return;
2939
+ const idx = this.accessOrder.indexOf(key);
2940
+ if (idx !== -1) {
2941
+ this.accessOrder.splice(idx, 1);
2942
+ }
2943
+ this.accessOrder.push(key);
2944
+ }
2945
+ /**
2946
+ * Remove key from LRU tracking.
2947
+ */
2948
+ removeLRU(key) {
2949
+ if (this.options.maxEntries <= 0) return;
2950
+ const idx = this.accessOrder.indexOf(key);
2951
+ if (idx !== -1) {
2952
+ this.accessOrder.splice(idx, 1);
2953
+ }
2954
+ }
2955
+ /**
2956
+ * Evict oldest entry (LRU).
2957
+ */
2958
+ evictOldest() {
2959
+ if (this.accessOrder.length === 0) return;
2960
+ const oldestKey = this.accessOrder.shift();
2961
+ if (oldestKey) {
2962
+ this.deleteEntry(oldestKey);
2963
+ }
2964
+ }
2965
+ /**
2966
+ * Start background sweeper.
2967
+ */
2968
+ startSweeper() {
2969
+ this.sweepInterval = setInterval(() => {
2970
+ this.sweep();
2971
+ }, this.options.sweepIntervalSeconds * 1e3);
2972
+ if (this.sweepInterval.unref) {
2973
+ this.sweepInterval.unref();
2974
+ }
2975
+ }
2976
+ /**
2977
+ * Stop background sweeper.
2978
+ */
2979
+ stopSweeper() {
2980
+ if (this.sweepInterval) {
2981
+ clearInterval(this.sweepInterval);
2982
+ this.sweepInterval = void 0;
2983
+ }
2984
+ }
2985
+ /**
2986
+ * Sweep expired entries.
2987
+ */
2988
+ sweep() {
2989
+ const now = Date.now();
2990
+ for (const [key, entry] of this.store) {
2991
+ if (entry.expiresAt && now >= entry.expiresAt) {
2992
+ this.deleteEntry(key);
2993
+ }
2994
+ }
2995
+ }
2996
+ // ============================================
2997
+ // Stats (for debugging)
2998
+ // ============================================
2999
+ /**
3000
+ * Get storage statistics.
3001
+ */
3002
+ getStats() {
3003
+ return {
3004
+ size: this.store.size,
3005
+ maxEntries: this.options.maxEntries,
3006
+ sweeperActive: this.sweepInterval !== void 0
3007
+ };
3008
+ }
3009
+ };
3010
+
3011
+ // libs/utils/src/storage/factory.ts
3012
+ function isProduction() {
3013
+ return process.env["NODE_ENV"] === "production";
3014
+ }
3015
+ function detectStorageType() {
3016
+ if (process.env["UPSTASH_REDIS_REST_URL"] && process.env["UPSTASH_REDIS_REST_TOKEN"]) {
3017
+ return "upstash";
3018
+ }
3019
+ if (process.env["KV_REST_API_URL"] && process.env["KV_REST_API_TOKEN"]) {
3020
+ return "vercel-kv";
3021
+ }
3022
+ if (process.env["REDIS_URL"] || process.env["REDIS_HOST"]) {
3023
+ return "redis";
3024
+ }
3025
+ return "memory";
3026
+ }
3027
+ async function createAdapter(type, config) {
3028
+ switch (type) {
3029
+ case "memory": {
3030
+ return new MemoryStorageAdapter(config.memory);
3031
+ }
3032
+ case "redis": {
3033
+ const { RedisStorageAdapter: RedisStorageAdapter2 } = await Promise.resolve().then(() => (init_redis(), redis_exports));
3034
+ return new RedisStorageAdapter2(config.redis);
3035
+ }
3036
+ case "vercel-kv": {
3037
+ const { VercelKvStorageAdapter: VercelKvStorageAdapter2 } = await Promise.resolve().then(() => (init_vercel_kv(), vercel_kv_exports));
3038
+ return new VercelKvStorageAdapter2(config.vercelKv);
3039
+ }
3040
+ case "upstash": {
3041
+ const { UpstashStorageAdapter: UpstashStorageAdapter2 } = await Promise.resolve().then(() => (init_upstash(), upstash_exports));
3042
+ return new UpstashStorageAdapter2(config.upstash);
3043
+ }
3044
+ case "auto":
3045
+ throw new StorageConfigError("auto", "Auto type should be resolved before calling createAdapter");
3046
+ default:
3047
+ throw new StorageConfigError("unknown", `Unknown storage type: ${type}`);
3048
+ }
3049
+ }
3050
+ async function createStorage(config = {}) {
3051
+ let type = config.type ?? "auto";
3052
+ const fallback = config.fallback ?? (isProduction() ? "error" : "memory");
3053
+ if (type === "auto") {
3054
+ type = detectStorageType();
3055
+ if (type === "memory" && isProduction()) {
3056
+ console.warn(
3057
+ "[storage] Warning: No distributed storage backend detected in production. Using in-memory storage. Set REDIS_URL, UPSTASH_REDIS_REST_URL, or KV_REST_API_URL."
3058
+ );
3059
+ }
3060
+ }
3061
+ let adapter;
3062
+ try {
3063
+ adapter = await createAdapter(type, config);
3064
+ } catch (e) {
3065
+ if (fallback === "memory" && type !== "memory") {
3066
+ console.warn(
3067
+ `[storage] Warning: Failed to create ${type} adapter, falling back to memory. Error: ${e instanceof Error ? e.message : String(e)}`
3068
+ );
3069
+ adapter = new MemoryStorageAdapter(config.memory);
3070
+ } else {
3071
+ throw e;
3072
+ }
3073
+ }
3074
+ try {
3075
+ await adapter.connect();
3076
+ } catch (e) {
3077
+ if (fallback === "memory" && type !== "memory") {
3078
+ console.warn(
3079
+ `[storage] Warning: Failed to connect to ${type}, falling back to memory. Error: ${e instanceof Error ? e.message : String(e)}`
3080
+ );
3081
+ adapter = new MemoryStorageAdapter(config.memory);
3082
+ await adapter.connect();
3083
+ } else {
3084
+ throw e;
3085
+ }
3086
+ }
3087
+ if (config.prefix) {
3088
+ return createNamespacedStorage(adapter, config.prefix);
3089
+ }
3090
+ return createRootStorage(adapter);
3091
+ }
3092
+ function createMemoryStorage(config = {}) {
3093
+ const adapter = new MemoryStorageAdapter(config);
3094
+ if (config.prefix) {
3095
+ return createNamespacedStorage(adapter, config.prefix);
3096
+ }
3097
+ return createRootStorage(adapter);
3098
+ }
3099
+ function getDetectedStorageType() {
3100
+ return detectStorageType();
3101
+ }
3102
+
3103
+ // libs/utils/src/storage/index.ts
3104
+ init_errors();
3105
+
3106
+ // libs/utils/src/storage/adapters/index.ts
3107
+ init_base();
3108
+ init_redis();
3109
+ init_vercel_kv();
3110
+ init_upstash();
3111
+
3112
+ // libs/utils/src/storage/index.ts
3113
+ init_ttl();
3114
+ export {
3115
+ BaseStorageAdapter,
3116
+ DEFAULT_CODE_VERIFIER_LENGTH,
3117
+ DEFAULT_MAX_INPUT_LENGTH,
3118
+ EncryptedBlobError,
3119
+ MAX_CODE_VERIFIER_LENGTH,
3120
+ MAX_TTL_SECONDS,
3121
+ MIN_CODE_VERIFIER_LENGTH,
3122
+ MemoryStorageAdapter,
3123
+ NAMESPACE_SEPARATOR,
3124
+ NamespacedStorageImpl,
3125
+ PkceError,
3126
+ REDOS_THRESHOLDS,
3127
+ RedisStorageAdapter,
3128
+ StorageConfigError,
3129
+ StorageConnectionError,
3130
+ StorageError,
3131
+ StorageNotConnectedError,
3132
+ StorageNotSupportedError,
3133
+ StorageOperationError,
3134
+ StoragePatternError,
3135
+ StorageTTLError,
3136
+ UpstashStorageAdapter,
3137
+ VercelKvStorageAdapter,
3138
+ access,
3139
+ analyzePattern,
3140
+ assertNode,
3141
+ base64urlDecode,
3142
+ base64urlEncode,
3143
+ buildPrefix,
3144
+ bytesToHex,
3145
+ clearCachedSecret,
3146
+ collapseChar,
3147
+ collapseWhitespace,
3148
+ copyFile,
3149
+ cp,
3150
+ createMemoryStorage,
3151
+ createNamespacedStorage,
3152
+ createRootStorage,
3153
+ createSafeRegExp,
3154
+ createSecretData,
3155
+ createStorage,
3156
+ decryptAesGcm,
3157
+ decryptValue,
3158
+ deleteSecret,
3159
+ deserializeAndDecrypt,
3160
+ deserializeBlob,
3161
+ encryptAesGcm,
3162
+ encryptAndSerialize,
3163
+ encryptValue,
3164
+ ensureDir,
3165
+ ensureMaxLen,
3166
+ escapeGlob,
3167
+ escapeHtml,
3168
+ escapeHtmlAttr,
3169
+ escapeJsString,
3170
+ escapeScriptClose,
3171
+ expandTemplate,
3172
+ expandUriTemplate,
3173
+ expiresAtToTTL,
3174
+ extractBracedParams,
3175
+ extractTemplateParams,
3176
+ extractUriScheme,
3177
+ fileExists,
3178
+ generateCodeChallenge,
3179
+ generateCodeVerifier,
3180
+ generatePkcePair,
3181
+ generateSecret,
3182
+ getCrypto,
3183
+ getDetectedStorageType,
3184
+ getOrCreateSecret,
3185
+ globToRegex,
3186
+ hasTemplatePlaceholders,
3187
+ hkdfSha256,
3188
+ hmacSha256,
3189
+ idFromString,
3190
+ inferMimeType,
3191
+ isBrowser,
3192
+ isDirEmpty,
3193
+ isExpired,
3194
+ isInputLengthSafe,
3195
+ isNode,
3196
+ isPatternSafe,
3197
+ isRsaPssAlg,
3198
+ isSecretCached,
3199
+ isSecretPersistenceEnabled,
3200
+ isUriTemplate,
3201
+ isValidCodeChallenge,
3202
+ isValidCodeVerifier,
3203
+ isValidEncryptedBlob,
3204
+ isValidMcpUri,
3205
+ isValidMcpUriTemplate,
3206
+ joinPath,
3207
+ jwtAlgToNodeAlg,
3208
+ loadSecret,
3209
+ matchUriTemplate,
3210
+ matchesPattern,
3211
+ mkdir,
3212
+ mkdtemp,
3213
+ normalizeTTL,
3214
+ parseSecretData,
3215
+ parseUriTemplate,
3216
+ randomBytes,
3217
+ randomUUID,
3218
+ readFile,
3219
+ readFileBuffer,
3220
+ readJSON,
3221
+ readdir,
3222
+ rename,
3223
+ resolveSecretPath,
3224
+ rm,
3225
+ rsaVerify2 as rsaVerify,
3226
+ runCmd,
3227
+ safeExec,
3228
+ safeJsonForScript,
3229
+ safeMatch,
3230
+ safeReplace,
3231
+ safeStringify,
3232
+ safeTest,
3233
+ sanitizeToJson,
3234
+ saveSecret,
3235
+ secretDataSchema,
3236
+ sepFor,
3237
+ serializeBlob,
3238
+ sha256,
3239
+ sha256Base64url,
3240
+ sha256Hex,
3241
+ shortHash,
3242
+ splitWords,
3243
+ stat,
3244
+ timingSafeEqual,
3245
+ toCase,
3246
+ trimBoth,
3247
+ trimChars,
3248
+ trimLeading,
3249
+ trimSlashes,
3250
+ trimTrailing,
3251
+ tryDecryptValue,
3252
+ tryDeserializeAndDecrypt,
3253
+ tryDeserializeBlob,
3254
+ ttlToExpiresAt,
3255
+ unlink,
3256
+ validateBaseUrl,
3257
+ validateOptionalTTL,
3258
+ validatePattern,
3259
+ validateSecretData,
3260
+ validateTTL,
3261
+ verifyCodeChallenge,
3262
+ writeFile,
3263
+ writeJSON
3264
+ };