@frontmcp/utils 1.10.15 → 1.11.0-rc.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 (137) hide show
  1. package/README.md +1 -0
  2. package/browser/async-context/browser-async-context.mjs +177 -0
  3. package/browser/async-context/index.mjs +15 -0
  4. package/browser/async-context/overlap-error.mjs +10 -0
  5. package/browser/class-brand/class-brand.mjs +27 -0
  6. package/browser/class-brand/index.mjs +6 -0
  7. package/browser/content/content.mjs +151 -0
  8. package/browser/content/index.mjs +8 -0
  9. package/browser/crypto/browser.mjs +103 -0
  10. package/browser/crypto/encrypted-blob.mjs +119 -0
  11. package/browser/crypto/hmac-signing.mjs +70 -0
  12. package/browser/crypto/index.mjs +281 -0
  13. package/browser/crypto/jwt-alg.mjs +40 -0
  14. package/browser/crypto/key-persistence/factory.mjs +134 -0
  15. package/browser/crypto/key-persistence/index.mjs +26 -0
  16. package/browser/crypto/key-persistence/key-persistence.mjs +185 -0
  17. package/browser/crypto/key-persistence/schemas.mjs +82 -0
  18. package/browser/crypto/pkce/index.mjs +25 -0
  19. package/browser/crypto/pkce/pkce.mjs +59 -0
  20. package/browser/crypto/runtime.mjs +17 -0
  21. package/browser/crypto/secret-persistence/index.mjs +29 -0
  22. package/browser/crypto/secret-persistence/persistence.mjs +181 -0
  23. package/browser/crypto/secret-persistence/schema.mjs +40 -0
  24. package/browser/crypto/web-rsa.mjs +36 -0
  25. package/browser/env/browser-env.mjs +56 -0
  26. package/browser/env/browser-runtime-context.mjs +85 -0
  27. package/browser/env/build-target.mjs +25 -0
  28. package/browser/env/edge-scope.mjs +17 -0
  29. package/browser/env/index.mjs +47 -0
  30. package/browser/env/provider.mjs +46 -0
  31. package/browser/escape/escape.mjs +43 -0
  32. package/browser/escape/index.mjs +9 -0
  33. package/browser/event-emitter/browser-event-emitter.mjs +49 -0
  34. package/browser/event-emitter/index.mjs +5 -0
  35. package/browser/fs/fs.mjs +227 -0
  36. package/browser/fs/index.mjs +65 -0
  37. package/browser/http/cookie.mjs +85 -0
  38. package/browser/http/http.mjs +18 -0
  39. package/browser/http/index.mjs +11 -0
  40. package/browser/index.mjs +526 -0
  41. package/browser/llm/index.mjs +5 -0
  42. package/browser/llm/llm-tool-handlers.mjs +55 -0
  43. package/browser/machine-id/index.mjs +7 -0
  44. package/browser/machine-id/machine-id.mjs +108 -0
  45. package/browser/naming/index.mjs +10 -0
  46. package/browser/naming/naming.mjs +68 -0
  47. package/browser/net/index.mjs +10 -0
  48. package/browser/net/ip-address.mjs +128 -0
  49. package/browser/net/redirect.mjs +7 -0
  50. package/browser/path/browser-path.mjs +47 -0
  51. package/browser/path/index.mjs +24 -0
  52. package/browser/path/path.mjs +13 -0
  53. package/browser/process/child-process.mjs +23 -0
  54. package/browser/process/index.mjs +15 -0
  55. package/browser/process/owned-process.mjs +175 -0
  56. package/browser/process/process-tree.mjs +149 -0
  57. package/browser/process/process-watch.mjs +48 -0
  58. package/browser/redaction/index.mjs +6 -0
  59. package/browser/redaction/redaction.mjs +74 -0
  60. package/browser/regex/index.mjs +3 -0
  61. package/browser/regex/patterns.mjs +167 -0
  62. package/browser/regex/safe-regex.mjs +93 -0
  63. package/browser/serialization/index.mjs +5 -0
  64. package/browser/serialization/serialization.mjs +22 -0
  65. package/browser/stdio-bridge/errors.mjs +27 -0
  66. package/browser/stdio-bridge/index.mjs +24 -0
  67. package/browser/stdio-bridge/list-changed.mjs +21 -0
  68. package/browser/stdio-bridge/state-machine.mjs +211 -0
  69. package/browser/stdio-bridge/stdio-framer.mjs +91 -0
  70. package/browser/stdio-bridge/upstream-client.mjs +189 -0
  71. package/browser/storage/adapters/base.mjs +139 -0
  72. package/browser/storage/adapters/cloudflare-kv.mjs +149 -0
  73. package/browser/storage/adapters/filesystem.mjs +279 -0
  74. package/browser/storage/adapters/index.mjs +23 -0
  75. package/browser/storage/adapters/indexeddb.mjs +198 -0
  76. package/browser/storage/adapters/localstorage.mjs +222 -0
  77. package/browser/storage/adapters/memory.mjs +350 -0
  78. package/browser/storage/adapters/redis.mjs +497 -0
  79. package/browser/storage/adapters/upstash.mjs +299 -0
  80. package/browser/storage/adapters/vercel-kv.mjs +226 -0
  81. package/browser/storage/change-feed.mjs +189 -0
  82. package/browser/storage/encrypted-typed-storage.mjs +329 -0
  83. package/browser/storage/errors.mjs +92 -0
  84. package/browser/storage/factory.mjs +155 -0
  85. package/browser/storage/index.mjs +96 -0
  86. package/browser/storage/namespace.mjs +186 -0
  87. package/browser/storage/redis-error-listener.mjs +32 -0
  88. package/browser/storage/redis-url.mjs +66 -0
  89. package/browser/storage/typed-storage.mjs +171 -0
  90. package/browser/storage/utils/compare-and-delete.mjs +9 -0
  91. package/browser/storage/utils/index.mjs +24 -0
  92. package/browser/storage/utils/pattern.mjs +68 -0
  93. package/browser/storage/utils/ttl.mjs +48 -0
  94. package/browser/uri/index.mjs +19 -0
  95. package/browser/uri/uri-template.mjs +86 -0
  96. package/browser/uri/uri-validation.mjs +18 -0
  97. package/content/content.d.ts +8 -0
  98. package/content/index.d.ts +1 -1
  99. package/crypto/browser.d.ts +1 -13
  100. package/crypto/browser.js +1 -1
  101. package/crypto/node.js +1 -1
  102. package/crypto/web-rsa.d.ts +13 -0
  103. package/env/browser-runtime-context.js +1 -1
  104. package/env/runtime-context.js +1 -1
  105. package/esm/crypto/browser.mjs +1 -1
  106. package/esm/crypto/node.mjs +1 -1
  107. package/esm/env/browser-runtime-context.mjs +1 -1
  108. package/esm/env/runtime-context.mjs +1 -1
  109. package/esm/index.mjs +1 -1
  110. package/fs/fs.d.ts +45 -4
  111. package/fs/index.d.ts +1 -1
  112. package/index.d.ts +10 -4
  113. package/index.js +1 -1
  114. package/package.json +24 -8
  115. package/process/child-process.d.ts +2 -0
  116. package/process/index.d.ts +6 -0
  117. package/process/owned-process.d.ts +59 -0
  118. package/process/process-tree.d.ts +78 -0
  119. package/process/process-watch.d.ts +41 -0
  120. package/redaction/index.d.ts +2 -0
  121. package/redaction/redaction.d.ts +24 -0
  122. package/stdio-bridge/errors.d.ts +23 -0
  123. package/stdio-bridge/index.d.ts +9 -0
  124. package/stdio-bridge/list-changed.d.ts +3 -0
  125. package/stdio-bridge/state-machine.d.ts +61 -0
  126. package/stdio-bridge/stdio-framer.d.ts +49 -0
  127. package/stdio-bridge/types.d.ts +10 -0
  128. package/stdio-bridge/upstream-client.d.ts +66 -0
  129. package/storage/adapters/base.d.ts +2 -0
  130. package/storage/adapters/memory.d.ts +1 -0
  131. package/storage/adapters/redis.d.ts +3 -0
  132. package/storage/change-feed.d.ts +47 -0
  133. package/storage/factory.d.ts +6 -0
  134. package/storage/index.d.ts +3 -1
  135. package/storage/namespace.d.ts +2 -0
  136. package/storage/types.d.ts +46 -1
  137. package/storage/utils/compare-and-delete.d.ts +2 -0
@@ -0,0 +1,329 @@
1
+ // libs/utils/src/storage/encrypted-typed-storage.ts
2
+ import { encryptAesGcm, decryptAesGcm, randomBytes, base64urlEncode, base64urlDecode, hkdfSha256 } from "../crypto/index.mjs";
3
+ import { EncryptedStorageError } from "./errors.mjs";
4
+ var textEncoder = new TextEncoder();
5
+ var textDecoder = new TextDecoder();
6
+ var EncryptedTypedStorage = class {
7
+ storage;
8
+ activeKey;
9
+ keyMap;
10
+ schema;
11
+ throwOnError;
12
+ onKeyRotationNeeded;
13
+ clientBinding;
14
+ constructor(storage, options) {
15
+ if (!options.keys || options.keys.length === 0) {
16
+ throw new EncryptedStorageError("At least one encryption key is required");
17
+ }
18
+ for (const k of options.keys) {
19
+ if (k.key.length !== 32) {
20
+ throw new EncryptedStorageError(
21
+ `Encryption key "${k.kid}" must be 32 bytes (AES-256), got ${k.key.length} bytes`
22
+ );
23
+ }
24
+ }
25
+ this.storage = storage;
26
+ this.activeKey = options.keys[0];
27
+ this.keyMap = new Map(options.keys.map((k) => [k.kid, k.key]));
28
+ this.schema = options.schema;
29
+ this.throwOnError = options.throwOnError ?? false;
30
+ this.onKeyRotationNeeded = options.onKeyRotationNeeded;
31
+ this.clientBinding = options.clientBinding;
32
+ }
33
+ /**
34
+ * Get a decrypted value by key.
35
+ *
36
+ * @param key - Storage key
37
+ * @returns The decrypted value, or null if not found or decryption fails
38
+ */
39
+ async get(key) {
40
+ const raw = await this.storage.get(key);
41
+ if (raw === null) {
42
+ return null;
43
+ }
44
+ return this.decryptAndParse(raw, key);
45
+ }
46
+ /**
47
+ * Encrypt and store a value.
48
+ *
49
+ * @param key - Storage key
50
+ * @param value - Value to encrypt and store
51
+ * @param options - Optional TTL and conditional flags
52
+ */
53
+ async set(key, value, options) {
54
+ const encrypted = this.encryptValue(value);
55
+ const serialized = JSON.stringify(encrypted);
56
+ await this.storage.set(key, serialized, options);
57
+ }
58
+ /**
59
+ * Delete a key.
60
+ *
61
+ * @param key - Storage key
62
+ * @returns true if key existed and was deleted
63
+ */
64
+ async delete(key) {
65
+ return this.storage.delete(key);
66
+ }
67
+ /**
68
+ * Check if a key exists.
69
+ *
70
+ * @param key - Storage key
71
+ * @returns true if key exists
72
+ */
73
+ async exists(key) {
74
+ return this.storage.exists(key);
75
+ }
76
+ /**
77
+ * Get multiple decrypted values.
78
+ *
79
+ * @param keys - Array of storage keys
80
+ * @returns Array of decrypted values (null for missing/invalid keys)
81
+ */
82
+ async mget(keys) {
83
+ if (keys.length === 0) {
84
+ return [];
85
+ }
86
+ const rawValues = await this.storage.mget(keys);
87
+ return rawValues.map((raw, index) => {
88
+ if (raw === null) {
89
+ return null;
90
+ }
91
+ return this.decryptAndParse(raw, keys[index]);
92
+ });
93
+ }
94
+ /**
95
+ * Encrypt and store multiple values.
96
+ *
97
+ * @param entries - Array of key-value-options entries
98
+ */
99
+ async mset(entries) {
100
+ if (entries.length === 0) {
101
+ return;
102
+ }
103
+ const rawEntries = entries.map((entry) => ({
104
+ key: entry.key,
105
+ value: JSON.stringify(this.encryptValue(entry.value)),
106
+ options: entry.options
107
+ }));
108
+ await this.storage.mset(rawEntries);
109
+ }
110
+ /**
111
+ * Delete multiple keys.
112
+ *
113
+ * @param keys - Array of storage keys
114
+ * @returns Number of keys actually deleted
115
+ */
116
+ async mdelete(keys) {
117
+ return this.storage.mdelete(keys);
118
+ }
119
+ /**
120
+ * Update TTL on an existing key.
121
+ *
122
+ * @param key - Storage key
123
+ * @param ttlSeconds - New TTL in seconds
124
+ * @returns true if key exists and TTL was set
125
+ */
126
+ async expire(key, ttlSeconds) {
127
+ return this.storage.expire(key, ttlSeconds);
128
+ }
129
+ /**
130
+ * Get remaining TTL for a key.
131
+ *
132
+ * @param key - Storage key
133
+ * @returns TTL in seconds, -1 if no TTL, or null if key doesn't exist
134
+ */
135
+ async ttl(key) {
136
+ return this.storage.ttl(key);
137
+ }
138
+ /**
139
+ * List keys matching a pattern.
140
+ *
141
+ * @param pattern - Glob pattern (default: '*' for all keys)
142
+ * @returns Array of matching keys
143
+ */
144
+ async keys(pattern) {
145
+ return this.storage.keys(pattern);
146
+ }
147
+ /**
148
+ * Count keys matching a pattern.
149
+ *
150
+ * @param pattern - Glob pattern (default: '*' for all keys)
151
+ * @returns Number of matching keys
152
+ */
153
+ async count(pattern) {
154
+ return this.storage.count(pattern);
155
+ }
156
+ /**
157
+ * Re-encrypt a value with the active key.
158
+ * Useful for key rotation - reads with old key, writes with new key.
159
+ *
160
+ * @param key - Storage key to re-encrypt
161
+ * @param options - Optional TTL for the re-encrypted value
162
+ * @returns true if value was re-encrypted, false if not found
163
+ */
164
+ async reencrypt(key, options) {
165
+ const value = await this.get(key);
166
+ if (value === null) {
167
+ return false;
168
+ }
169
+ await this.set(key, value, options);
170
+ return true;
171
+ }
172
+ /**
173
+ * Rotate the active encryption key.
174
+ * New encryptions will use the new key; old keys remain for decryption.
175
+ *
176
+ * @param newKey - New encryption key to use for writes
177
+ */
178
+ rotateKey(newKey) {
179
+ if (newKey.key.length !== 32) {
180
+ throw new EncryptedStorageError(`New encryption key must be 32 bytes (AES-256), got ${newKey.key.length} bytes`);
181
+ }
182
+ if (!this.keyMap.has(newKey.kid)) {
183
+ this.keyMap.set(newKey.kid, newKey.key);
184
+ }
185
+ this.activeKey = newKey;
186
+ }
187
+ /**
188
+ * Get the active key ID.
189
+ */
190
+ get activeKeyId() {
191
+ return this.activeKey.kid;
192
+ }
193
+ /**
194
+ * Get all known key IDs.
195
+ */
196
+ get keyIds() {
197
+ return Array.from(this.keyMap.keys());
198
+ }
199
+ /**
200
+ * Get the underlying storage adapter.
201
+ * Use with caution - operations bypass encryption.
202
+ */
203
+ get raw() {
204
+ return this.storage;
205
+ }
206
+ /**
207
+ * Check if client-side key binding is enabled.
208
+ * When true, encryption keys are derived from both server key and client secret.
209
+ */
210
+ get hasClientBinding() {
211
+ return this.clientBinding !== void 0;
212
+ }
213
+ /**
214
+ * Derive the actual encryption key.
215
+ *
216
+ * If clientBinding is configured, combines server key with client secret
217
+ * using HKDF-SHA256 (RFC 5869) to produce the actual encryption key.
218
+ * Otherwise, returns the server key as-is.
219
+ *
220
+ * This provides zero-knowledge encryption where:
221
+ * - Server cannot decrypt without client secret (sessionId)
222
+ * - Client cannot decrypt without server key
223
+ * - Key derivation is deterministic (same inputs -> same derived key)
224
+ *
225
+ * @param serverKey - The server-side encryption key
226
+ * @returns The key to use for actual encryption/decryption
227
+ */
228
+ deriveKey(serverKey) {
229
+ if (!this.clientBinding) {
230
+ return serverKey;
231
+ }
232
+ const clientSecret = typeof this.clientBinding.secret === "string" ? textEncoder.encode(this.clientBinding.secret) : this.clientBinding.secret;
233
+ const ikm = new Uint8Array(serverKey.length + clientSecret.length);
234
+ ikm.set(serverKey, 0);
235
+ ikm.set(clientSecret, serverKey.length);
236
+ const salt = this.clientBinding.salt ?? new Uint8Array(0);
237
+ const info = textEncoder.encode(this.clientBinding.info ?? "frontmcp-client-bound-v1");
238
+ return hkdfSha256(ikm, salt, info, 32);
239
+ }
240
+ /**
241
+ * Encrypt a value using the active key.
242
+ * If client binding is configured, uses derived key from server key + client secret.
243
+ */
244
+ encryptValue(value) {
245
+ let jsonString;
246
+ try {
247
+ jsonString = JSON.stringify(value);
248
+ } catch (error) {
249
+ throw new EncryptedStorageError(`Failed to serialize value: ${error.message}`);
250
+ }
251
+ const plaintext = textEncoder.encode(jsonString);
252
+ const iv = randomBytes(12);
253
+ const encryptionKey = this.deriveKey(this.activeKey.key);
254
+ const { ciphertext, tag } = encryptAesGcm(encryptionKey, plaintext, iv);
255
+ return {
256
+ alg: "A256GCM",
257
+ kid: this.activeKey.kid,
258
+ iv: base64urlEncode(iv),
259
+ tag: base64urlEncode(tag),
260
+ data: base64urlEncode(ciphertext)
261
+ };
262
+ }
263
+ /**
264
+ * Decrypt and parse a stored value.
265
+ */
266
+ decryptAndParse(raw, storageKey) {
267
+ let blob;
268
+ try {
269
+ blob = JSON.parse(raw);
270
+ } catch (_error) {
271
+ if (this.throwOnError) {
272
+ throw new EncryptedStorageError(`Failed to parse stored blob for key "${storageKey}"`);
273
+ }
274
+ return null;
275
+ }
276
+ if (!this.isValidBlob(blob)) {
277
+ if (this.throwOnError) {
278
+ throw new EncryptedStorageError(`Invalid encrypted blob structure for key "${storageKey}"`);
279
+ }
280
+ return null;
281
+ }
282
+ const serverKey = this.keyMap.get(blob.kid);
283
+ if (!serverKey) {
284
+ if (this.throwOnError) {
285
+ throw new EncryptedStorageError(
286
+ `Unknown encryption key "${blob.kid}" for key "${storageKey}". Known keys: ${Array.from(this.keyMap.keys()).join(", ")}`
287
+ );
288
+ }
289
+ return null;
290
+ }
291
+ const decryptionKey = this.deriveKey(serverKey);
292
+ let decrypted;
293
+ try {
294
+ const iv = base64urlDecode(blob.iv);
295
+ const tag = base64urlDecode(blob.tag);
296
+ const ciphertext = base64urlDecode(blob.data);
297
+ const plaintext = decryptAesGcm(decryptionKey, ciphertext, iv, tag);
298
+ decrypted = JSON.parse(textDecoder.decode(plaintext));
299
+ } catch (error) {
300
+ if (this.throwOnError) {
301
+ throw new EncryptedStorageError(`Decryption failed for key "${storageKey}": ${error.message}`);
302
+ }
303
+ return null;
304
+ }
305
+ if (this.schema) {
306
+ const result = this.schema.safeParse(decrypted);
307
+ if (!result.success) {
308
+ if (this.throwOnError) {
309
+ throw new EncryptedStorageError(`Schema validation failed for key "${storageKey}": ${result.error.message}`);
310
+ }
311
+ return null;
312
+ }
313
+ decrypted = result.data;
314
+ }
315
+ if (blob.kid !== this.activeKey.kid && this.onKeyRotationNeeded) {
316
+ this.onKeyRotationNeeded(storageKey, blob.kid, this.activeKey.kid);
317
+ }
318
+ return decrypted;
319
+ }
320
+ /**
321
+ * Validate blob structure.
322
+ */
323
+ isValidBlob(blob) {
324
+ return typeof blob === "object" && blob !== null && "alg" in blob && "kid" in blob && "iv" in blob && "tag" in blob && "data" in blob && blob.alg === "A256GCM" && typeof blob.kid === "string" && typeof blob.iv === "string" && typeof blob.tag === "string" && typeof blob.data === "string";
325
+ }
326
+ };
327
+ export {
328
+ EncryptedTypedStorage
329
+ };
@@ -0,0 +1,92 @@
1
+ // libs/utils/src/storage/errors.ts
2
+ var StorageError = class extends Error {
3
+ cause;
4
+ constructor(message, cause) {
5
+ super(message);
6
+ this.cause = cause;
7
+ this.name = "StorageError";
8
+ Object.setPrototypeOf(this, new.target.prototype);
9
+ if (Error.captureStackTrace) {
10
+ Error.captureStackTrace(this, this.constructor);
11
+ }
12
+ }
13
+ };
14
+ var StorageConnectionError = class extends StorageError {
15
+ constructor(message, cause, backend) {
16
+ const fullMessage = cause ? `${message}: ${cause.message}` : message;
17
+ super(fullMessage, cause);
18
+ this.backend = backend;
19
+ this.name = "StorageConnectionError";
20
+ }
21
+ backend;
22
+ };
23
+ var StorageOperationError = class extends StorageError {
24
+ constructor(operation, key, message, cause) {
25
+ super(`${operation} failed for key "${key}": ${message}`, cause);
26
+ this.operation = operation;
27
+ this.key = key;
28
+ this.name = "StorageOperationError";
29
+ }
30
+ operation;
31
+ key;
32
+ };
33
+ var StorageNotSupportedError = class extends StorageError {
34
+ constructor(operation, backend, suggestion) {
35
+ const msg = suggestion ? `${operation} is not supported by ${backend}. ${suggestion}` : `${operation} is not supported by ${backend}`;
36
+ super(msg);
37
+ this.operation = operation;
38
+ this.backend = backend;
39
+ this.name = "StorageNotSupportedError";
40
+ }
41
+ operation;
42
+ backend;
43
+ };
44
+ var StorageConfigError = class extends StorageError {
45
+ constructor(backend, message) {
46
+ super(`Invalid ${backend} configuration: ${message}`);
47
+ this.backend = backend;
48
+ this.name = "StorageConfigError";
49
+ }
50
+ backend;
51
+ };
52
+ var StorageTTLError = class extends StorageError {
53
+ constructor(ttl, message) {
54
+ super(message ?? `Invalid TTL value: ${ttl}. TTL must be a positive integer.`);
55
+ this.ttl = ttl;
56
+ this.name = "StorageTTLError";
57
+ }
58
+ ttl;
59
+ };
60
+ var StoragePatternError = class extends StorageError {
61
+ constructor(pattern, message) {
62
+ super(`Invalid pattern "${pattern}": ${message}`);
63
+ this.pattern = pattern;
64
+ this.name = "StoragePatternError";
65
+ }
66
+ pattern;
67
+ };
68
+ var StorageNotConnectedError = class extends StorageError {
69
+ constructor(backend) {
70
+ super(`Storage backend "${backend}" is not connected. Call connect() first.`);
71
+ this.backend = backend;
72
+ this.name = "StorageNotConnectedError";
73
+ }
74
+ backend;
75
+ };
76
+ var EncryptedStorageError = class extends StorageError {
77
+ constructor(message) {
78
+ super(message);
79
+ this.name = "EncryptedStorageError";
80
+ }
81
+ };
82
+ export {
83
+ EncryptedStorageError,
84
+ StorageConfigError,
85
+ StorageConnectionError,
86
+ StorageError,
87
+ StorageNotConnectedError,
88
+ StorageNotSupportedError,
89
+ StorageOperationError,
90
+ StoragePatternError,
91
+ StorageTTLError
92
+ };
@@ -0,0 +1,155 @@
1
+ var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
2
+ const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
3
+ // __frontmcpCreateRequire
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
10
+
11
+ // libs/utils/src/storage/factory.ts
12
+ import { z } from "@frontmcp/lazy-zod";
13
+ import { getEnv, isProduction } from "../env/browser-env.mjs";
14
+ import { getRuntimeContext } from "../env/browser-runtime-context.mjs";
15
+ import { MemoryStorageAdapter } from "./adapters/memory.mjs";
16
+ import { StorageConfigError } from "./errors.mjs";
17
+ import { createNamespacedStorage, createRootStorage } from "./namespace.mjs";
18
+ var SQLITE_STORAGE_PACKAGE = "@frontmcp/storage-sqlite";
19
+ var PATH_REQUIRED = { error: "is required (the path of the database file)" };
20
+ var sqliteAdapterOptionsSchema = z.object({
21
+ path: z.string(PATH_REQUIRED).min(1, PATH_REQUIRED),
22
+ walMode: z.boolean().optional(),
23
+ busyTimeoutMs: z.number().int().nonnegative().optional(),
24
+ ttlCleanupIntervalMs: z.number().int().nonnegative().optional(),
25
+ encryption: z.object({ secret: z.string().min(1) }).optional()
26
+ });
27
+ function parseSqliteOptions(options) {
28
+ const result = sqliteAdapterOptionsSchema.safeParse(options ?? {});
29
+ if (result.success) return result.data;
30
+ const problems = result.error.issues.map((issue) => `sqlite.${issue.path.join(".")}: ${issue.message}`);
31
+ throw new StorageConfigError("sqlite", problems.join("; "));
32
+ }
33
+ function loadSqliteStorage() {
34
+ try {
35
+ return __require(SQLITE_STORAGE_PACKAGE);
36
+ } catch (error) {
37
+ const missing = error.code === "MODULE_NOT_FOUND";
38
+ if (!missing || !String(error.message).includes(SQLITE_STORAGE_PACKAGE)) throw error;
39
+ throw new StorageConfigError(
40
+ "sqlite",
41
+ `the sqlite type needs ${SQLITE_STORAGE_PACKAGE}. Install it with: npm install ${SQLITE_STORAGE_PACKAGE}`
42
+ );
43
+ }
44
+ }
45
+ var noDistributedStorageWarned = false;
46
+ function warnNoDistributedStorage() {
47
+ if (noDistributedStorageWarned || getRuntimeContext().runtime === "browser") return;
48
+ noDistributedStorageWarned = true;
49
+ console.warn(
50
+ "[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."
51
+ );
52
+ }
53
+ function detectStorageType() {
54
+ if (getEnv("UPSTASH_REDIS_REST_URL") && getEnv("UPSTASH_REDIS_REST_TOKEN")) {
55
+ return "upstash";
56
+ }
57
+ if (getEnv("KV_REST_API_URL") && getEnv("KV_REST_API_TOKEN")) {
58
+ return "vercel-kv";
59
+ }
60
+ if (getEnv("REDIS_URL") || getEnv("REDIS_HOST")) {
61
+ return "redis";
62
+ }
63
+ return "memory";
64
+ }
65
+ async function createAdapter(type, config) {
66
+ switch (type) {
67
+ case "memory": {
68
+ return new MemoryStorageAdapter(config.memory);
69
+ }
70
+ case "redis": {
71
+ const { RedisStorageAdapter } = await import("./adapters/redis.mjs");
72
+ return new RedisStorageAdapter(config.redis);
73
+ }
74
+ case "vercel-kv": {
75
+ const { VercelKvStorageAdapter } = await import("./adapters/vercel-kv.mjs");
76
+ return new VercelKvStorageAdapter(config.vercelKv);
77
+ }
78
+ case "upstash": {
79
+ const { UpstashStorageAdapter } = await import("./adapters/upstash.mjs");
80
+ return new UpstashStorageAdapter(config.upstash);
81
+ }
82
+ case "sqlite": {
83
+ const { SqliteStorageAdapter } = loadSqliteStorage();
84
+ return new SqliteStorageAdapter(parseSqliteOptions(config.sqlite));
85
+ }
86
+ case "cloudflare-kv": {
87
+ if (!config.cloudflareKv) {
88
+ throw new StorageConfigError(
89
+ "cloudflare-kv",
90
+ "cloudflare-kv requires `cloudflareKv: { namespace }` (the KV binding can't be auto-detected)"
91
+ );
92
+ }
93
+ const { CloudflareKvStorageAdapter } = await import("./adapters/cloudflare-kv.mjs");
94
+ return new CloudflareKvStorageAdapter(config.cloudflareKv);
95
+ }
96
+ case "auto":
97
+ throw new StorageConfigError("auto", "Auto type should be resolved before calling createAdapter");
98
+ default:
99
+ throw new StorageConfigError("unknown", `Unknown storage type: ${type}`);
100
+ }
101
+ }
102
+ async function createStorage(config = {}) {
103
+ let type = config.type ?? "auto";
104
+ const fallback = config.fallback ?? (isProduction() ? "error" : "memory");
105
+ if (type === "auto") {
106
+ type = detectStorageType();
107
+ if (type === "memory" && isProduction()) warnNoDistributedStorage();
108
+ }
109
+ if (type === "sqlite") parseSqliteOptions(config.sqlite);
110
+ let adapter;
111
+ try {
112
+ adapter = await createAdapter(type, config);
113
+ } catch (e) {
114
+ if (fallback === "memory" && type !== "memory") {
115
+ console.warn(
116
+ `[storage] Warning: Failed to create ${type} adapter, falling back to memory. Error: ${e instanceof Error ? e.message : String(e)}`
117
+ );
118
+ adapter = new MemoryStorageAdapter(config.memory);
119
+ } else {
120
+ throw e;
121
+ }
122
+ }
123
+ try {
124
+ await adapter.connect();
125
+ } catch (e) {
126
+ if (fallback === "memory" && type !== "memory") {
127
+ console.warn(
128
+ `[storage] Warning: Failed to connect to ${type}, falling back to memory. Error: ${e instanceof Error ? e.message : String(e)}`
129
+ );
130
+ adapter = new MemoryStorageAdapter(config.memory);
131
+ await adapter.connect();
132
+ } else {
133
+ throw e;
134
+ }
135
+ }
136
+ if (config.prefix) {
137
+ return createNamespacedStorage(adapter, config.prefix);
138
+ }
139
+ return createRootStorage(adapter);
140
+ }
141
+ function createMemoryStorage(config = {}) {
142
+ const adapter = new MemoryStorageAdapter(config);
143
+ if (config.prefix) {
144
+ return createNamespacedStorage(adapter, config.prefix);
145
+ }
146
+ return createRootStorage(adapter);
147
+ }
148
+ function getDetectedStorageType() {
149
+ return detectStorageType();
150
+ }
151
+ export {
152
+ createMemoryStorage,
153
+ createStorage,
154
+ getDetectedStorageType
155
+ };
@@ -0,0 +1,96 @@
1
+ // libs/utils/src/storage/index.ts
2
+ import { createStorage, createMemoryStorage, getDetectedStorageType } from "./factory.mjs";
3
+ import { createChangeFeed, DEFAULT_CHANGE_FEED_COALESCE_MS, DEFAULT_CHANGE_FEED_POLL_INTERVAL_MS } from "./change-feed.mjs";
4
+ import { attachRedisErrorListener, DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS } from "./redis-error-listener.mjs";
5
+ import { mergeRedisUrlFields, describeRedisUrlConflicts } from "./redis-url.mjs";
6
+ import {
7
+ NamespacedStorageImpl,
8
+ createRootStorage,
9
+ createNamespacedStorage,
10
+ buildPrefix,
11
+ NAMESPACE_SEPARATOR
12
+ } from "./namespace.mjs";
13
+ import {
14
+ StorageError,
15
+ StorageConnectionError,
16
+ StorageOperationError,
17
+ StorageNotSupportedError,
18
+ StorageConfigError,
19
+ StorageTTLError,
20
+ StoragePatternError,
21
+ StorageNotConnectedError,
22
+ EncryptedStorageError
23
+ } from "./errors.mjs";
24
+ import { TypedStorage } from "./typed-storage.mjs";
25
+ import { EncryptedTypedStorage } from "./encrypted-typed-storage.mjs";
26
+ import {
27
+ BaseStorageAdapter,
28
+ MemoryStorageAdapter,
29
+ RedisStorageAdapter,
30
+ createRedisClient,
31
+ VercelKvStorageAdapter,
32
+ createVercelKvClient,
33
+ UpstashStorageAdapter,
34
+ FileSystemStorageAdapter,
35
+ LocalStorageAdapter,
36
+ IndexedDBStorageAdapter
37
+ } from "./adapters/index.mjs";
38
+ import { globToRegex, matchesPattern, validatePattern, escapeGlob } from "./utils/pattern.mjs";
39
+ import {
40
+ MAX_TTL_SECONDS,
41
+ validateTTL,
42
+ validateOptionalTTL,
43
+ ttlToExpiresAt,
44
+ expiresAtToTTL,
45
+ isExpired,
46
+ normalizeTTL
47
+ } from "./utils/ttl.mjs";
48
+ export {
49
+ BaseStorageAdapter,
50
+ DEFAULT_CHANGE_FEED_COALESCE_MS,
51
+ DEFAULT_CHANGE_FEED_POLL_INTERVAL_MS,
52
+ DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS,
53
+ EncryptedStorageError,
54
+ EncryptedTypedStorage,
55
+ FileSystemStorageAdapter,
56
+ IndexedDBStorageAdapter,
57
+ LocalStorageAdapter,
58
+ MAX_TTL_SECONDS,
59
+ MemoryStorageAdapter,
60
+ NAMESPACE_SEPARATOR,
61
+ NamespacedStorageImpl,
62
+ RedisStorageAdapter,
63
+ StorageConfigError,
64
+ StorageConnectionError,
65
+ StorageError,
66
+ StorageNotConnectedError,
67
+ StorageNotSupportedError,
68
+ StorageOperationError,
69
+ StoragePatternError,
70
+ StorageTTLError,
71
+ TypedStorage,
72
+ UpstashStorageAdapter,
73
+ VercelKvStorageAdapter,
74
+ attachRedisErrorListener,
75
+ buildPrefix,
76
+ createChangeFeed,
77
+ createMemoryStorage,
78
+ createNamespacedStorage,
79
+ createRedisClient,
80
+ createRootStorage,
81
+ createStorage,
82
+ createVercelKvClient,
83
+ describeRedisUrlConflicts,
84
+ escapeGlob,
85
+ expiresAtToTTL,
86
+ getDetectedStorageType,
87
+ globToRegex,
88
+ isExpired,
89
+ matchesPattern,
90
+ mergeRedisUrlFields,
91
+ normalizeTTL,
92
+ ttlToExpiresAt,
93
+ validateOptionalTTL,
94
+ validatePattern,
95
+ validateTTL
96
+ };