@frontmcp/utils 0.7.2 → 0.8.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.
- package/crypto/hmac-signing.d.ts +96 -0
- package/crypto/index.d.ts +11 -0
- package/crypto/key-persistence/factory.d.ts +58 -0
- package/crypto/key-persistence/index.d.ts +35 -0
- package/crypto/key-persistence/key-persistence.d.ts +143 -0
- package/crypto/key-persistence/schemas.d.ts +180 -0
- package/crypto/key-persistence/types.d.ts +120 -0
- package/esm/index.mjs +1778 -583
- package/esm/package.json +17 -2
- package/fs/fs.d.ts +16 -0
- package/fs/index.d.ts +1 -1
- package/index.d.ts +3 -3
- package/index.js +1802 -585
- package/package.json +17 -2
- package/regex/safe-regex.d.ts +1 -1
- package/storage/adapters/filesystem.d.ts +144 -0
- package/storage/adapters/index.d.ts +2 -0
- package/storage/encrypted-typed-storage.d.ts +196 -0
- package/storage/encrypted-typed-storage.types.d.ts +139 -0
- package/storage/errors.d.ts +11 -0
- package/storage/index.d.ts +7 -2
- package/storage/typed-storage.d.ts +129 -0
- package/storage/typed-storage.types.d.ts +47 -0
package/index.js
CHANGED
|
@@ -57,219 +57,8 @@ var init_jwt_alg = __esm({
|
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
// libs/utils/src/crypto/node.ts
|
|
61
|
-
var node_exports = {};
|
|
62
|
-
__export(node_exports, {
|
|
63
|
-
createSignedJwt: () => createSignedJwt,
|
|
64
|
-
generateRsaKeyPair: () => generateRsaKeyPair,
|
|
65
|
-
isRsaPssAlg: () => isRsaPssAlg,
|
|
66
|
-
jwtAlgToNodeAlg: () => jwtAlgToNodeAlg,
|
|
67
|
-
nodeCrypto: () => nodeCrypto,
|
|
68
|
-
rsaSign: () => rsaSign,
|
|
69
|
-
rsaVerify: () => rsaVerify
|
|
70
|
-
});
|
|
71
|
-
function toUint8Array(buf) {
|
|
72
|
-
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
73
|
-
}
|
|
74
|
-
function toBuffer(data) {
|
|
75
|
-
if (typeof data === "string") {
|
|
76
|
-
return Buffer.from(data, "utf8");
|
|
77
|
-
}
|
|
78
|
-
return Buffer.from(data);
|
|
79
|
-
}
|
|
80
|
-
function generateRsaKeyPair(modulusLength = 2048, alg = "RS256") {
|
|
81
|
-
const kid = `rsa-key-${Date.now()}-${import_node_crypto.default.randomBytes(8).toString("hex")}`;
|
|
82
|
-
const { privateKey, publicKey } = import_node_crypto.default.generateKeyPairSync("rsa", {
|
|
83
|
-
modulusLength
|
|
84
|
-
});
|
|
85
|
-
const exported = publicKey.export({ format: "jwk" });
|
|
86
|
-
const publicJwk = {
|
|
87
|
-
...exported,
|
|
88
|
-
kid,
|
|
89
|
-
alg,
|
|
90
|
-
use: "sig",
|
|
91
|
-
kty: "RSA"
|
|
92
|
-
};
|
|
93
|
-
return { privateKey, publicKey, publicJwk };
|
|
94
|
-
}
|
|
95
|
-
function rsaSign(algorithm, data, privateKey, options) {
|
|
96
|
-
const signingKey = options ? { key: privateKey, ...options } : privateKey;
|
|
97
|
-
return import_node_crypto.default.sign(algorithm, data, signingKey);
|
|
98
|
-
}
|
|
99
|
-
function rsaVerify(jwtAlg, data, publicJwk, signature) {
|
|
100
|
-
const publicKey = import_node_crypto.default.createPublicKey({ key: publicJwk, format: "jwk" });
|
|
101
|
-
const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
|
|
102
|
-
const verifyKey = isRsaPssAlg(jwtAlg) ? {
|
|
103
|
-
key: publicKey,
|
|
104
|
-
padding: import_node_crypto.default.constants.RSA_PKCS1_PSS_PADDING,
|
|
105
|
-
saltLength: import_node_crypto.default.constants.RSA_PSS_SALTLEN_DIGEST
|
|
106
|
-
} : publicKey;
|
|
107
|
-
return import_node_crypto.default.verify(nodeAlgorithm, data, verifyKey, signature);
|
|
108
|
-
}
|
|
109
|
-
function createSignedJwt(payload, privateKey, kid, alg = "RS256") {
|
|
110
|
-
const header = { alg, typ: "JWT", kid };
|
|
111
|
-
const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
|
|
112
|
-
const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
113
|
-
const signatureInput = `${headerB64}.${payloadB64}`;
|
|
114
|
-
const nodeAlgorithm = jwtAlgToNodeAlg(alg);
|
|
115
|
-
const signature = rsaSign(
|
|
116
|
-
nodeAlgorithm,
|
|
117
|
-
Buffer.from(signatureInput),
|
|
118
|
-
privateKey,
|
|
119
|
-
isRsaPssAlg(alg) ? {
|
|
120
|
-
padding: import_node_crypto.default.constants.RSA_PKCS1_PSS_PADDING,
|
|
121
|
-
saltLength: import_node_crypto.default.constants.RSA_PSS_SALTLEN_DIGEST
|
|
122
|
-
} : void 0
|
|
123
|
-
);
|
|
124
|
-
const signatureB64 = signature.toString("base64url");
|
|
125
|
-
return `${headerB64}.${payloadB64}.${signatureB64}`;
|
|
126
|
-
}
|
|
127
|
-
var import_node_crypto, nodeCrypto;
|
|
128
|
-
var init_node = __esm({
|
|
129
|
-
"libs/utils/src/crypto/node.ts"() {
|
|
130
|
-
"use strict";
|
|
131
|
-
import_node_crypto = __toESM(require("node:crypto"));
|
|
132
|
-
init_jwt_alg();
|
|
133
|
-
init_jwt_alg();
|
|
134
|
-
nodeCrypto = {
|
|
135
|
-
randomUUID() {
|
|
136
|
-
return import_node_crypto.default.randomUUID();
|
|
137
|
-
},
|
|
138
|
-
randomBytes(length) {
|
|
139
|
-
return toUint8Array(import_node_crypto.default.randomBytes(length));
|
|
140
|
-
},
|
|
141
|
-
sha256(data) {
|
|
142
|
-
const hash = import_node_crypto.default.createHash("sha256").update(toBuffer(data)).digest();
|
|
143
|
-
return toUint8Array(hash);
|
|
144
|
-
},
|
|
145
|
-
sha256Hex(data) {
|
|
146
|
-
return import_node_crypto.default.createHash("sha256").update(toBuffer(data)).digest("hex");
|
|
147
|
-
},
|
|
148
|
-
hmacSha256(key, data) {
|
|
149
|
-
const hmac2 = import_node_crypto.default.createHmac("sha256", Buffer.from(key)).update(Buffer.from(data)).digest();
|
|
150
|
-
return toUint8Array(hmac2);
|
|
151
|
-
},
|
|
152
|
-
hkdfSha256(ikm, salt, info, length) {
|
|
153
|
-
const ikmBuf = Buffer.from(ikm);
|
|
154
|
-
const saltBuf = salt.length > 0 ? Buffer.from(salt) : Buffer.alloc(32);
|
|
155
|
-
const prk = import_node_crypto.default.createHmac("sha256", saltBuf).update(ikmBuf).digest();
|
|
156
|
-
const hashLen = 32;
|
|
157
|
-
const n = Math.ceil(length / hashLen);
|
|
158
|
-
const chunks = [];
|
|
159
|
-
let prev = Buffer.alloc(0);
|
|
160
|
-
for (let i = 1; i <= n; i++) {
|
|
161
|
-
prev = import_node_crypto.default.createHmac("sha256", prk).update(Buffer.concat([prev, Buffer.from(info), Buffer.from([i])])).digest();
|
|
162
|
-
chunks.push(prev);
|
|
163
|
-
}
|
|
164
|
-
return toUint8Array(Buffer.concat(chunks).subarray(0, length));
|
|
165
|
-
},
|
|
166
|
-
encryptAesGcm(key, plaintext, iv) {
|
|
167
|
-
const cipher = import_node_crypto.default.createCipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
|
|
168
|
-
const encrypted = Buffer.concat([cipher.update(Buffer.from(plaintext)), cipher.final()]);
|
|
169
|
-
const tag = cipher.getAuthTag();
|
|
170
|
-
return {
|
|
171
|
-
ciphertext: toUint8Array(encrypted),
|
|
172
|
-
tag: toUint8Array(tag)
|
|
173
|
-
};
|
|
174
|
-
},
|
|
175
|
-
decryptAesGcm(key, ciphertext, iv, tag) {
|
|
176
|
-
const decipher = import_node_crypto.default.createDecipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
|
|
177
|
-
decipher.setAuthTag(Buffer.from(tag));
|
|
178
|
-
const decrypted = Buffer.concat([decipher.update(Buffer.from(ciphertext)), decipher.final()]);
|
|
179
|
-
return toUint8Array(decrypted);
|
|
180
|
-
},
|
|
181
|
-
timingSafeEqual(a, b) {
|
|
182
|
-
if (a.length !== b.length) return false;
|
|
183
|
-
return import_node_crypto.default.timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
// libs/utils/src/crypto/browser.ts
|
|
190
|
-
var browser_exports = {};
|
|
191
|
-
__export(browser_exports, {
|
|
192
|
-
browserCrypto: () => browserCrypto
|
|
193
|
-
});
|
|
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 = (0, import_utils.randomBytes)(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 import_sha2, import_hmac, import_hkdf, import_utils, import_aes, browserCrypto;
|
|
222
|
-
var init_browser = __esm({
|
|
223
|
-
"libs/utils/src/crypto/browser.ts"() {
|
|
224
|
-
"use strict";
|
|
225
|
-
import_sha2 = require("@noble/hashes/sha2.js");
|
|
226
|
-
import_hmac = require("@noble/hashes/hmac.js");
|
|
227
|
-
import_hkdf = require("@noble/hashes/hkdf.js");
|
|
228
|
-
import_utils = require("@noble/hashes/utils.js");
|
|
229
|
-
import_aes = require("@noble/ciphers/aes.js");
|
|
230
|
-
browserCrypto = {
|
|
231
|
-
randomUUID() {
|
|
232
|
-
return generateUUID();
|
|
233
|
-
},
|
|
234
|
-
randomBytes(length) {
|
|
235
|
-
return (0, import_utils.randomBytes)(length);
|
|
236
|
-
},
|
|
237
|
-
sha256(data) {
|
|
238
|
-
return (0, import_sha2.sha256)(toBytes(data));
|
|
239
|
-
},
|
|
240
|
-
sha256Hex(data) {
|
|
241
|
-
return toHex((0, import_sha2.sha256)(toBytes(data)));
|
|
242
|
-
},
|
|
243
|
-
hmacSha256(key, data) {
|
|
244
|
-
return (0, import_hmac.hmac)(import_sha2.sha256, key, data);
|
|
245
|
-
},
|
|
246
|
-
hkdfSha256(ikm, salt, info, length) {
|
|
247
|
-
const effectiveSalt = salt.length > 0 ? salt : new Uint8Array(32);
|
|
248
|
-
return (0, import_hkdf.hkdf)(import_sha2.sha256, ikm, effectiveSalt, info, length);
|
|
249
|
-
},
|
|
250
|
-
encryptAesGcm(key, plaintext, iv) {
|
|
251
|
-
const cipher = (0, import_aes.gcm)(key, iv);
|
|
252
|
-
const sealed = cipher.encrypt(plaintext);
|
|
253
|
-
const ciphertext = sealed.slice(0, -16);
|
|
254
|
-
const tag = sealed.slice(-16);
|
|
255
|
-
return { ciphertext, tag };
|
|
256
|
-
},
|
|
257
|
-
decryptAesGcm(key, ciphertext, iv, tag) {
|
|
258
|
-
const cipher = (0, import_aes.gcm)(key, iv);
|
|
259
|
-
const sealed = new Uint8Array(ciphertext.length + tag.length);
|
|
260
|
-
sealed.set(ciphertext);
|
|
261
|
-
sealed.set(tag, ciphertext.length);
|
|
262
|
-
return cipher.decrypt(sealed);
|
|
263
|
-
},
|
|
264
|
-
timingSafeEqual(a, b) {
|
|
265
|
-
return constantTimeEqual(a, b);
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
|
|
271
60
|
// libs/utils/src/storage/errors.ts
|
|
272
|
-
var StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError;
|
|
61
|
+
var StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError, EncryptedStorageError;
|
|
273
62
|
var init_errors = __esm({
|
|
274
63
|
"libs/utils/src/storage/errors.ts"() {
|
|
275
64
|
"use strict";
|
|
@@ -287,7 +76,8 @@ var init_errors = __esm({
|
|
|
287
76
|
};
|
|
288
77
|
StorageConnectionError = class extends StorageError {
|
|
289
78
|
constructor(message, cause, backend) {
|
|
290
|
-
|
|
79
|
+
const fullMessage = cause ? `${message}: ${cause.message}` : message;
|
|
80
|
+
super(fullMessage, cause);
|
|
291
81
|
this.backend = backend;
|
|
292
82
|
this.name = "StorageConnectionError";
|
|
293
83
|
}
|
|
@@ -337,6 +127,12 @@ var init_errors = __esm({
|
|
|
337
127
|
this.name = "StorageNotConnectedError";
|
|
338
128
|
}
|
|
339
129
|
};
|
|
130
|
+
EncryptedStorageError = class extends StorageError {
|
|
131
|
+
constructor(message) {
|
|
132
|
+
super(message);
|
|
133
|
+
this.name = "EncryptedStorageError";
|
|
134
|
+
}
|
|
135
|
+
};
|
|
340
136
|
}
|
|
341
137
|
});
|
|
342
138
|
|
|
@@ -502,15 +298,309 @@ var init_base = __esm({
|
|
|
502
298
|
}
|
|
503
299
|
});
|
|
504
300
|
|
|
505
|
-
// libs/utils/src/storage/
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
301
|
+
// libs/utils/src/storage/utils/pattern.ts
|
|
302
|
+
function globToRegex(pattern) {
|
|
303
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
304
|
+
throw new StoragePatternError(
|
|
305
|
+
pattern.substring(0, 50) + "...",
|
|
306
|
+
`Pattern exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
const wildcardCount = (pattern.match(/[*?]/g) || []).length;
|
|
310
|
+
if (wildcardCount > MAX_WILDCARDS) {
|
|
311
|
+
throw new StoragePatternError(pattern, `Pattern has too many wildcards (max: ${MAX_WILDCARDS})`);
|
|
312
|
+
}
|
|
313
|
+
if (pattern === "" || pattern === "*") {
|
|
314
|
+
return /^.*$/;
|
|
315
|
+
}
|
|
316
|
+
let regexStr = "^";
|
|
317
|
+
let prevChar = "";
|
|
318
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
319
|
+
const char = pattern[i];
|
|
320
|
+
switch (char) {
|
|
321
|
+
case "*":
|
|
322
|
+
if (prevChar !== "*") {
|
|
323
|
+
regexStr += ".*";
|
|
324
|
+
}
|
|
325
|
+
break;
|
|
326
|
+
case "?":
|
|
327
|
+
regexStr += ".";
|
|
328
|
+
break;
|
|
329
|
+
default:
|
|
330
|
+
regexStr += char.replace(REGEX_SPECIAL_CHARS, "\\$&");
|
|
331
|
+
}
|
|
332
|
+
prevChar = char;
|
|
333
|
+
}
|
|
334
|
+
regexStr += "$";
|
|
511
335
|
try {
|
|
512
|
-
return
|
|
336
|
+
return new RegExp(regexStr);
|
|
513
337
|
} catch {
|
|
338
|
+
throw new StoragePatternError(pattern, "Failed to compile pattern to regex");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function matchesPattern(key, pattern) {
|
|
342
|
+
const regex = globToRegex(pattern);
|
|
343
|
+
return regex.test(key);
|
|
344
|
+
}
|
|
345
|
+
function validatePattern(pattern) {
|
|
346
|
+
try {
|
|
347
|
+
globToRegex(pattern);
|
|
348
|
+
return { valid: true };
|
|
349
|
+
} catch (e) {
|
|
350
|
+
return {
|
|
351
|
+
valid: false,
|
|
352
|
+
error: e instanceof Error ? e.message : "Invalid pattern"
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function escapeGlob(literal) {
|
|
357
|
+
return literal.replace(/[*?\\]/g, "\\$&");
|
|
358
|
+
}
|
|
359
|
+
var MAX_PATTERN_LENGTH, MAX_WILDCARDS, REGEX_SPECIAL_CHARS;
|
|
360
|
+
var init_pattern = __esm({
|
|
361
|
+
"libs/utils/src/storage/utils/pattern.ts"() {
|
|
362
|
+
"use strict";
|
|
363
|
+
init_errors();
|
|
364
|
+
MAX_PATTERN_LENGTH = 500;
|
|
365
|
+
MAX_WILDCARDS = 20;
|
|
366
|
+
REGEX_SPECIAL_CHARS = /[.+^${}()|[\]\\]/g;
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// libs/utils/src/crypto/node.ts
|
|
371
|
+
var node_exports = {};
|
|
372
|
+
__export(node_exports, {
|
|
373
|
+
createSignedJwt: () => createSignedJwt,
|
|
374
|
+
generateRsaKeyPair: () => generateRsaKeyPair,
|
|
375
|
+
isRsaPssAlg: () => isRsaPssAlg,
|
|
376
|
+
jwtAlgToNodeAlg: () => jwtAlgToNodeAlg,
|
|
377
|
+
nodeCrypto: () => nodeCrypto,
|
|
378
|
+
rsaSign: () => rsaSign,
|
|
379
|
+
rsaVerify: () => rsaVerify
|
|
380
|
+
});
|
|
381
|
+
function toUint8Array(buf) {
|
|
382
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
383
|
+
}
|
|
384
|
+
function toBuffer(data) {
|
|
385
|
+
if (typeof data === "string") {
|
|
386
|
+
return Buffer.from(data, "utf8");
|
|
387
|
+
}
|
|
388
|
+
return Buffer.from(data);
|
|
389
|
+
}
|
|
390
|
+
function generateRsaKeyPair(modulusLength = 2048, alg = "RS256") {
|
|
391
|
+
const kid = `rsa-key-${Date.now()}-${import_node_crypto.default.randomBytes(8).toString("hex")}`;
|
|
392
|
+
const { privateKey, publicKey } = import_node_crypto.default.generateKeyPairSync("rsa", {
|
|
393
|
+
modulusLength
|
|
394
|
+
});
|
|
395
|
+
const exported = publicKey.export({ format: "jwk" });
|
|
396
|
+
const publicJwk = {
|
|
397
|
+
...exported,
|
|
398
|
+
kid,
|
|
399
|
+
alg,
|
|
400
|
+
use: "sig",
|
|
401
|
+
kty: "RSA"
|
|
402
|
+
};
|
|
403
|
+
return { privateKey, publicKey, publicJwk };
|
|
404
|
+
}
|
|
405
|
+
function rsaSign(algorithm, data, privateKey, options) {
|
|
406
|
+
const signingKey = options ? { key: privateKey, ...options } : privateKey;
|
|
407
|
+
return import_node_crypto.default.sign(algorithm, data, signingKey);
|
|
408
|
+
}
|
|
409
|
+
function rsaVerify(jwtAlg, data, publicJwk, signature) {
|
|
410
|
+
const publicKey = import_node_crypto.default.createPublicKey({ key: publicJwk, format: "jwk" });
|
|
411
|
+
const nodeAlgorithm = jwtAlgToNodeAlg(jwtAlg);
|
|
412
|
+
const verifyKey = isRsaPssAlg(jwtAlg) ? {
|
|
413
|
+
key: publicKey,
|
|
414
|
+
padding: import_node_crypto.default.constants.RSA_PKCS1_PSS_PADDING,
|
|
415
|
+
saltLength: import_node_crypto.default.constants.RSA_PSS_SALTLEN_DIGEST
|
|
416
|
+
} : publicKey;
|
|
417
|
+
return import_node_crypto.default.verify(nodeAlgorithm, data, verifyKey, signature);
|
|
418
|
+
}
|
|
419
|
+
function createSignedJwt(payload, privateKey, kid, alg = "RS256") {
|
|
420
|
+
const header = { alg, typ: "JWT", kid };
|
|
421
|
+
const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
|
|
422
|
+
const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
423
|
+
const signatureInput = `${headerB64}.${payloadB64}`;
|
|
424
|
+
const nodeAlgorithm = jwtAlgToNodeAlg(alg);
|
|
425
|
+
const signature = rsaSign(
|
|
426
|
+
nodeAlgorithm,
|
|
427
|
+
Buffer.from(signatureInput),
|
|
428
|
+
privateKey,
|
|
429
|
+
isRsaPssAlg(alg) ? {
|
|
430
|
+
padding: import_node_crypto.default.constants.RSA_PKCS1_PSS_PADDING,
|
|
431
|
+
saltLength: import_node_crypto.default.constants.RSA_PSS_SALTLEN_DIGEST
|
|
432
|
+
} : void 0
|
|
433
|
+
);
|
|
434
|
+
const signatureB64 = signature.toString("base64url");
|
|
435
|
+
return `${headerB64}.${payloadB64}.${signatureB64}`;
|
|
436
|
+
}
|
|
437
|
+
var import_node_crypto, nodeCrypto;
|
|
438
|
+
var init_node = __esm({
|
|
439
|
+
"libs/utils/src/crypto/node.ts"() {
|
|
440
|
+
"use strict";
|
|
441
|
+
import_node_crypto = __toESM(require("node:crypto"));
|
|
442
|
+
init_jwt_alg();
|
|
443
|
+
init_jwt_alg();
|
|
444
|
+
nodeCrypto = {
|
|
445
|
+
randomUUID() {
|
|
446
|
+
return import_node_crypto.default.randomUUID();
|
|
447
|
+
},
|
|
448
|
+
randomBytes(length) {
|
|
449
|
+
return toUint8Array(import_node_crypto.default.randomBytes(length));
|
|
450
|
+
},
|
|
451
|
+
sha256(data) {
|
|
452
|
+
const hash = import_node_crypto.default.createHash("sha256").update(toBuffer(data)).digest();
|
|
453
|
+
return toUint8Array(hash);
|
|
454
|
+
},
|
|
455
|
+
sha256Hex(data) {
|
|
456
|
+
return import_node_crypto.default.createHash("sha256").update(toBuffer(data)).digest("hex");
|
|
457
|
+
},
|
|
458
|
+
hmacSha256(key, data) {
|
|
459
|
+
const hmac2 = import_node_crypto.default.createHmac("sha256", Buffer.from(key)).update(Buffer.from(data)).digest();
|
|
460
|
+
return toUint8Array(hmac2);
|
|
461
|
+
},
|
|
462
|
+
hkdfSha256(ikm, salt, info, length) {
|
|
463
|
+
const ikmBuf = Buffer.from(ikm);
|
|
464
|
+
const saltBuf = salt.length > 0 ? Buffer.from(salt) : Buffer.alloc(32);
|
|
465
|
+
const prk = import_node_crypto.default.createHmac("sha256", saltBuf).update(ikmBuf).digest();
|
|
466
|
+
const hashLen = 32;
|
|
467
|
+
const n = Math.ceil(length / hashLen);
|
|
468
|
+
const chunks = [];
|
|
469
|
+
let prev = Buffer.alloc(0);
|
|
470
|
+
for (let i = 1; i <= n; i++) {
|
|
471
|
+
prev = import_node_crypto.default.createHmac("sha256", prk).update(Buffer.concat([prev, Buffer.from(info), Buffer.from([i])])).digest();
|
|
472
|
+
chunks.push(prev);
|
|
473
|
+
}
|
|
474
|
+
return toUint8Array(Buffer.concat(chunks).subarray(0, length));
|
|
475
|
+
},
|
|
476
|
+
encryptAesGcm(key, plaintext, iv) {
|
|
477
|
+
const cipher = import_node_crypto.default.createCipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
|
|
478
|
+
const encrypted = Buffer.concat([cipher.update(Buffer.from(plaintext)), cipher.final()]);
|
|
479
|
+
const tag = cipher.getAuthTag();
|
|
480
|
+
return {
|
|
481
|
+
ciphertext: toUint8Array(encrypted),
|
|
482
|
+
tag: toUint8Array(tag)
|
|
483
|
+
};
|
|
484
|
+
},
|
|
485
|
+
decryptAesGcm(key, ciphertext, iv, tag) {
|
|
486
|
+
const decipher = import_node_crypto.default.createDecipheriv("aes-256-gcm", Buffer.from(key), Buffer.from(iv));
|
|
487
|
+
decipher.setAuthTag(Buffer.from(tag));
|
|
488
|
+
const decrypted = Buffer.concat([decipher.update(Buffer.from(ciphertext)), decipher.final()]);
|
|
489
|
+
return toUint8Array(decrypted);
|
|
490
|
+
},
|
|
491
|
+
timingSafeEqual(a, b) {
|
|
492
|
+
if (a.length !== b.length) return false;
|
|
493
|
+
return import_node_crypto.default.timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// libs/utils/src/crypto/browser.ts
|
|
500
|
+
var browser_exports = {};
|
|
501
|
+
__export(browser_exports, {
|
|
502
|
+
browserCrypto: () => browserCrypto
|
|
503
|
+
});
|
|
504
|
+
function toBytes(data) {
|
|
505
|
+
if (typeof data === "string") {
|
|
506
|
+
return new TextEncoder().encode(data);
|
|
507
|
+
}
|
|
508
|
+
return data;
|
|
509
|
+
}
|
|
510
|
+
function toHex(bytes) {
|
|
511
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
512
|
+
}
|
|
513
|
+
function generateUUID() {
|
|
514
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
515
|
+
return crypto.randomUUID();
|
|
516
|
+
}
|
|
517
|
+
const bytes = (0, import_utils.randomBytes)(16);
|
|
518
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
519
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
520
|
+
const hex = toHex(bytes);
|
|
521
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
522
|
+
}
|
|
523
|
+
function constantTimeEqual(a, b) {
|
|
524
|
+
if (a.length !== b.length) return false;
|
|
525
|
+
let result = 0;
|
|
526
|
+
for (let i = 0; i < a.length; i++) {
|
|
527
|
+
result |= a[i] ^ b[i];
|
|
528
|
+
}
|
|
529
|
+
return result === 0;
|
|
530
|
+
}
|
|
531
|
+
var import_sha2, import_hmac, import_hkdf, import_utils, import_aes, browserCrypto;
|
|
532
|
+
var init_browser = __esm({
|
|
533
|
+
"libs/utils/src/crypto/browser.ts"() {
|
|
534
|
+
"use strict";
|
|
535
|
+
import_sha2 = require("@noble/hashes/sha2.js");
|
|
536
|
+
import_hmac = require("@noble/hashes/hmac.js");
|
|
537
|
+
import_hkdf = require("@noble/hashes/hkdf.js");
|
|
538
|
+
import_utils = require("@noble/hashes/utils.js");
|
|
539
|
+
import_aes = require("@noble/ciphers/aes.js");
|
|
540
|
+
browserCrypto = {
|
|
541
|
+
randomUUID() {
|
|
542
|
+
return generateUUID();
|
|
543
|
+
},
|
|
544
|
+
randomBytes(length) {
|
|
545
|
+
return (0, import_utils.randomBytes)(length);
|
|
546
|
+
},
|
|
547
|
+
sha256(data) {
|
|
548
|
+
return (0, import_sha2.sha256)(toBytes(data));
|
|
549
|
+
},
|
|
550
|
+
sha256Hex(data) {
|
|
551
|
+
return toHex((0, import_sha2.sha256)(toBytes(data)));
|
|
552
|
+
},
|
|
553
|
+
hmacSha256(key, data) {
|
|
554
|
+
return (0, import_hmac.hmac)(import_sha2.sha256, key, data);
|
|
555
|
+
},
|
|
556
|
+
hkdfSha256(ikm, salt, info, length) {
|
|
557
|
+
const effectiveSalt = salt.length > 0 ? salt : new Uint8Array(32);
|
|
558
|
+
return (0, import_hkdf.hkdf)(import_sha2.sha256, ikm, effectiveSalt, info, length);
|
|
559
|
+
},
|
|
560
|
+
encryptAesGcm(key, plaintext, iv) {
|
|
561
|
+
const cipher = (0, import_aes.gcm)(key, iv);
|
|
562
|
+
const sealed = cipher.encrypt(plaintext);
|
|
563
|
+
const ciphertext = sealed.slice(0, -16);
|
|
564
|
+
const tag = sealed.slice(-16);
|
|
565
|
+
return { ciphertext, tag };
|
|
566
|
+
},
|
|
567
|
+
decryptAesGcm(key, ciphertext, iv, tag) {
|
|
568
|
+
const cipher = (0, import_aes.gcm)(key, iv);
|
|
569
|
+
const sealed = new Uint8Array(ciphertext.length + tag.length);
|
|
570
|
+
sealed.set(ciphertext);
|
|
571
|
+
sealed.set(tag, ciphertext.length);
|
|
572
|
+
return cipher.decrypt(sealed);
|
|
573
|
+
},
|
|
574
|
+
timingSafeEqual(a, b) {
|
|
575
|
+
return constantTimeEqual(a, b);
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
// libs/utils/src/storage/utils/index.ts
|
|
582
|
+
var init_utils = __esm({
|
|
583
|
+
"libs/utils/src/storage/utils/index.ts"() {
|
|
584
|
+
"use strict";
|
|
585
|
+
init_ttl();
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
// libs/utils/src/storage/adapters/redis.ts
|
|
590
|
+
var redis_exports = {};
|
|
591
|
+
__export(redis_exports, {
|
|
592
|
+
RedisStorageAdapter: () => RedisStorageAdapter
|
|
593
|
+
});
|
|
594
|
+
function getRedisClass() {
|
|
595
|
+
try {
|
|
596
|
+
return require("ioredis").default || require("ioredis");
|
|
597
|
+
} catch (error) {
|
|
598
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
599
|
+
if (msg.includes("Dynamic require") || msg.includes("require is not defined")) {
|
|
600
|
+
throw new Error(
|
|
601
|
+
`Failed to load ioredis: ${msg}. This typically happens with ESM bundlers (esbuild, Vite). Ensure your bundler externalizes ioredis or use CJS mode.`
|
|
602
|
+
);
|
|
603
|
+
}
|
|
514
604
|
throw new Error("ioredis is required for Redis storage adapter. Install it with: npm install ioredis");
|
|
515
605
|
}
|
|
516
606
|
}
|
|
@@ -520,7 +610,7 @@ var init_redis = __esm({
|
|
|
520
610
|
"use strict";
|
|
521
611
|
init_base();
|
|
522
612
|
init_errors();
|
|
523
|
-
|
|
613
|
+
init_utils();
|
|
524
614
|
RedisStorageAdapter = class extends BaseStorageAdapter {
|
|
525
615
|
backendName = "redis";
|
|
526
616
|
client;
|
|
@@ -1356,6 +1446,10 @@ __export(index_exports, {
|
|
|
1356
1446
|
DEFAULT_CODE_VERIFIER_LENGTH: () => DEFAULT_CODE_VERIFIER_LENGTH,
|
|
1357
1447
|
DEFAULT_MAX_INPUT_LENGTH: () => DEFAULT_MAX_INPUT_LENGTH,
|
|
1358
1448
|
EncryptedBlobError: () => EncryptedBlobError,
|
|
1449
|
+
EncryptedStorageError: () => EncryptedStorageError,
|
|
1450
|
+
EncryptedTypedStorage: () => EncryptedTypedStorage,
|
|
1451
|
+
FileSystemStorageAdapter: () => FileSystemStorageAdapter,
|
|
1452
|
+
KeyPersistence: () => KeyPersistence,
|
|
1359
1453
|
MAX_CODE_VERIFIER_LENGTH: () => MAX_CODE_VERIFIER_LENGTH,
|
|
1360
1454
|
MAX_TTL_SECONDS: () => MAX_TTL_SECONDS,
|
|
1361
1455
|
MIN_CODE_VERIFIER_LENGTH: () => MIN_CODE_VERIFIER_LENGTH,
|
|
@@ -1363,7 +1457,7 @@ __export(index_exports, {
|
|
|
1363
1457
|
NAMESPACE_SEPARATOR: () => NAMESPACE_SEPARATOR,
|
|
1364
1458
|
NamespacedStorageImpl: () => NamespacedStorageImpl,
|
|
1365
1459
|
PkceError: () => PkceError,
|
|
1366
|
-
REDOS_THRESHOLDS: () =>
|
|
1460
|
+
REDOS_THRESHOLDS: () => import_ast.REDOS_THRESHOLDS,
|
|
1367
1461
|
RedisStorageAdapter: () => RedisStorageAdapter,
|
|
1368
1462
|
StorageConfigError: () => StorageConfigError,
|
|
1369
1463
|
StorageConnectionError: () => StorageConnectionError,
|
|
@@ -1373,11 +1467,17 @@ __export(index_exports, {
|
|
|
1373
1467
|
StorageOperationError: () => StorageOperationError,
|
|
1374
1468
|
StoragePatternError: () => StoragePatternError,
|
|
1375
1469
|
StorageTTLError: () => StorageTTLError,
|
|
1470
|
+
TypedStorage: () => TypedStorage,
|
|
1376
1471
|
UpstashStorageAdapter: () => UpstashStorageAdapter,
|
|
1377
1472
|
VercelKvStorageAdapter: () => VercelKvStorageAdapter,
|
|
1378
1473
|
access: () => access,
|
|
1379
1474
|
analyzePattern: () => analyzePattern,
|
|
1475
|
+
anyKeyDataSchema: () => anyKeyDataSchema,
|
|
1380
1476
|
assertNode: () => assertNode,
|
|
1477
|
+
asymmetricAlgSchema: () => asymmetricAlgSchema,
|
|
1478
|
+
asymmetricKeyDataSchema: () => asymmetricKeyDataSchema,
|
|
1479
|
+
base64Decode: () => base64Decode,
|
|
1480
|
+
base64Encode: () => base64Encode,
|
|
1381
1481
|
base64urlDecode: () => base64urlDecode,
|
|
1382
1482
|
base64urlEncode: () => base64urlEncode,
|
|
1383
1483
|
buildPrefix: () => buildPrefix,
|
|
@@ -1387,6 +1487,8 @@ __export(index_exports, {
|
|
|
1387
1487
|
collapseWhitespace: () => collapseWhitespace,
|
|
1388
1488
|
copyFile: () => copyFile,
|
|
1389
1489
|
cp: () => cp,
|
|
1490
|
+
createKeyPersistence: () => createKeyPersistence,
|
|
1491
|
+
createKeyPersistenceWithStorage: () => createKeyPersistenceWithStorage,
|
|
1390
1492
|
createMemoryStorage: () => createMemoryStorage,
|
|
1391
1493
|
createNamespacedStorage: () => createNamespacedStorage,
|
|
1392
1494
|
createRootStorage: () => createRootStorage,
|
|
@@ -1428,6 +1530,7 @@ __export(index_exports, {
|
|
|
1428
1530
|
hmacSha256: () => hmacSha256,
|
|
1429
1531
|
idFromString: () => idFromString,
|
|
1430
1532
|
inferMimeType: () => inferMimeType,
|
|
1533
|
+
isAsymmetricKeyData: () => isAsymmetricKeyData,
|
|
1431
1534
|
isBrowser: () => isBrowser,
|
|
1432
1535
|
isDirEmpty: () => isDirEmpty,
|
|
1433
1536
|
isExpired: () => isExpired,
|
|
@@ -1436,7 +1539,9 @@ __export(index_exports, {
|
|
|
1436
1539
|
isPatternSafe: () => isPatternSafe,
|
|
1437
1540
|
isRsaPssAlg: () => isRsaPssAlg,
|
|
1438
1541
|
isSecretCached: () => isSecretCached,
|
|
1542
|
+
isSecretKeyData: () => isSecretKeyData,
|
|
1439
1543
|
isSecretPersistenceEnabled: () => isSecretPersistenceEnabled,
|
|
1544
|
+
isSignedData: () => isSignedData,
|
|
1440
1545
|
isUriTemplate: () => isUriTemplate,
|
|
1441
1546
|
isValidCodeChallenge: () => isValidCodeChallenge,
|
|
1442
1547
|
isValidCodeVerifier: () => isValidCodeVerifier,
|
|
@@ -1451,12 +1556,14 @@ __export(index_exports, {
|
|
|
1451
1556
|
mkdir: () => mkdir,
|
|
1452
1557
|
mkdtemp: () => mkdtemp,
|
|
1453
1558
|
normalizeTTL: () => normalizeTTL,
|
|
1559
|
+
parseKeyData: () => parseKeyData,
|
|
1454
1560
|
parseSecretData: () => parseSecretData,
|
|
1455
1561
|
parseUriTemplate: () => parseUriTemplate,
|
|
1456
1562
|
randomBytes: () => randomBytes,
|
|
1457
1563
|
randomUUID: () => randomUUID,
|
|
1458
1564
|
readFile: () => readFile,
|
|
1459
1565
|
readFileBuffer: () => readFileBuffer,
|
|
1566
|
+
readFileSync: () => readFileSync,
|
|
1460
1567
|
readJSON: () => readJSON,
|
|
1461
1568
|
readdir: () => readdir,
|
|
1462
1569
|
rename: () => rename,
|
|
@@ -1473,12 +1580,14 @@ __export(index_exports, {
|
|
|
1473
1580
|
sanitizeToJson: () => sanitizeToJson,
|
|
1474
1581
|
saveSecret: () => saveSecret,
|
|
1475
1582
|
secretDataSchema: () => secretDataSchema,
|
|
1583
|
+
secretKeyDataSchema: () => secretKeyDataSchema,
|
|
1476
1584
|
sepFor: () => sepFor,
|
|
1477
1585
|
serializeBlob: () => serializeBlob,
|
|
1478
1586
|
sha256: () => sha256,
|
|
1479
1587
|
sha256Base64url: () => sha256Base64url,
|
|
1480
1588
|
sha256Hex: () => sha256Hex,
|
|
1481
1589
|
shortHash: () => shortHash,
|
|
1590
|
+
signData: () => signData,
|
|
1482
1591
|
splitWords: () => splitWords,
|
|
1483
1592
|
stat: () => stat,
|
|
1484
1593
|
timingSafeEqual: () => timingSafeEqual,
|
|
@@ -1494,18 +1603,21 @@ __export(index_exports, {
|
|
|
1494
1603
|
ttlToExpiresAt: () => ttlToExpiresAt,
|
|
1495
1604
|
unlink: () => unlink,
|
|
1496
1605
|
validateBaseUrl: () => validateBaseUrl,
|
|
1606
|
+
validateKeyData: () => validateKeyData,
|
|
1497
1607
|
validateOptionalTTL: () => validateOptionalTTL,
|
|
1498
1608
|
validatePattern: () => validatePattern,
|
|
1499
1609
|
validateSecretData: () => validateSecretData,
|
|
1500
1610
|
validateTTL: () => validateTTL,
|
|
1501
1611
|
verifyCodeChallenge: () => verifyCodeChallenge,
|
|
1612
|
+
verifyData: () => verifyData,
|
|
1613
|
+
verifyOrParseData: () => verifyOrParseData,
|
|
1502
1614
|
writeFile: () => writeFile,
|
|
1503
1615
|
writeJSON: () => writeJSON
|
|
1504
1616
|
});
|
|
1505
1617
|
module.exports = __toCommonJS(index_exports);
|
|
1506
1618
|
|
|
1507
1619
|
// libs/utils/src/regex/safe-regex.ts
|
|
1508
|
-
var
|
|
1620
|
+
var import_ast = require("@enclave-vm/ast");
|
|
1509
1621
|
var DEFAULT_MAX_INPUT_LENGTH = 5e4;
|
|
1510
1622
|
function analyzePattern(pattern, level = "polynomial") {
|
|
1511
1623
|
const patternStr = typeof pattern === "string" ? pattern : pattern.source;
|
|
@@ -1520,7 +1632,7 @@ function analyzePattern(pattern, level = "polynomial") {
|
|
|
1520
1632
|
};
|
|
1521
1633
|
}
|
|
1522
1634
|
try {
|
|
1523
|
-
const result = (0,
|
|
1635
|
+
const result = (0, import_ast.analyzeForReDoS)(patternStr, level);
|
|
1524
1636
|
return {
|
|
1525
1637
|
safe: !result.vulnerable,
|
|
1526
1638
|
score: result.score,
|
|
@@ -2001,6 +2113,7 @@ function assertNode(feature) {
|
|
|
2001
2113
|
|
|
2002
2114
|
// libs/utils/src/fs/fs.ts
|
|
2003
2115
|
var _fsp = null;
|
|
2116
|
+
var _fs = null;
|
|
2004
2117
|
var _spawn = null;
|
|
2005
2118
|
function getFsp() {
|
|
2006
2119
|
if (!_fsp) {
|
|
@@ -2009,8 +2122,15 @@ function getFsp() {
|
|
|
2009
2122
|
}
|
|
2010
2123
|
return _fsp;
|
|
2011
2124
|
}
|
|
2012
|
-
function
|
|
2013
|
-
if (!
|
|
2125
|
+
function getFs() {
|
|
2126
|
+
if (!_fs) {
|
|
2127
|
+
assertNode("File system operations");
|
|
2128
|
+
_fs = require("fs");
|
|
2129
|
+
}
|
|
2130
|
+
return _fs;
|
|
2131
|
+
}
|
|
2132
|
+
function getSpawn() {
|
|
2133
|
+
if (!_spawn) {
|
|
2014
2134
|
assertNode("Child process operations");
|
|
2015
2135
|
_spawn = require("child_process").spawn;
|
|
2016
2136
|
}
|
|
@@ -2021,6 +2141,10 @@ async function readFile(p, encoding = "utf8") {
|
|
|
2021
2141
|
const fsp = getFsp();
|
|
2022
2142
|
return fsp.readFile(p, encoding);
|
|
2023
2143
|
}
|
|
2144
|
+
function readFileSync(p, encoding = "utf8") {
|
|
2145
|
+
const fs = getFs();
|
|
2146
|
+
return fs.readFileSync(p, encoding);
|
|
2147
|
+
}
|
|
2024
2148
|
async function readFileBuffer(p) {
|
|
2025
2149
|
const fsp = getFsp();
|
|
2026
2150
|
return fsp.readFile(p);
|
|
@@ -2516,379 +2640,372 @@ function isSecretCached(options) {
|
|
|
2516
2640
|
return secretCache.has(cacheKey);
|
|
2517
2641
|
}
|
|
2518
2642
|
|
|
2519
|
-
// libs/utils/src/crypto/
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
}
|
|
2535
|
-
|
|
2536
|
-
return getCrypto().randomUUID();
|
|
2643
|
+
// libs/utils/src/crypto/hmac-signing.ts
|
|
2644
|
+
function computeSignature(data, secret) {
|
|
2645
|
+
const encoder = new TextEncoder();
|
|
2646
|
+
const keyBytes = encoder.encode(secret);
|
|
2647
|
+
const dataBytes = encoder.encode(data);
|
|
2648
|
+
const hmac2 = hmacSha256(keyBytes, dataBytes);
|
|
2649
|
+
return base64urlEncode(hmac2);
|
|
2650
|
+
}
|
|
2651
|
+
function signData(data, config) {
|
|
2652
|
+
const jsonData = JSON.stringify(data);
|
|
2653
|
+
const sig = computeSignature(jsonData, config.secret);
|
|
2654
|
+
const signed = {
|
|
2655
|
+
data,
|
|
2656
|
+
sig,
|
|
2657
|
+
v: 1
|
|
2658
|
+
};
|
|
2659
|
+
return JSON.stringify(signed);
|
|
2537
2660
|
}
|
|
2538
|
-
function
|
|
2539
|
-
|
|
2540
|
-
|
|
2661
|
+
function verifyData(signedJson, config) {
|
|
2662
|
+
try {
|
|
2663
|
+
const parsed = JSON.parse(signedJson);
|
|
2664
|
+
if (!parsed || typeof parsed !== "object" || !("sig" in parsed)) {
|
|
2665
|
+
return null;
|
|
2666
|
+
}
|
|
2667
|
+
const signed = parsed;
|
|
2668
|
+
if (signed.v !== 1) {
|
|
2669
|
+
return null;
|
|
2670
|
+
}
|
|
2671
|
+
const jsonData = JSON.stringify(signed.data);
|
|
2672
|
+
const expectedSig = computeSignature(jsonData, config.secret);
|
|
2673
|
+
const sigBytes = base64urlDecode(signed.sig);
|
|
2674
|
+
const expectedBytes = base64urlDecode(expectedSig);
|
|
2675
|
+
if (sigBytes.length !== expectedBytes.length) {
|
|
2676
|
+
return null;
|
|
2677
|
+
}
|
|
2678
|
+
if (!timingSafeEqual(sigBytes, expectedBytes)) {
|
|
2679
|
+
return null;
|
|
2680
|
+
}
|
|
2681
|
+
return signed.data;
|
|
2682
|
+
} catch {
|
|
2683
|
+
return null;
|
|
2541
2684
|
}
|
|
2542
|
-
return getCrypto().randomBytes(length);
|
|
2543
|
-
}
|
|
2544
|
-
function sha256(data) {
|
|
2545
|
-
return getCrypto().sha256(data);
|
|
2546
|
-
}
|
|
2547
|
-
function sha256Hex(data) {
|
|
2548
|
-
return getCrypto().sha256Hex(data);
|
|
2549
|
-
}
|
|
2550
|
-
function hmacSha256(key, data) {
|
|
2551
|
-
return getCrypto().hmacSha256(key, data);
|
|
2552
2685
|
}
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
throw new Error(`HKDF-SHA256 length cannot exceed ${HKDF_SHA256_MAX_LENGTH} bytes, got ${length}`);
|
|
2686
|
+
function isSignedData(json) {
|
|
2687
|
+
try {
|
|
2688
|
+
const parsed = JSON.parse(json);
|
|
2689
|
+
return parsed && typeof parsed === "object" && "sig" in parsed && "v" in parsed;
|
|
2690
|
+
} catch {
|
|
2691
|
+
return false;
|
|
2560
2692
|
}
|
|
2561
|
-
return getCrypto().hkdfSha256(ikm, salt, info, length);
|
|
2562
2693
|
}
|
|
2563
|
-
function
|
|
2564
|
-
if (
|
|
2565
|
-
|
|
2694
|
+
function verifyOrParseData(json, config) {
|
|
2695
|
+
if (isSignedData(json)) {
|
|
2696
|
+
return verifyData(json, config);
|
|
2566
2697
|
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2698
|
+
try {
|
|
2699
|
+
return JSON.parse(json);
|
|
2700
|
+
} catch {
|
|
2701
|
+
return null;
|
|
2569
2702
|
}
|
|
2570
|
-
return getCrypto().encryptAesGcm(key, plaintext, iv);
|
|
2571
2703
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2704
|
+
|
|
2705
|
+
// libs/utils/src/crypto/key-persistence/schemas.ts
|
|
2706
|
+
var import_zod2 = require("zod");
|
|
2707
|
+
var MAX_CLOCK_DRIFT_MS2 = 6e4;
|
|
2708
|
+
var MAX_KEY_AGE_MS = 100 * 365 * 24 * 60 * 60 * 1e3;
|
|
2709
|
+
var asymmetricAlgSchema = import_zod2.z.enum(["RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]);
|
|
2710
|
+
var baseKeyDataSchema = import_zod2.z.object({
|
|
2711
|
+
kid: import_zod2.z.string().min(1),
|
|
2712
|
+
createdAt: import_zod2.z.number().positive().int(),
|
|
2713
|
+
version: import_zod2.z.number().positive().int()
|
|
2714
|
+
});
|
|
2715
|
+
var jsonWebKeySchema = import_zod2.z.object({
|
|
2716
|
+
kty: import_zod2.z.string().optional(),
|
|
2717
|
+
use: import_zod2.z.string().optional(),
|
|
2718
|
+
alg: import_zod2.z.string().optional(),
|
|
2719
|
+
kid: import_zod2.z.string().optional(),
|
|
2720
|
+
n: import_zod2.z.string().optional(),
|
|
2721
|
+
e: import_zod2.z.string().optional(),
|
|
2722
|
+
d: import_zod2.z.string().optional(),
|
|
2723
|
+
p: import_zod2.z.string().optional(),
|
|
2724
|
+
q: import_zod2.z.string().optional(),
|
|
2725
|
+
dp: import_zod2.z.string().optional(),
|
|
2726
|
+
dq: import_zod2.z.string().optional(),
|
|
2727
|
+
qi: import_zod2.z.string().optional(),
|
|
2728
|
+
x: import_zod2.z.string().optional(),
|
|
2729
|
+
y: import_zod2.z.string().optional(),
|
|
2730
|
+
crv: import_zod2.z.string().optional()
|
|
2731
|
+
}).passthrough();
|
|
2732
|
+
var secretKeyDataSchema = baseKeyDataSchema.extend({
|
|
2733
|
+
type: import_zod2.z.literal("secret"),
|
|
2734
|
+
secret: import_zod2.z.string().min(1),
|
|
2735
|
+
bytes: import_zod2.z.number().positive().int()
|
|
2736
|
+
});
|
|
2737
|
+
var asymmetricKeyDataSchema = baseKeyDataSchema.extend({
|
|
2738
|
+
type: import_zod2.z.literal("asymmetric"),
|
|
2739
|
+
alg: asymmetricAlgSchema,
|
|
2740
|
+
privateKey: jsonWebKeySchema,
|
|
2741
|
+
publicJwk: import_zod2.z.object({
|
|
2742
|
+
keys: import_zod2.z.array(jsonWebKeySchema).min(1)
|
|
2743
|
+
})
|
|
2744
|
+
});
|
|
2745
|
+
var anyKeyDataSchema = import_zod2.z.discriminatedUnion("type", [secretKeyDataSchema, asymmetricKeyDataSchema]);
|
|
2746
|
+
function validateKeyData(data) {
|
|
2747
|
+
const result = anyKeyDataSchema.safeParse(data);
|
|
2748
|
+
if (!result.success) {
|
|
2749
|
+
return {
|
|
2750
|
+
valid: false,
|
|
2751
|
+
error: result.error.issues[0]?.message ?? "Invalid key structure"
|
|
2752
|
+
};
|
|
2575
2753
|
}
|
|
2576
|
-
|
|
2577
|
-
|
|
2754
|
+
const parsed = result.data;
|
|
2755
|
+
const now = Date.now();
|
|
2756
|
+
if (parsed.createdAt > now + MAX_CLOCK_DRIFT_MS2) {
|
|
2757
|
+
return { valid: false, error: "createdAt is in the future" };
|
|
2578
2758
|
}
|
|
2579
|
-
if (
|
|
2580
|
-
|
|
2759
|
+
if (parsed.createdAt < now - MAX_KEY_AGE_MS) {
|
|
2760
|
+
return { valid: false, error: "createdAt is too old" };
|
|
2581
2761
|
}
|
|
2582
|
-
return
|
|
2762
|
+
return { valid: true, data: parsed };
|
|
2583
2763
|
}
|
|
2584
|
-
function
|
|
2585
|
-
|
|
2586
|
-
|
|
2764
|
+
function parseKeyData(data) {
|
|
2765
|
+
const validation = validateKeyData(data);
|
|
2766
|
+
if (!validation.valid) {
|
|
2767
|
+
return null;
|
|
2587
2768
|
}
|
|
2588
|
-
return
|
|
2769
|
+
return validation.data;
|
|
2589
2770
|
}
|
|
2590
|
-
function
|
|
2591
|
-
return
|
|
2771
|
+
function isSecretKeyData(data) {
|
|
2772
|
+
return data.type === "secret";
|
|
2592
2773
|
}
|
|
2593
|
-
function
|
|
2594
|
-
|
|
2595
|
-
if (typeof Buffer !== "undefined") {
|
|
2596
|
-
base64 = Buffer.from(data).toString("base64");
|
|
2597
|
-
} else {
|
|
2598
|
-
const binString = Array.from(data, (byte) => String.fromCodePoint(byte)).join("");
|
|
2599
|
-
base64 = btoa(binString);
|
|
2600
|
-
}
|
|
2601
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
2774
|
+
function isAsymmetricKeyData(data) {
|
|
2775
|
+
return data.type === "asymmetric";
|
|
2602
2776
|
}
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2777
|
+
|
|
2778
|
+
// libs/utils/src/crypto/key-persistence/key-persistence.ts
|
|
2779
|
+
var KeyPersistence = class {
|
|
2780
|
+
storage;
|
|
2781
|
+
cache = /* @__PURE__ */ new Map();
|
|
2782
|
+
enableCache;
|
|
2783
|
+
throwOnInvalid;
|
|
2784
|
+
constructor(options) {
|
|
2785
|
+
this.storage = options.storage;
|
|
2786
|
+
this.enableCache = options.enableCache ?? true;
|
|
2787
|
+
this.throwOnInvalid = options.throwOnInvalid ?? false;
|
|
2608
2788
|
}
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2789
|
+
/**
|
|
2790
|
+
* Get a key by ID.
|
|
2791
|
+
*
|
|
2792
|
+
* @param kid - Key identifier
|
|
2793
|
+
* @returns Key data or null if not found
|
|
2794
|
+
*/
|
|
2795
|
+
async get(kid) {
|
|
2796
|
+
if (this.enableCache) {
|
|
2797
|
+
const cached = this.cache.get(kid);
|
|
2798
|
+
if (cached) return cached;
|
|
2799
|
+
}
|
|
2800
|
+
const raw = await this.storage.get(kid);
|
|
2801
|
+
if (!raw) return null;
|
|
2802
|
+
let parsed;
|
|
2803
|
+
try {
|
|
2804
|
+
parsed = JSON.parse(raw);
|
|
2805
|
+
} catch {
|
|
2806
|
+
if (this.throwOnInvalid) {
|
|
2807
|
+
throw new Error(`Invalid JSON for key "${kid}"`);
|
|
2808
|
+
}
|
|
2809
|
+
return null;
|
|
2810
|
+
}
|
|
2811
|
+
const validation = validateKeyData(parsed);
|
|
2812
|
+
if (!validation.valid) {
|
|
2813
|
+
if (this.throwOnInvalid) {
|
|
2814
|
+
throw new Error(`Invalid key data for "${kid}": ${validation.error}`);
|
|
2815
|
+
}
|
|
2816
|
+
return null;
|
|
2817
|
+
}
|
|
2818
|
+
const key = validation.data;
|
|
2819
|
+
if (this.enableCache) {
|
|
2820
|
+
this.cache.set(kid, key);
|
|
2821
|
+
}
|
|
2822
|
+
return key;
|
|
2614
2823
|
}
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
function buildPrefix(name, id) {
|
|
2626
|
-
if (id) {
|
|
2627
|
-
return `${name}${NAMESPACE_SEPARATOR}${id}${NAMESPACE_SEPARATOR}`;
|
|
2824
|
+
/**
|
|
2825
|
+
* Get a secret key by ID.
|
|
2826
|
+
*
|
|
2827
|
+
* @param kid - Key identifier
|
|
2828
|
+
* @returns Secret key data or null if not found or wrong type
|
|
2829
|
+
*/
|
|
2830
|
+
async getSecret(kid) {
|
|
2831
|
+
const key = await this.get(kid);
|
|
2832
|
+
if (!key || !isSecretKeyData(key)) return null;
|
|
2833
|
+
return key;
|
|
2628
2834
|
}
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2835
|
+
/**
|
|
2836
|
+
* Get an asymmetric key by ID.
|
|
2837
|
+
*
|
|
2838
|
+
* @param kid - Key identifier
|
|
2839
|
+
* @returns Asymmetric key data or null if not found or wrong type
|
|
2840
|
+
*/
|
|
2841
|
+
async getAsymmetric(kid) {
|
|
2842
|
+
const key = await this.get(kid);
|
|
2843
|
+
if (!key || !isAsymmetricKeyData(key)) return null;
|
|
2844
|
+
return key;
|
|
2636
2845
|
}
|
|
2637
|
-
// ============================================
|
|
2638
|
-
// Key Prefixing Helpers
|
|
2639
|
-
// ============================================
|
|
2640
2846
|
/**
|
|
2641
|
-
*
|
|
2847
|
+
* Store a key.
|
|
2848
|
+
*
|
|
2849
|
+
* @param key - Key data to store
|
|
2642
2850
|
*/
|
|
2643
|
-
|
|
2644
|
-
|
|
2851
|
+
async set(key) {
|
|
2852
|
+
const validation = validateKeyData(key);
|
|
2853
|
+
if (!validation.valid) {
|
|
2854
|
+
throw new Error(`Invalid key data: ${validation.error}`);
|
|
2855
|
+
}
|
|
2856
|
+
await this.storage.set(key.kid, JSON.stringify(key, null, 2));
|
|
2857
|
+
if (this.enableCache) {
|
|
2858
|
+
this.cache.set(key.kid, key);
|
|
2859
|
+
}
|
|
2645
2860
|
}
|
|
2646
2861
|
/**
|
|
2647
|
-
*
|
|
2862
|
+
* Delete a key.
|
|
2863
|
+
*
|
|
2864
|
+
* @param kid - Key identifier
|
|
2865
|
+
* @returns true if key existed and was deleted
|
|
2648
2866
|
*/
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2867
|
+
async delete(kid) {
|
|
2868
|
+
this.cache.delete(kid);
|
|
2869
|
+
return this.storage.delete(kid);
|
|
2870
|
+
}
|
|
2871
|
+
/**
|
|
2872
|
+
* Check if a key exists.
|
|
2873
|
+
*
|
|
2874
|
+
* @param kid - Key identifier
|
|
2875
|
+
* @returns true if key exists
|
|
2876
|
+
*/
|
|
2877
|
+
async has(kid) {
|
|
2878
|
+
if (this.enableCache && this.cache.has(kid)) {
|
|
2879
|
+
return true;
|
|
2652
2880
|
}
|
|
2653
|
-
return
|
|
2881
|
+
return this.storage.exists(kid);
|
|
2654
2882
|
}
|
|
2655
2883
|
/**
|
|
2656
|
-
*
|
|
2884
|
+
* List all key IDs.
|
|
2885
|
+
*
|
|
2886
|
+
* @returns Array of key identifiers
|
|
2657
2887
|
*/
|
|
2658
|
-
|
|
2659
|
-
return this.
|
|
2888
|
+
async list() {
|
|
2889
|
+
return this.storage.keys();
|
|
2660
2890
|
}
|
|
2661
2891
|
/**
|
|
2662
|
-
*
|
|
2892
|
+
* Get or create a secret key.
|
|
2893
|
+
*
|
|
2894
|
+
* If a key with the given ID exists and is a valid secret key,
|
|
2895
|
+
* it is returned. Otherwise, a new secret key is generated.
|
|
2896
|
+
*
|
|
2897
|
+
* @param kid - Key identifier
|
|
2898
|
+
* @param options - Options for key creation
|
|
2899
|
+
* @returns Secret key data
|
|
2900
|
+
*
|
|
2901
|
+
* @example
|
|
2902
|
+
* ```typescript
|
|
2903
|
+
* const secret = await keys.getOrCreateSecret('encryption-key');
|
|
2904
|
+
* const bytes = base64urlDecode(secret.secret);
|
|
2905
|
+
* ```
|
|
2663
2906
|
*/
|
|
2664
|
-
|
|
2665
|
-
|
|
2907
|
+
async getOrCreateSecret(kid, options) {
|
|
2908
|
+
const existing = await this.getSecret(kid);
|
|
2909
|
+
if (existing) {
|
|
2910
|
+
return existing;
|
|
2911
|
+
}
|
|
2912
|
+
const bytes = options?.bytes ?? 32;
|
|
2913
|
+
const secret = {
|
|
2914
|
+
type: "secret",
|
|
2915
|
+
kid,
|
|
2916
|
+
secret: base64urlEncode(randomBytes(bytes)),
|
|
2917
|
+
bytes,
|
|
2918
|
+
createdAt: Date.now(),
|
|
2919
|
+
version: 1
|
|
2920
|
+
};
|
|
2921
|
+
await this.set(secret);
|
|
2922
|
+
return secret;
|
|
2666
2923
|
}
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2924
|
+
/**
|
|
2925
|
+
* Clear the in-memory cache.
|
|
2926
|
+
*
|
|
2927
|
+
* Useful when you want to force a reload from storage.
|
|
2928
|
+
*/
|
|
2929
|
+
clearCache() {
|
|
2930
|
+
this.cache.clear();
|
|
2931
|
+
}
|
|
2932
|
+
/**
|
|
2933
|
+
* Clear cache for a specific key.
|
|
2934
|
+
*
|
|
2935
|
+
* @param kid - Key identifier
|
|
2936
|
+
*/
|
|
2937
|
+
clearCacheFor(kid) {
|
|
2938
|
+
this.cache.delete(kid);
|
|
2939
|
+
}
|
|
2940
|
+
/**
|
|
2941
|
+
* Check if a key is in the cache.
|
|
2942
|
+
*
|
|
2943
|
+
* @param kid - Key identifier
|
|
2944
|
+
* @returns true if key is cached
|
|
2945
|
+
*/
|
|
2946
|
+
isCached(kid) {
|
|
2947
|
+
return this.cache.has(kid);
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* Get the underlying storage adapter.
|
|
2951
|
+
*
|
|
2952
|
+
* Use with caution - direct storage access bypasses validation.
|
|
2953
|
+
*/
|
|
2954
|
+
getAdapter() {
|
|
2955
|
+
return this.storage;
|
|
2956
|
+
}
|
|
2957
|
+
};
|
|
2958
|
+
|
|
2959
|
+
// libs/utils/src/storage/adapters/memory.ts
|
|
2960
|
+
var import_events = require("events");
|
|
2961
|
+
init_base();
|
|
2962
|
+
init_errors();
|
|
2963
|
+
init_pattern();
|
|
2964
|
+
init_ttl();
|
|
2965
|
+
var DEFAULT_SWEEP_INTERVAL_SECONDS = 60;
|
|
2966
|
+
var MAX_TIMEOUT_MS = 2147483647;
|
|
2967
|
+
var MemoryStorageAdapter = class extends BaseStorageAdapter {
|
|
2968
|
+
backendName = "memory";
|
|
2969
|
+
store = /* @__PURE__ */ new Map();
|
|
2970
|
+
emitter = new import_events.EventEmitter();
|
|
2971
|
+
sweepInterval;
|
|
2972
|
+
options;
|
|
2973
|
+
// LRU tracking (simple linked list via insertion order in Map)
|
|
2974
|
+
accessOrder = [];
|
|
2975
|
+
constructor(options = {}) {
|
|
2976
|
+
super();
|
|
2977
|
+
this.options = {
|
|
2978
|
+
enableSweeper: options.enableSweeper ?? true,
|
|
2979
|
+
sweepIntervalSeconds: options.sweepIntervalSeconds ?? DEFAULT_SWEEP_INTERVAL_SECONDS,
|
|
2980
|
+
maxEntries: options.maxEntries ?? 0
|
|
2981
|
+
// 0 = unlimited
|
|
2982
|
+
};
|
|
2983
|
+
this.emitter.setMaxListeners(1e3);
|
|
2673
2984
|
}
|
|
2674
2985
|
// ============================================
|
|
2675
|
-
// Connection Lifecycle
|
|
2986
|
+
// Connection Lifecycle
|
|
2676
2987
|
// ============================================
|
|
2677
2988
|
async connect() {
|
|
2678
|
-
|
|
2989
|
+
if (this.connected) return;
|
|
2990
|
+
this.connected = true;
|
|
2991
|
+
if (this.options.enableSweeper && this.options.sweepIntervalSeconds > 0) {
|
|
2992
|
+
this.startSweeper();
|
|
2993
|
+
}
|
|
2679
2994
|
}
|
|
2680
2995
|
async disconnect() {
|
|
2681
|
-
|
|
2996
|
+
if (!this.connected) return;
|
|
2997
|
+
this.stopSweeper();
|
|
2998
|
+
this.clearAllTimeouts();
|
|
2999
|
+
this.store.clear();
|
|
3000
|
+
this.accessOrder = [];
|
|
3001
|
+
this.emitter.removeAllListeners();
|
|
3002
|
+
this.connected = false;
|
|
2682
3003
|
}
|
|
2683
3004
|
async ping() {
|
|
2684
|
-
return this.
|
|
3005
|
+
return this.connected;
|
|
2685
3006
|
}
|
|
2686
3007
|
// ============================================
|
|
2687
|
-
// Core Operations
|
|
2688
|
-
// ============================================
|
|
2689
|
-
async get(key) {
|
|
2690
|
-
return this.adapter.get(this.prefixKey(key));
|
|
2691
|
-
}
|
|
2692
|
-
async set(key, value, options) {
|
|
2693
|
-
return this.adapter.set(this.prefixKey(key), value, options);
|
|
2694
|
-
}
|
|
2695
|
-
async delete(key) {
|
|
2696
|
-
return this.adapter.delete(this.prefixKey(key));
|
|
2697
|
-
}
|
|
2698
|
-
async exists(key) {
|
|
2699
|
-
return this.adapter.exists(this.prefixKey(key));
|
|
2700
|
-
}
|
|
2701
|
-
// ============================================
|
|
2702
|
-
// Batch Operations (with prefixing)
|
|
2703
|
-
// ============================================
|
|
2704
|
-
async mget(keys) {
|
|
2705
|
-
const prefixedKeys = keys.map((k) => this.prefixKey(k));
|
|
2706
|
-
return this.adapter.mget(prefixedKeys);
|
|
2707
|
-
}
|
|
2708
|
-
async mset(entries) {
|
|
2709
|
-
const prefixedEntries = entries.map((e) => ({
|
|
2710
|
-
...e,
|
|
2711
|
-
key: this.prefixKey(e.key)
|
|
2712
|
-
}));
|
|
2713
|
-
return this.adapter.mset(prefixedEntries);
|
|
2714
|
-
}
|
|
2715
|
-
async mdelete(keys) {
|
|
2716
|
-
const prefixedKeys = keys.map((k) => this.prefixKey(k));
|
|
2717
|
-
return this.adapter.mdelete(prefixedKeys);
|
|
2718
|
-
}
|
|
2719
|
-
// ============================================
|
|
2720
|
-
// TTL Operations (with prefixing)
|
|
2721
|
-
// ============================================
|
|
2722
|
-
async expire(key, ttlSeconds) {
|
|
2723
|
-
return this.adapter.expire(this.prefixKey(key), ttlSeconds);
|
|
2724
|
-
}
|
|
2725
|
-
async ttl(key) {
|
|
2726
|
-
return this.adapter.ttl(this.prefixKey(key));
|
|
2727
|
-
}
|
|
2728
|
-
// ============================================
|
|
2729
|
-
// Key Enumeration (with prefixing)
|
|
2730
|
-
// ============================================
|
|
2731
|
-
async keys(pattern = "*") {
|
|
2732
|
-
const prefixedPattern = this.prefixPattern(pattern);
|
|
2733
|
-
const keys = await this.adapter.keys(prefixedPattern);
|
|
2734
|
-
return keys.map((k) => this.unprefixKey(k));
|
|
2735
|
-
}
|
|
2736
|
-
async count(pattern = "*") {
|
|
2737
|
-
const prefixedPattern = this.prefixPattern(pattern);
|
|
2738
|
-
return this.adapter.count(prefixedPattern);
|
|
2739
|
-
}
|
|
2740
|
-
// ============================================
|
|
2741
|
-
// Atomic Operations (with prefixing)
|
|
2742
|
-
// ============================================
|
|
2743
|
-
async incr(key) {
|
|
2744
|
-
return this.adapter.incr(this.prefixKey(key));
|
|
2745
|
-
}
|
|
2746
|
-
async decr(key) {
|
|
2747
|
-
return this.adapter.decr(this.prefixKey(key));
|
|
2748
|
-
}
|
|
2749
|
-
async incrBy(key, amount) {
|
|
2750
|
-
return this.adapter.incrBy(this.prefixKey(key), amount);
|
|
2751
|
-
}
|
|
2752
|
-
// ============================================
|
|
2753
|
-
// Pub/Sub (with channel prefixing)
|
|
2754
|
-
// ============================================
|
|
2755
|
-
async publish(channel, message) {
|
|
2756
|
-
return this.adapter.publish(this.prefixChannel(channel), message);
|
|
2757
|
-
}
|
|
2758
|
-
async subscribe(channel, handler) {
|
|
2759
|
-
const prefixedChannel = this.prefixChannel(channel);
|
|
2760
|
-
const wrappedHandler = (message, ch) => {
|
|
2761
|
-
const unprefixedChannel = ch.startsWith(this.prefix) ? ch.slice(this.prefix.length) : ch;
|
|
2762
|
-
handler(message, unprefixedChannel);
|
|
2763
|
-
};
|
|
2764
|
-
return this.adapter.subscribe(prefixedChannel, wrappedHandler);
|
|
2765
|
-
}
|
|
2766
|
-
supportsPubSub() {
|
|
2767
|
-
return this.adapter.supportsPubSub();
|
|
2768
|
-
}
|
|
2769
|
-
};
|
|
2770
|
-
function createRootStorage(adapter) {
|
|
2771
|
-
return new NamespacedStorageImpl(adapter, "", adapter);
|
|
2772
|
-
}
|
|
2773
|
-
function createNamespacedStorage(adapter, prefix) {
|
|
2774
|
-
const normalizedPrefix = prefix && !prefix.endsWith(NAMESPACE_SEPARATOR) ? prefix + NAMESPACE_SEPARATOR : prefix;
|
|
2775
|
-
return new NamespacedStorageImpl(adapter, normalizedPrefix, adapter);
|
|
2776
|
-
}
|
|
2777
|
-
|
|
2778
|
-
// libs/utils/src/storage/adapters/memory.ts
|
|
2779
|
-
var import_events = require("events");
|
|
2780
|
-
init_base();
|
|
2781
|
-
init_errors();
|
|
2782
|
-
|
|
2783
|
-
// libs/utils/src/storage/utils/pattern.ts
|
|
2784
|
-
init_errors();
|
|
2785
|
-
var MAX_PATTERN_LENGTH = 500;
|
|
2786
|
-
var MAX_WILDCARDS = 20;
|
|
2787
|
-
var REGEX_SPECIAL_CHARS = /[.+^${}()|[\]\\]/g;
|
|
2788
|
-
function globToRegex(pattern) {
|
|
2789
|
-
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
2790
|
-
throw new StoragePatternError(
|
|
2791
|
-
pattern.substring(0, 50) + "...",
|
|
2792
|
-
`Pattern exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`
|
|
2793
|
-
);
|
|
2794
|
-
}
|
|
2795
|
-
const wildcardCount = (pattern.match(/[*?]/g) || []).length;
|
|
2796
|
-
if (wildcardCount > MAX_WILDCARDS) {
|
|
2797
|
-
throw new StoragePatternError(pattern, `Pattern has too many wildcards (max: ${MAX_WILDCARDS})`);
|
|
2798
|
-
}
|
|
2799
|
-
if (pattern === "" || pattern === "*") {
|
|
2800
|
-
return /^.*$/;
|
|
2801
|
-
}
|
|
2802
|
-
let regexStr = "^";
|
|
2803
|
-
let prevChar = "";
|
|
2804
|
-
for (let i = 0; i < pattern.length; i++) {
|
|
2805
|
-
const char = pattern[i];
|
|
2806
|
-
switch (char) {
|
|
2807
|
-
case "*":
|
|
2808
|
-
if (prevChar !== "*") {
|
|
2809
|
-
regexStr += ".*";
|
|
2810
|
-
}
|
|
2811
|
-
break;
|
|
2812
|
-
case "?":
|
|
2813
|
-
regexStr += ".";
|
|
2814
|
-
break;
|
|
2815
|
-
default:
|
|
2816
|
-
regexStr += char.replace(REGEX_SPECIAL_CHARS, "\\$&");
|
|
2817
|
-
}
|
|
2818
|
-
prevChar = char;
|
|
2819
|
-
}
|
|
2820
|
-
regexStr += "$";
|
|
2821
|
-
try {
|
|
2822
|
-
return new RegExp(regexStr);
|
|
2823
|
-
} catch {
|
|
2824
|
-
throw new StoragePatternError(pattern, "Failed to compile pattern to regex");
|
|
2825
|
-
}
|
|
2826
|
-
}
|
|
2827
|
-
function matchesPattern(key, pattern) {
|
|
2828
|
-
const regex = globToRegex(pattern);
|
|
2829
|
-
return regex.test(key);
|
|
2830
|
-
}
|
|
2831
|
-
function validatePattern(pattern) {
|
|
2832
|
-
try {
|
|
2833
|
-
globToRegex(pattern);
|
|
2834
|
-
return { valid: true };
|
|
2835
|
-
} catch (e) {
|
|
2836
|
-
return {
|
|
2837
|
-
valid: false,
|
|
2838
|
-
error: e instanceof Error ? e.message : "Invalid pattern"
|
|
2839
|
-
};
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
2842
|
-
function escapeGlob(literal) {
|
|
2843
|
-
return literal.replace(/[*?\\]/g, "\\$&");
|
|
2844
|
-
}
|
|
2845
|
-
|
|
2846
|
-
// libs/utils/src/storage/adapters/memory.ts
|
|
2847
|
-
init_ttl();
|
|
2848
|
-
var DEFAULT_SWEEP_INTERVAL_SECONDS = 60;
|
|
2849
|
-
var MAX_TIMEOUT_MS = 2147483647;
|
|
2850
|
-
var MemoryStorageAdapter = class extends BaseStorageAdapter {
|
|
2851
|
-
backendName = "memory";
|
|
2852
|
-
store = /* @__PURE__ */ new Map();
|
|
2853
|
-
emitter = new import_events.EventEmitter();
|
|
2854
|
-
sweepInterval;
|
|
2855
|
-
options;
|
|
2856
|
-
// LRU tracking (simple linked list via insertion order in Map)
|
|
2857
|
-
accessOrder = [];
|
|
2858
|
-
constructor(options = {}) {
|
|
2859
|
-
super();
|
|
2860
|
-
this.options = {
|
|
2861
|
-
enableSweeper: options.enableSweeper ?? true,
|
|
2862
|
-
sweepIntervalSeconds: options.sweepIntervalSeconds ?? DEFAULT_SWEEP_INTERVAL_SECONDS,
|
|
2863
|
-
maxEntries: options.maxEntries ?? 0
|
|
2864
|
-
// 0 = unlimited
|
|
2865
|
-
};
|
|
2866
|
-
this.emitter.setMaxListeners(1e3);
|
|
2867
|
-
}
|
|
2868
|
-
// ============================================
|
|
2869
|
-
// Connection Lifecycle
|
|
2870
|
-
// ============================================
|
|
2871
|
-
async connect() {
|
|
2872
|
-
if (this.connected) return;
|
|
2873
|
-
this.connected = true;
|
|
2874
|
-
if (this.options.enableSweeper && this.options.sweepIntervalSeconds > 0) {
|
|
2875
|
-
this.startSweeper();
|
|
2876
|
-
}
|
|
2877
|
-
}
|
|
2878
|
-
async disconnect() {
|
|
2879
|
-
if (!this.connected) return;
|
|
2880
|
-
this.stopSweeper();
|
|
2881
|
-
this.clearAllTimeouts();
|
|
2882
|
-
this.store.clear();
|
|
2883
|
-
this.accessOrder = [];
|
|
2884
|
-
this.emitter.removeAllListeners();
|
|
2885
|
-
this.connected = false;
|
|
2886
|
-
}
|
|
2887
|
-
async ping() {
|
|
2888
|
-
return this.connected;
|
|
2889
|
-
}
|
|
2890
|
-
// ============================================
|
|
2891
|
-
// Core Operations
|
|
3008
|
+
// Core Operations
|
|
2892
3009
|
// ============================================
|
|
2893
3010
|
async get(key) {
|
|
2894
3011
|
this.ensureConnected();
|
|
@@ -3168,42 +3285,624 @@ var MemoryStorageAdapter = class extends BaseStorageAdapter {
|
|
|
3168
3285
|
}
|
|
3169
3286
|
};
|
|
3170
3287
|
|
|
3171
|
-
// libs/utils/src/storage/
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3288
|
+
// libs/utils/src/storage/adapters/filesystem.ts
|
|
3289
|
+
init_base();
|
|
3290
|
+
init_errors();
|
|
3291
|
+
init_pattern();
|
|
3292
|
+
var FileSystemStorageAdapter = class extends BaseStorageAdapter {
|
|
3293
|
+
backendName = "filesystem";
|
|
3294
|
+
baseDir;
|
|
3295
|
+
extension;
|
|
3296
|
+
dirMode;
|
|
3297
|
+
fileMode;
|
|
3298
|
+
constructor(options) {
|
|
3299
|
+
super();
|
|
3300
|
+
this.baseDir = options.baseDir;
|
|
3301
|
+
this.extension = options.extension ?? ".json";
|
|
3302
|
+
this.dirMode = options.dirMode ?? 448;
|
|
3303
|
+
this.fileMode = options.fileMode ?? 384;
|
|
3178
3304
|
}
|
|
3179
|
-
|
|
3180
|
-
|
|
3305
|
+
// ============================================
|
|
3306
|
+
// Connection Lifecycle
|
|
3307
|
+
// ============================================
|
|
3308
|
+
async connect() {
|
|
3309
|
+
if (this.connected) return;
|
|
3310
|
+
await ensureDir(this.baseDir);
|
|
3311
|
+
try {
|
|
3312
|
+
await mkdir(this.baseDir, { recursive: true, mode: this.dirMode });
|
|
3313
|
+
} catch {
|
|
3314
|
+
}
|
|
3315
|
+
this.connected = true;
|
|
3181
3316
|
}
|
|
3182
|
-
|
|
3183
|
-
return
|
|
3317
|
+
async disconnect() {
|
|
3318
|
+
if (!this.connected) return;
|
|
3319
|
+
this.connected = false;
|
|
3184
3320
|
}
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
return
|
|
3321
|
+
async ping() {
|
|
3322
|
+
if (!this.connected) return false;
|
|
3323
|
+
try {
|
|
3324
|
+
return await fileExists(this.baseDir);
|
|
3325
|
+
} catch {
|
|
3326
|
+
return false;
|
|
3191
3327
|
}
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3328
|
+
}
|
|
3329
|
+
// ============================================
|
|
3330
|
+
// Core Operations
|
|
3331
|
+
// ============================================
|
|
3332
|
+
async get(key) {
|
|
3333
|
+
this.ensureConnected();
|
|
3334
|
+
const filePath = this.keyToPath(key);
|
|
3335
|
+
try {
|
|
3336
|
+
const entry = await readJSON(filePath);
|
|
3337
|
+
if (!entry) return null;
|
|
3338
|
+
if (entry.expiresAt && Date.now() >= entry.expiresAt) {
|
|
3339
|
+
await this.deleteFile(filePath);
|
|
3340
|
+
return null;
|
|
3341
|
+
}
|
|
3342
|
+
return entry.value;
|
|
3343
|
+
} catch (e) {
|
|
3344
|
+
const error = e;
|
|
3345
|
+
if (error.code === "ENOENT") {
|
|
3346
|
+
return null;
|
|
3347
|
+
}
|
|
3348
|
+
throw e;
|
|
3195
3349
|
}
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3350
|
+
}
|
|
3351
|
+
async doSet(key, value, options) {
|
|
3352
|
+
const filePath = this.keyToPath(key);
|
|
3353
|
+
const existingEntry = await this.readEntry(filePath);
|
|
3354
|
+
const exists = existingEntry !== null && !this.isExpired(existingEntry);
|
|
3355
|
+
if (options?.ifNotExists && exists) {
|
|
3356
|
+
return;
|
|
3199
3357
|
}
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
return new UpstashStorageAdapter2(config.upstash);
|
|
3358
|
+
if (options?.ifExists && !exists) {
|
|
3359
|
+
return;
|
|
3203
3360
|
}
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3361
|
+
const entry = { value };
|
|
3362
|
+
if (options?.ttlSeconds) {
|
|
3363
|
+
entry.expiresAt = Date.now() + options.ttlSeconds * 1e3;
|
|
3364
|
+
}
|
|
3365
|
+
await this.atomicWrite(filePath, entry);
|
|
3366
|
+
}
|
|
3367
|
+
async delete(key) {
|
|
3368
|
+
this.ensureConnected();
|
|
3369
|
+
const filePath = this.keyToPath(key);
|
|
3370
|
+
return this.deleteFile(filePath);
|
|
3371
|
+
}
|
|
3372
|
+
async exists(key) {
|
|
3373
|
+
this.ensureConnected();
|
|
3374
|
+
const filePath = this.keyToPath(key);
|
|
3375
|
+
try {
|
|
3376
|
+
const entry = await this.readEntry(filePath);
|
|
3377
|
+
if (!entry) return false;
|
|
3378
|
+
if (this.isExpired(entry)) {
|
|
3379
|
+
await this.deleteFile(filePath);
|
|
3380
|
+
return false;
|
|
3381
|
+
}
|
|
3382
|
+
return true;
|
|
3383
|
+
} catch {
|
|
3384
|
+
return false;
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
// ============================================
|
|
3388
|
+
// TTL Operations
|
|
3389
|
+
// ============================================
|
|
3390
|
+
async expire(key, ttlSeconds) {
|
|
3391
|
+
this.ensureConnected();
|
|
3392
|
+
const filePath = this.keyToPath(key);
|
|
3393
|
+
const entry = await this.readEntry(filePath);
|
|
3394
|
+
if (!entry || this.isExpired(entry)) {
|
|
3395
|
+
return false;
|
|
3396
|
+
}
|
|
3397
|
+
entry.expiresAt = Date.now() + ttlSeconds * 1e3;
|
|
3398
|
+
await this.atomicWrite(filePath, entry);
|
|
3399
|
+
return true;
|
|
3400
|
+
}
|
|
3401
|
+
async ttl(key) {
|
|
3402
|
+
this.ensureConnected();
|
|
3403
|
+
const filePath = this.keyToPath(key);
|
|
3404
|
+
const entry = await this.readEntry(filePath);
|
|
3405
|
+
if (!entry) return null;
|
|
3406
|
+
if (this.isExpired(entry)) {
|
|
3407
|
+
await this.deleteFile(filePath);
|
|
3408
|
+
return null;
|
|
3409
|
+
}
|
|
3410
|
+
if (entry.expiresAt === void 0) {
|
|
3411
|
+
return -1;
|
|
3412
|
+
}
|
|
3413
|
+
return Math.max(0, Math.ceil((entry.expiresAt - Date.now()) / 1e3));
|
|
3414
|
+
}
|
|
3415
|
+
// ============================================
|
|
3416
|
+
// Key Enumeration
|
|
3417
|
+
// ============================================
|
|
3418
|
+
async keys(pattern = "*") {
|
|
3419
|
+
this.ensureConnected();
|
|
3420
|
+
const regex = globToRegex(pattern);
|
|
3421
|
+
const result = [];
|
|
3422
|
+
try {
|
|
3423
|
+
const files = await readdir(this.baseDir);
|
|
3424
|
+
for (const file of files) {
|
|
3425
|
+
if (!file.endsWith(this.extension)) continue;
|
|
3426
|
+
const key = this.pathToKey(file);
|
|
3427
|
+
if (!regex.test(key)) continue;
|
|
3428
|
+
const filePath = this.keyToPath(key);
|
|
3429
|
+
const entry = await this.readEntry(filePath);
|
|
3430
|
+
if (entry && !this.isExpired(entry)) {
|
|
3431
|
+
result.push(key);
|
|
3432
|
+
} else if (entry && this.isExpired(entry)) {
|
|
3433
|
+
await this.deleteFile(filePath);
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
} catch (e) {
|
|
3437
|
+
const error = e;
|
|
3438
|
+
if (error.code === "ENOENT") {
|
|
3439
|
+
return [];
|
|
3440
|
+
}
|
|
3441
|
+
throw e;
|
|
3442
|
+
}
|
|
3443
|
+
return result;
|
|
3444
|
+
}
|
|
3445
|
+
// ============================================
|
|
3446
|
+
// Atomic Operations
|
|
3447
|
+
// ============================================
|
|
3448
|
+
async incr(key) {
|
|
3449
|
+
return this.incrBy(key, 1);
|
|
3450
|
+
}
|
|
3451
|
+
async decr(key) {
|
|
3452
|
+
return this.incrBy(key, -1);
|
|
3453
|
+
}
|
|
3454
|
+
async incrBy(key, amount) {
|
|
3455
|
+
this.ensureConnected();
|
|
3456
|
+
const filePath = this.keyToPath(key);
|
|
3457
|
+
const entry = await this.readEntry(filePath);
|
|
3458
|
+
let currentValue = 0;
|
|
3459
|
+
if (entry && !this.isExpired(entry)) {
|
|
3460
|
+
const parsed = parseInt(entry.value, 10);
|
|
3461
|
+
if (isNaN(parsed)) {
|
|
3462
|
+
throw new StorageOperationError("incrBy", key, "Value is not an integer");
|
|
3463
|
+
}
|
|
3464
|
+
currentValue = parsed;
|
|
3465
|
+
}
|
|
3466
|
+
const newValue = currentValue + amount;
|
|
3467
|
+
const newEntry = { value: String(newValue) };
|
|
3468
|
+
if (entry?.expiresAt && !this.isExpired(entry)) {
|
|
3469
|
+
newEntry.expiresAt = entry.expiresAt;
|
|
3470
|
+
}
|
|
3471
|
+
await this.atomicWrite(filePath, newEntry);
|
|
3472
|
+
return newValue;
|
|
3473
|
+
}
|
|
3474
|
+
// ============================================
|
|
3475
|
+
// Internal Helpers
|
|
3476
|
+
// ============================================
|
|
3477
|
+
/**
|
|
3478
|
+
* Convert a storage key to a file path.
|
|
3479
|
+
* Sanitizes the key to be filesystem-safe.
|
|
3480
|
+
*/
|
|
3481
|
+
keyToPath(key) {
|
|
3482
|
+
const safeKey = this.encodeKey(key);
|
|
3483
|
+
return `${this.baseDir}/${safeKey}${this.extension}`;
|
|
3484
|
+
}
|
|
3485
|
+
/**
|
|
3486
|
+
* Convert a filename back to a storage key.
|
|
3487
|
+
*/
|
|
3488
|
+
pathToKey(filename) {
|
|
3489
|
+
const safeKey = filename.slice(0, -this.extension.length);
|
|
3490
|
+
return this.decodeKey(safeKey);
|
|
3491
|
+
}
|
|
3492
|
+
/**
|
|
3493
|
+
* Encode a key to be filesystem-safe.
|
|
3494
|
+
* Uses URL encoding to handle special characters.
|
|
3495
|
+
*/
|
|
3496
|
+
encodeKey(key) {
|
|
3497
|
+
return encodeURIComponent(key).replace(/\./g, "%2E");
|
|
3498
|
+
}
|
|
3499
|
+
/**
|
|
3500
|
+
* Decode a filesystem-safe key back to original.
|
|
3501
|
+
*/
|
|
3502
|
+
decodeKey(encoded) {
|
|
3503
|
+
return decodeURIComponent(encoded);
|
|
3504
|
+
}
|
|
3505
|
+
/**
|
|
3506
|
+
* Read an entry from a file.
|
|
3507
|
+
*/
|
|
3508
|
+
async readEntry(filePath) {
|
|
3509
|
+
try {
|
|
3510
|
+
return await readJSON(filePath);
|
|
3511
|
+
} catch {
|
|
3512
|
+
return null;
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3515
|
+
/**
|
|
3516
|
+
* Check if an entry is expired.
|
|
3517
|
+
*/
|
|
3518
|
+
isExpired(entry) {
|
|
3519
|
+
return entry.expiresAt !== void 0 && Date.now() >= entry.expiresAt;
|
|
3520
|
+
}
|
|
3521
|
+
/**
|
|
3522
|
+
* Delete a file, returning true if it existed.
|
|
3523
|
+
*/
|
|
3524
|
+
async deleteFile(filePath) {
|
|
3525
|
+
try {
|
|
3526
|
+
await unlink(filePath);
|
|
3527
|
+
return true;
|
|
3528
|
+
} catch (e) {
|
|
3529
|
+
const error = e;
|
|
3530
|
+
if (error.code === "ENOENT") {
|
|
3531
|
+
return false;
|
|
3532
|
+
}
|
|
3533
|
+
throw e;
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
/**
|
|
3537
|
+
* Atomic write: write to temp file then rename.
|
|
3538
|
+
* This ensures we don't corrupt the file if write is interrupted.
|
|
3539
|
+
*/
|
|
3540
|
+
async atomicWrite(filePath, entry) {
|
|
3541
|
+
const tempSuffix = bytesToHex(randomBytes(8));
|
|
3542
|
+
const tempPath = `${filePath}.${tempSuffix}.tmp`;
|
|
3543
|
+
try {
|
|
3544
|
+
const content = JSON.stringify(entry, null, 2);
|
|
3545
|
+
await writeFile(tempPath, content, { mode: this.fileMode });
|
|
3546
|
+
await rename(tempPath, filePath);
|
|
3547
|
+
} catch (e) {
|
|
3548
|
+
try {
|
|
3549
|
+
await unlink(tempPath);
|
|
3550
|
+
} catch {
|
|
3551
|
+
}
|
|
3552
|
+
throw e;
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
/**
|
|
3556
|
+
* Get suggestion message for pub/sub not supported error.
|
|
3557
|
+
*/
|
|
3558
|
+
getPubSubSuggestion() {
|
|
3559
|
+
return "FileSystem adapter does not support pub/sub. Use Redis or Memory adapter for pub/sub support.";
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
|
|
3563
|
+
// libs/utils/src/crypto/key-persistence/factory.ts
|
|
3564
|
+
var DEFAULT_BASE_DIR = ".frontmcp/keys";
|
|
3565
|
+
async function createKeyPersistence(options) {
|
|
3566
|
+
const type = options?.type ?? "auto";
|
|
3567
|
+
const baseDir = options?.baseDir ?? DEFAULT_BASE_DIR;
|
|
3568
|
+
let adapter;
|
|
3569
|
+
if (type === "memory") {
|
|
3570
|
+
adapter = new MemoryStorageAdapter();
|
|
3571
|
+
} else if (type === "filesystem") {
|
|
3572
|
+
adapter = new FileSystemStorageAdapter({ baseDir });
|
|
3573
|
+
} else {
|
|
3574
|
+
if (isNode()) {
|
|
3575
|
+
adapter = new FileSystemStorageAdapter({ baseDir });
|
|
3576
|
+
} else {
|
|
3577
|
+
adapter = new MemoryStorageAdapter();
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
await adapter.connect();
|
|
3581
|
+
return new KeyPersistence({
|
|
3582
|
+
storage: adapter,
|
|
3583
|
+
throwOnInvalid: options?.throwOnInvalid ?? false,
|
|
3584
|
+
enableCache: options?.enableCache ?? true
|
|
3585
|
+
});
|
|
3586
|
+
}
|
|
3587
|
+
function createKeyPersistenceWithStorage(storage, options) {
|
|
3588
|
+
return new KeyPersistence({
|
|
3589
|
+
storage,
|
|
3590
|
+
throwOnInvalid: options?.throwOnInvalid ?? false,
|
|
3591
|
+
enableCache: options?.enableCache ?? true
|
|
3592
|
+
});
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
// libs/utils/src/crypto/index.ts
|
|
3596
|
+
var _provider = null;
|
|
3597
|
+
function getCrypto() {
|
|
3598
|
+
if (!_provider) {
|
|
3599
|
+
if (isNode()) {
|
|
3600
|
+
_provider = (init_node(), __toCommonJS(node_exports)).nodeCrypto;
|
|
3601
|
+
} else {
|
|
3602
|
+
_provider = (init_browser(), __toCommonJS(browser_exports)).browserCrypto;
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
return _provider;
|
|
3606
|
+
}
|
|
3607
|
+
function rsaVerify2(jwtAlg, data, publicJwk, signature) {
|
|
3608
|
+
assertNode("rsaVerify");
|
|
3609
|
+
return (init_node(), __toCommonJS(node_exports)).rsaVerify(jwtAlg, data, publicJwk, signature);
|
|
3610
|
+
}
|
|
3611
|
+
function randomUUID() {
|
|
3612
|
+
return getCrypto().randomUUID();
|
|
3613
|
+
}
|
|
3614
|
+
function randomBytes(length) {
|
|
3615
|
+
if (!Number.isInteger(length) || length <= 0) {
|
|
3616
|
+
throw new Error(`randomBytes length must be a positive integer, got ${length}`);
|
|
3617
|
+
}
|
|
3618
|
+
return getCrypto().randomBytes(length);
|
|
3619
|
+
}
|
|
3620
|
+
function sha256(data) {
|
|
3621
|
+
return getCrypto().sha256(data);
|
|
3622
|
+
}
|
|
3623
|
+
function sha256Hex(data) {
|
|
3624
|
+
return getCrypto().sha256Hex(data);
|
|
3625
|
+
}
|
|
3626
|
+
function hmacSha256(key, data) {
|
|
3627
|
+
return getCrypto().hmacSha256(key, data);
|
|
3628
|
+
}
|
|
3629
|
+
var HKDF_SHA256_MAX_LENGTH = 255 * 32;
|
|
3630
|
+
function hkdfSha256(ikm, salt, info, length) {
|
|
3631
|
+
if (!Number.isInteger(length) || length <= 0) {
|
|
3632
|
+
throw new Error(`HKDF length must be a positive integer, got ${length}`);
|
|
3633
|
+
}
|
|
3634
|
+
if (length > HKDF_SHA256_MAX_LENGTH) {
|
|
3635
|
+
throw new Error(`HKDF-SHA256 length cannot exceed ${HKDF_SHA256_MAX_LENGTH} bytes, got ${length}`);
|
|
3636
|
+
}
|
|
3637
|
+
return getCrypto().hkdfSha256(ikm, salt, info, length);
|
|
3638
|
+
}
|
|
3639
|
+
function encryptAesGcm(key, plaintext, iv) {
|
|
3640
|
+
if (key.length !== 32) {
|
|
3641
|
+
throw new Error(`AES-256-GCM requires a 32-byte key, got ${key.length} bytes`);
|
|
3642
|
+
}
|
|
3643
|
+
if (iv.length !== 12) {
|
|
3644
|
+
throw new Error(`AES-GCM requires a 12-byte IV, got ${iv.length} bytes`);
|
|
3645
|
+
}
|
|
3646
|
+
return getCrypto().encryptAesGcm(key, plaintext, iv);
|
|
3647
|
+
}
|
|
3648
|
+
function decryptAesGcm(key, ciphertext, iv, tag) {
|
|
3649
|
+
if (key.length !== 32) {
|
|
3650
|
+
throw new Error(`AES-256-GCM requires a 32-byte key, got ${key.length} bytes`);
|
|
3651
|
+
}
|
|
3652
|
+
if (iv.length !== 12) {
|
|
3653
|
+
throw new Error(`AES-GCM requires a 12-byte IV, got ${iv.length} bytes`);
|
|
3654
|
+
}
|
|
3655
|
+
if (tag.length !== 16) {
|
|
3656
|
+
throw new Error(`AES-GCM requires a 16-byte authentication tag, got ${tag.length} bytes`);
|
|
3657
|
+
}
|
|
3658
|
+
return getCrypto().decryptAesGcm(key, ciphertext, iv, tag);
|
|
3659
|
+
}
|
|
3660
|
+
function timingSafeEqual(a, b) {
|
|
3661
|
+
if (a.length !== b.length) {
|
|
3662
|
+
throw new Error(`timingSafeEqual requires equal-length arrays, got ${a.length} and ${b.length} bytes`);
|
|
3663
|
+
}
|
|
3664
|
+
return getCrypto().timingSafeEqual(a, b);
|
|
3665
|
+
}
|
|
3666
|
+
function bytesToHex(data) {
|
|
3667
|
+
return Array.from(data).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
3668
|
+
}
|
|
3669
|
+
function base64urlEncode(data) {
|
|
3670
|
+
let base64;
|
|
3671
|
+
if (typeof Buffer !== "undefined") {
|
|
3672
|
+
base64 = Buffer.from(data).toString("base64");
|
|
3673
|
+
} else {
|
|
3674
|
+
const binString = Array.from(data, (byte) => String.fromCodePoint(byte)).join("");
|
|
3675
|
+
base64 = btoa(binString);
|
|
3676
|
+
}
|
|
3677
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3678
|
+
}
|
|
3679
|
+
function base64urlDecode(data) {
|
|
3680
|
+
let base64 = data.replace(/-/g, "+").replace(/_/g, "/");
|
|
3681
|
+
const pad = base64.length % 4;
|
|
3682
|
+
if (pad) {
|
|
3683
|
+
base64 += "=".repeat(4 - pad);
|
|
3684
|
+
}
|
|
3685
|
+
if (typeof Buffer !== "undefined") {
|
|
3686
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
3687
|
+
} else {
|
|
3688
|
+
const binString = atob(base64);
|
|
3689
|
+
return Uint8Array.from(binString, (c) => c.codePointAt(0) ?? 0);
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
function base64Encode(data) {
|
|
3693
|
+
if (typeof Buffer !== "undefined") {
|
|
3694
|
+
return Buffer.from(data).toString("base64");
|
|
3695
|
+
} else {
|
|
3696
|
+
const binString = Array.from(data, (byte) => String.fromCodePoint(byte)).join("");
|
|
3697
|
+
return btoa(binString);
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
function base64Decode(data) {
|
|
3701
|
+
if (typeof Buffer !== "undefined") {
|
|
3702
|
+
return new Uint8Array(Buffer.from(data, "base64"));
|
|
3703
|
+
} else {
|
|
3704
|
+
const binString = atob(data);
|
|
3705
|
+
return Uint8Array.from(binString, (c) => c.codePointAt(0) ?? 0);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
function sha256Base64url(data) {
|
|
3709
|
+
return base64urlEncode(sha256(data));
|
|
3710
|
+
}
|
|
3711
|
+
|
|
3712
|
+
// libs/utils/src/storage/factory.ts
|
|
3713
|
+
init_errors();
|
|
3714
|
+
|
|
3715
|
+
// libs/utils/src/storage/namespace.ts
|
|
3716
|
+
var NAMESPACE_SEPARATOR = ":";
|
|
3717
|
+
function buildPrefix(name, id) {
|
|
3718
|
+
if (id) {
|
|
3719
|
+
return `${name}${NAMESPACE_SEPARATOR}${id}${NAMESPACE_SEPARATOR}`;
|
|
3720
|
+
}
|
|
3721
|
+
return `${name}${NAMESPACE_SEPARATOR}`;
|
|
3722
|
+
}
|
|
3723
|
+
var NamespacedStorageImpl = class _NamespacedStorageImpl {
|
|
3724
|
+
constructor(adapter, prefix = "", root = adapter) {
|
|
3725
|
+
this.adapter = adapter;
|
|
3726
|
+
this.prefix = prefix;
|
|
3727
|
+
this.root = root;
|
|
3728
|
+
}
|
|
3729
|
+
// ============================================
|
|
3730
|
+
// Key Prefixing Helpers
|
|
3731
|
+
// ============================================
|
|
3732
|
+
/**
|
|
3733
|
+
* Add prefix to a key.
|
|
3734
|
+
*/
|
|
3735
|
+
prefixKey(key) {
|
|
3736
|
+
return this.prefix + key;
|
|
3737
|
+
}
|
|
3738
|
+
/**
|
|
3739
|
+
* Remove prefix from a key.
|
|
3740
|
+
*/
|
|
3741
|
+
unprefixKey(key) {
|
|
3742
|
+
if (this.prefix && key.startsWith(this.prefix)) {
|
|
3743
|
+
return key.slice(this.prefix.length);
|
|
3744
|
+
}
|
|
3745
|
+
return key;
|
|
3746
|
+
}
|
|
3747
|
+
/**
|
|
3748
|
+
* Add prefix to a pattern (for keys() operation).
|
|
3749
|
+
*/
|
|
3750
|
+
prefixPattern(pattern) {
|
|
3751
|
+
return this.prefix + pattern;
|
|
3752
|
+
}
|
|
3753
|
+
/**
|
|
3754
|
+
* Add prefix to a channel (for pub/sub).
|
|
3755
|
+
*/
|
|
3756
|
+
prefixChannel(channel) {
|
|
3757
|
+
return this.prefix + channel;
|
|
3758
|
+
}
|
|
3759
|
+
// ============================================
|
|
3760
|
+
// Namespace API
|
|
3761
|
+
// ============================================
|
|
3762
|
+
namespace(name, id) {
|
|
3763
|
+
const newPrefix = this.prefix + buildPrefix(name, id);
|
|
3764
|
+
return new _NamespacedStorageImpl(this.adapter, newPrefix, this.root);
|
|
3765
|
+
}
|
|
3766
|
+
// ============================================
|
|
3767
|
+
// Connection Lifecycle (delegated to root)
|
|
3768
|
+
// ============================================
|
|
3769
|
+
async connect() {
|
|
3770
|
+
return this.adapter.connect();
|
|
3771
|
+
}
|
|
3772
|
+
async disconnect() {
|
|
3773
|
+
return this.adapter.disconnect();
|
|
3774
|
+
}
|
|
3775
|
+
async ping() {
|
|
3776
|
+
return this.adapter.ping();
|
|
3777
|
+
}
|
|
3778
|
+
// ============================================
|
|
3779
|
+
// Core Operations (with prefixing)
|
|
3780
|
+
// ============================================
|
|
3781
|
+
async get(key) {
|
|
3782
|
+
return this.adapter.get(this.prefixKey(key));
|
|
3783
|
+
}
|
|
3784
|
+
async set(key, value, options) {
|
|
3785
|
+
return this.adapter.set(this.prefixKey(key), value, options);
|
|
3786
|
+
}
|
|
3787
|
+
async delete(key) {
|
|
3788
|
+
return this.adapter.delete(this.prefixKey(key));
|
|
3789
|
+
}
|
|
3790
|
+
async exists(key) {
|
|
3791
|
+
return this.adapter.exists(this.prefixKey(key));
|
|
3792
|
+
}
|
|
3793
|
+
// ============================================
|
|
3794
|
+
// Batch Operations (with prefixing)
|
|
3795
|
+
// ============================================
|
|
3796
|
+
async mget(keys) {
|
|
3797
|
+
const prefixedKeys = keys.map((k) => this.prefixKey(k));
|
|
3798
|
+
return this.adapter.mget(prefixedKeys);
|
|
3799
|
+
}
|
|
3800
|
+
async mset(entries) {
|
|
3801
|
+
const prefixedEntries = entries.map((e) => ({
|
|
3802
|
+
...e,
|
|
3803
|
+
key: this.prefixKey(e.key)
|
|
3804
|
+
}));
|
|
3805
|
+
return this.adapter.mset(prefixedEntries);
|
|
3806
|
+
}
|
|
3807
|
+
async mdelete(keys) {
|
|
3808
|
+
const prefixedKeys = keys.map((k) => this.prefixKey(k));
|
|
3809
|
+
return this.adapter.mdelete(prefixedKeys);
|
|
3810
|
+
}
|
|
3811
|
+
// ============================================
|
|
3812
|
+
// TTL Operations (with prefixing)
|
|
3813
|
+
// ============================================
|
|
3814
|
+
async expire(key, ttlSeconds) {
|
|
3815
|
+
return this.adapter.expire(this.prefixKey(key), ttlSeconds);
|
|
3816
|
+
}
|
|
3817
|
+
async ttl(key) {
|
|
3818
|
+
return this.adapter.ttl(this.prefixKey(key));
|
|
3819
|
+
}
|
|
3820
|
+
// ============================================
|
|
3821
|
+
// Key Enumeration (with prefixing)
|
|
3822
|
+
// ============================================
|
|
3823
|
+
async keys(pattern = "*") {
|
|
3824
|
+
const prefixedPattern = this.prefixPattern(pattern);
|
|
3825
|
+
const keys = await this.adapter.keys(prefixedPattern);
|
|
3826
|
+
return keys.map((k) => this.unprefixKey(k));
|
|
3827
|
+
}
|
|
3828
|
+
async count(pattern = "*") {
|
|
3829
|
+
const prefixedPattern = this.prefixPattern(pattern);
|
|
3830
|
+
return this.adapter.count(prefixedPattern);
|
|
3831
|
+
}
|
|
3832
|
+
// ============================================
|
|
3833
|
+
// Atomic Operations (with prefixing)
|
|
3834
|
+
// ============================================
|
|
3835
|
+
async incr(key) {
|
|
3836
|
+
return this.adapter.incr(this.prefixKey(key));
|
|
3837
|
+
}
|
|
3838
|
+
async decr(key) {
|
|
3839
|
+
return this.adapter.decr(this.prefixKey(key));
|
|
3840
|
+
}
|
|
3841
|
+
async incrBy(key, amount) {
|
|
3842
|
+
return this.adapter.incrBy(this.prefixKey(key), amount);
|
|
3843
|
+
}
|
|
3844
|
+
// ============================================
|
|
3845
|
+
// Pub/Sub (with channel prefixing)
|
|
3846
|
+
// ============================================
|
|
3847
|
+
async publish(channel, message) {
|
|
3848
|
+
return this.adapter.publish(this.prefixChannel(channel), message);
|
|
3849
|
+
}
|
|
3850
|
+
async subscribe(channel, handler) {
|
|
3851
|
+
const prefixedChannel = this.prefixChannel(channel);
|
|
3852
|
+
const wrappedHandler = (message, ch) => {
|
|
3853
|
+
const unprefixedChannel = ch.startsWith(this.prefix) ? ch.slice(this.prefix.length) : ch;
|
|
3854
|
+
handler(message, unprefixedChannel);
|
|
3855
|
+
};
|
|
3856
|
+
return this.adapter.subscribe(prefixedChannel, wrappedHandler);
|
|
3857
|
+
}
|
|
3858
|
+
supportsPubSub() {
|
|
3859
|
+
return this.adapter.supportsPubSub();
|
|
3860
|
+
}
|
|
3861
|
+
};
|
|
3862
|
+
function createRootStorage(adapter) {
|
|
3863
|
+
return new NamespacedStorageImpl(adapter, "", adapter);
|
|
3864
|
+
}
|
|
3865
|
+
function createNamespacedStorage(adapter, prefix) {
|
|
3866
|
+
const normalizedPrefix = prefix && !prefix.endsWith(NAMESPACE_SEPARATOR) ? prefix + NAMESPACE_SEPARATOR : prefix;
|
|
3867
|
+
return new NamespacedStorageImpl(adapter, normalizedPrefix, adapter);
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
// libs/utils/src/storage/factory.ts
|
|
3871
|
+
function isProduction() {
|
|
3872
|
+
return process.env["NODE_ENV"] === "production";
|
|
3873
|
+
}
|
|
3874
|
+
function detectStorageType() {
|
|
3875
|
+
if (process.env["UPSTASH_REDIS_REST_URL"] && process.env["UPSTASH_REDIS_REST_TOKEN"]) {
|
|
3876
|
+
return "upstash";
|
|
3877
|
+
}
|
|
3878
|
+
if (process.env["KV_REST_API_URL"] && process.env["KV_REST_API_TOKEN"]) {
|
|
3879
|
+
return "vercel-kv";
|
|
3880
|
+
}
|
|
3881
|
+
if (process.env["REDIS_URL"] || process.env["REDIS_HOST"]) {
|
|
3882
|
+
return "redis";
|
|
3883
|
+
}
|
|
3884
|
+
return "memory";
|
|
3885
|
+
}
|
|
3886
|
+
async function createAdapter(type, config) {
|
|
3887
|
+
switch (type) {
|
|
3888
|
+
case "memory": {
|
|
3889
|
+
return new MemoryStorageAdapter(config.memory);
|
|
3890
|
+
}
|
|
3891
|
+
case "redis": {
|
|
3892
|
+
const { RedisStorageAdapter: RedisStorageAdapter2 } = await Promise.resolve().then(() => (init_redis(), redis_exports));
|
|
3893
|
+
return new RedisStorageAdapter2(config.redis);
|
|
3894
|
+
}
|
|
3895
|
+
case "vercel-kv": {
|
|
3896
|
+
const { VercelKvStorageAdapter: VercelKvStorageAdapter2 } = await Promise.resolve().then(() => (init_vercel_kv(), vercel_kv_exports));
|
|
3897
|
+
return new VercelKvStorageAdapter2(config.vercelKv);
|
|
3898
|
+
}
|
|
3899
|
+
case "upstash": {
|
|
3900
|
+
const { UpstashStorageAdapter: UpstashStorageAdapter2 } = await Promise.resolve().then(() => (init_upstash(), upstash_exports));
|
|
3901
|
+
return new UpstashStorageAdapter2(config.upstash);
|
|
3902
|
+
}
|
|
3903
|
+
case "auto":
|
|
3904
|
+
throw new StorageConfigError("auto", "Auto type should be resolved before calling createAdapter");
|
|
3905
|
+
default:
|
|
3207
3906
|
throw new StorageConfigError("unknown", `Unknown storage type: ${type}`);
|
|
3208
3907
|
}
|
|
3209
3908
|
}
|
|
@@ -3263,6 +3962,501 @@ function getDetectedStorageType() {
|
|
|
3263
3962
|
// libs/utils/src/storage/index.ts
|
|
3264
3963
|
init_errors();
|
|
3265
3964
|
|
|
3965
|
+
// libs/utils/src/storage/typed-storage.ts
|
|
3966
|
+
var TypedStorage = class {
|
|
3967
|
+
storage;
|
|
3968
|
+
serialize;
|
|
3969
|
+
deserialize;
|
|
3970
|
+
schema;
|
|
3971
|
+
throwOnInvalid;
|
|
3972
|
+
constructor(storage, options = {}) {
|
|
3973
|
+
this.storage = storage;
|
|
3974
|
+
this.serialize = options.serialize ?? JSON.stringify;
|
|
3975
|
+
this.deserialize = options.deserialize ?? JSON.parse;
|
|
3976
|
+
this.schema = options.schema;
|
|
3977
|
+
this.throwOnInvalid = options.throwOnInvalid ?? false;
|
|
3978
|
+
}
|
|
3979
|
+
/**
|
|
3980
|
+
* Get a typed value by key.
|
|
3981
|
+
*
|
|
3982
|
+
* @param key - Storage key
|
|
3983
|
+
* @returns The typed value, or null if not found or invalid
|
|
3984
|
+
*/
|
|
3985
|
+
async get(key) {
|
|
3986
|
+
const raw = await this.storage.get(key);
|
|
3987
|
+
if (raw === null) {
|
|
3988
|
+
return null;
|
|
3989
|
+
}
|
|
3990
|
+
return this.parseValue(raw, key);
|
|
3991
|
+
}
|
|
3992
|
+
/**
|
|
3993
|
+
* Set a typed value with optional TTL.
|
|
3994
|
+
*
|
|
3995
|
+
* @param key - Storage key
|
|
3996
|
+
* @param value - Typed value to store
|
|
3997
|
+
* @param options - Optional TTL and conditional flags
|
|
3998
|
+
*/
|
|
3999
|
+
async set(key, value, options) {
|
|
4000
|
+
const serialized = this.serialize(value);
|
|
4001
|
+
await this.storage.set(key, serialized, options);
|
|
4002
|
+
}
|
|
4003
|
+
/**
|
|
4004
|
+
* Delete a key.
|
|
4005
|
+
*
|
|
4006
|
+
* @param key - Storage key
|
|
4007
|
+
* @returns true if key existed and was deleted
|
|
4008
|
+
*/
|
|
4009
|
+
async delete(key) {
|
|
4010
|
+
return this.storage.delete(key);
|
|
4011
|
+
}
|
|
4012
|
+
/**
|
|
4013
|
+
* Check if a key exists.
|
|
4014
|
+
*
|
|
4015
|
+
* @param key - Storage key
|
|
4016
|
+
* @returns true if key exists
|
|
4017
|
+
*/
|
|
4018
|
+
async exists(key) {
|
|
4019
|
+
return this.storage.exists(key);
|
|
4020
|
+
}
|
|
4021
|
+
/**
|
|
4022
|
+
* Get multiple typed values.
|
|
4023
|
+
*
|
|
4024
|
+
* @param keys - Array of storage keys
|
|
4025
|
+
* @returns Array of typed values (null for missing/invalid keys)
|
|
4026
|
+
*/
|
|
4027
|
+
async mget(keys) {
|
|
4028
|
+
if (keys.length === 0) {
|
|
4029
|
+
return [];
|
|
4030
|
+
}
|
|
4031
|
+
const rawValues = await this.storage.mget(keys);
|
|
4032
|
+
return rawValues.map((raw, index) => {
|
|
4033
|
+
if (raw === null) {
|
|
4034
|
+
return null;
|
|
4035
|
+
}
|
|
4036
|
+
return this.parseValue(raw, keys[index]);
|
|
4037
|
+
});
|
|
4038
|
+
}
|
|
4039
|
+
/**
|
|
4040
|
+
* Set multiple typed values.
|
|
4041
|
+
*
|
|
4042
|
+
* @param entries - Array of key-value-options entries
|
|
4043
|
+
*/
|
|
4044
|
+
async mset(entries) {
|
|
4045
|
+
if (entries.length === 0) {
|
|
4046
|
+
return;
|
|
4047
|
+
}
|
|
4048
|
+
const rawEntries = entries.map((entry) => ({
|
|
4049
|
+
key: entry.key,
|
|
4050
|
+
value: this.serialize(entry.value),
|
|
4051
|
+
options: entry.options
|
|
4052
|
+
}));
|
|
4053
|
+
await this.storage.mset(rawEntries);
|
|
4054
|
+
}
|
|
4055
|
+
/**
|
|
4056
|
+
* Delete multiple keys.
|
|
4057
|
+
*
|
|
4058
|
+
* @param keys - Array of storage keys
|
|
4059
|
+
* @returns Number of keys actually deleted
|
|
4060
|
+
*/
|
|
4061
|
+
async mdelete(keys) {
|
|
4062
|
+
return this.storage.mdelete(keys);
|
|
4063
|
+
}
|
|
4064
|
+
/**
|
|
4065
|
+
* Update TTL on an existing key.
|
|
4066
|
+
*
|
|
4067
|
+
* @param key - Storage key
|
|
4068
|
+
* @param ttlSeconds - New TTL in seconds
|
|
4069
|
+
* @returns true if key exists and TTL was set
|
|
4070
|
+
*/
|
|
4071
|
+
async expire(key, ttlSeconds) {
|
|
4072
|
+
return this.storage.expire(key, ttlSeconds);
|
|
4073
|
+
}
|
|
4074
|
+
/**
|
|
4075
|
+
* Get remaining TTL for a key.
|
|
4076
|
+
*
|
|
4077
|
+
* @param key - Storage key
|
|
4078
|
+
* @returns TTL in seconds, -1 if no TTL, or null if key doesn't exist
|
|
4079
|
+
*/
|
|
4080
|
+
async ttl(key) {
|
|
4081
|
+
return this.storage.ttl(key);
|
|
4082
|
+
}
|
|
4083
|
+
/**
|
|
4084
|
+
* List keys matching a pattern.
|
|
4085
|
+
*
|
|
4086
|
+
* @param pattern - Glob pattern (default: '*' for all keys)
|
|
4087
|
+
* @returns Array of matching keys
|
|
4088
|
+
*/
|
|
4089
|
+
async keys(pattern) {
|
|
4090
|
+
return this.storage.keys(pattern);
|
|
4091
|
+
}
|
|
4092
|
+
/**
|
|
4093
|
+
* Count keys matching a pattern.
|
|
4094
|
+
*
|
|
4095
|
+
* @param pattern - Glob pattern (default: '*' for all keys)
|
|
4096
|
+
* @returns Number of matching keys
|
|
4097
|
+
*/
|
|
4098
|
+
async count(pattern) {
|
|
4099
|
+
return this.storage.count(pattern);
|
|
4100
|
+
}
|
|
4101
|
+
/**
|
|
4102
|
+
* Get the underlying storage adapter.
|
|
4103
|
+
* Use with caution - operations bypass type safety.
|
|
4104
|
+
*/
|
|
4105
|
+
get raw() {
|
|
4106
|
+
return this.storage;
|
|
4107
|
+
}
|
|
4108
|
+
/**
|
|
4109
|
+
* Parse and validate a raw value.
|
|
4110
|
+
*/
|
|
4111
|
+
parseValue(raw, key) {
|
|
4112
|
+
try {
|
|
4113
|
+
const parsed = this.deserialize(raw);
|
|
4114
|
+
if (this.schema) {
|
|
4115
|
+
const result = this.schema.safeParse(parsed);
|
|
4116
|
+
if (!result.success) {
|
|
4117
|
+
if (this.throwOnInvalid) {
|
|
4118
|
+
throw new Error(`TypedStorage validation failed for key "${key}": ${result.error.message}`);
|
|
4119
|
+
}
|
|
4120
|
+
return null;
|
|
4121
|
+
}
|
|
4122
|
+
return result.data;
|
|
4123
|
+
}
|
|
4124
|
+
return parsed;
|
|
4125
|
+
} catch (error) {
|
|
4126
|
+
if (this.throwOnInvalid) {
|
|
4127
|
+
throw error;
|
|
4128
|
+
}
|
|
4129
|
+
return null;
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
};
|
|
4133
|
+
|
|
4134
|
+
// libs/utils/src/storage/encrypted-typed-storage.ts
|
|
4135
|
+
init_errors();
|
|
4136
|
+
var textEncoder2 = new TextEncoder();
|
|
4137
|
+
var textDecoder2 = new TextDecoder();
|
|
4138
|
+
var EncryptedTypedStorage = class {
|
|
4139
|
+
storage;
|
|
4140
|
+
activeKey;
|
|
4141
|
+
keyMap;
|
|
4142
|
+
schema;
|
|
4143
|
+
throwOnError;
|
|
4144
|
+
onKeyRotationNeeded;
|
|
4145
|
+
clientBinding;
|
|
4146
|
+
constructor(storage, options) {
|
|
4147
|
+
if (!options.keys || options.keys.length === 0) {
|
|
4148
|
+
throw new EncryptedStorageError("At least one encryption key is required");
|
|
4149
|
+
}
|
|
4150
|
+
for (const k of options.keys) {
|
|
4151
|
+
if (k.key.length !== 32) {
|
|
4152
|
+
throw new EncryptedStorageError(
|
|
4153
|
+
`Encryption key "${k.kid}" must be 32 bytes (AES-256), got ${k.key.length} bytes`
|
|
4154
|
+
);
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
this.storage = storage;
|
|
4158
|
+
this.activeKey = options.keys[0];
|
|
4159
|
+
this.keyMap = new Map(options.keys.map((k) => [k.kid, k.key]));
|
|
4160
|
+
this.schema = options.schema;
|
|
4161
|
+
this.throwOnError = options.throwOnError ?? false;
|
|
4162
|
+
this.onKeyRotationNeeded = options.onKeyRotationNeeded;
|
|
4163
|
+
this.clientBinding = options.clientBinding;
|
|
4164
|
+
}
|
|
4165
|
+
/**
|
|
4166
|
+
* Get a decrypted value by key.
|
|
4167
|
+
*
|
|
4168
|
+
* @param key - Storage key
|
|
4169
|
+
* @returns The decrypted value, or null if not found or decryption fails
|
|
4170
|
+
*/
|
|
4171
|
+
async get(key) {
|
|
4172
|
+
const raw = await this.storage.get(key);
|
|
4173
|
+
if (raw === null) {
|
|
4174
|
+
return null;
|
|
4175
|
+
}
|
|
4176
|
+
return this.decryptAndParse(raw, key);
|
|
4177
|
+
}
|
|
4178
|
+
/**
|
|
4179
|
+
* Encrypt and store a value.
|
|
4180
|
+
*
|
|
4181
|
+
* @param key - Storage key
|
|
4182
|
+
* @param value - Value to encrypt and store
|
|
4183
|
+
* @param options - Optional TTL and conditional flags
|
|
4184
|
+
*/
|
|
4185
|
+
async set(key, value, options) {
|
|
4186
|
+
const encrypted = this.encryptValue(value);
|
|
4187
|
+
const serialized = JSON.stringify(encrypted);
|
|
4188
|
+
await this.storage.set(key, serialized, options);
|
|
4189
|
+
}
|
|
4190
|
+
/**
|
|
4191
|
+
* Delete a key.
|
|
4192
|
+
*
|
|
4193
|
+
* @param key - Storage key
|
|
4194
|
+
* @returns true if key existed and was deleted
|
|
4195
|
+
*/
|
|
4196
|
+
async delete(key) {
|
|
4197
|
+
return this.storage.delete(key);
|
|
4198
|
+
}
|
|
4199
|
+
/**
|
|
4200
|
+
* Check if a key exists.
|
|
4201
|
+
*
|
|
4202
|
+
* @param key - Storage key
|
|
4203
|
+
* @returns true if key exists
|
|
4204
|
+
*/
|
|
4205
|
+
async exists(key) {
|
|
4206
|
+
return this.storage.exists(key);
|
|
4207
|
+
}
|
|
4208
|
+
/**
|
|
4209
|
+
* Get multiple decrypted values.
|
|
4210
|
+
*
|
|
4211
|
+
* @param keys - Array of storage keys
|
|
4212
|
+
* @returns Array of decrypted values (null for missing/invalid keys)
|
|
4213
|
+
*/
|
|
4214
|
+
async mget(keys) {
|
|
4215
|
+
if (keys.length === 0) {
|
|
4216
|
+
return [];
|
|
4217
|
+
}
|
|
4218
|
+
const rawValues = await this.storage.mget(keys);
|
|
4219
|
+
return rawValues.map((raw, index) => {
|
|
4220
|
+
if (raw === null) {
|
|
4221
|
+
return null;
|
|
4222
|
+
}
|
|
4223
|
+
return this.decryptAndParse(raw, keys[index]);
|
|
4224
|
+
});
|
|
4225
|
+
}
|
|
4226
|
+
/**
|
|
4227
|
+
* Encrypt and store multiple values.
|
|
4228
|
+
*
|
|
4229
|
+
* @param entries - Array of key-value-options entries
|
|
4230
|
+
*/
|
|
4231
|
+
async mset(entries) {
|
|
4232
|
+
if (entries.length === 0) {
|
|
4233
|
+
return;
|
|
4234
|
+
}
|
|
4235
|
+
const rawEntries = entries.map((entry) => ({
|
|
4236
|
+
key: entry.key,
|
|
4237
|
+
value: JSON.stringify(this.encryptValue(entry.value)),
|
|
4238
|
+
options: entry.options
|
|
4239
|
+
}));
|
|
4240
|
+
await this.storage.mset(rawEntries);
|
|
4241
|
+
}
|
|
4242
|
+
/**
|
|
4243
|
+
* Delete multiple keys.
|
|
4244
|
+
*
|
|
4245
|
+
* @param keys - Array of storage keys
|
|
4246
|
+
* @returns Number of keys actually deleted
|
|
4247
|
+
*/
|
|
4248
|
+
async mdelete(keys) {
|
|
4249
|
+
return this.storage.mdelete(keys);
|
|
4250
|
+
}
|
|
4251
|
+
/**
|
|
4252
|
+
* Update TTL on an existing key.
|
|
4253
|
+
*
|
|
4254
|
+
* @param key - Storage key
|
|
4255
|
+
* @param ttlSeconds - New TTL in seconds
|
|
4256
|
+
* @returns true if key exists and TTL was set
|
|
4257
|
+
*/
|
|
4258
|
+
async expire(key, ttlSeconds) {
|
|
4259
|
+
return this.storage.expire(key, ttlSeconds);
|
|
4260
|
+
}
|
|
4261
|
+
/**
|
|
4262
|
+
* Get remaining TTL for a key.
|
|
4263
|
+
*
|
|
4264
|
+
* @param key - Storage key
|
|
4265
|
+
* @returns TTL in seconds, -1 if no TTL, or null if key doesn't exist
|
|
4266
|
+
*/
|
|
4267
|
+
async ttl(key) {
|
|
4268
|
+
return this.storage.ttl(key);
|
|
4269
|
+
}
|
|
4270
|
+
/**
|
|
4271
|
+
* List keys matching a pattern.
|
|
4272
|
+
*
|
|
4273
|
+
* @param pattern - Glob pattern (default: '*' for all keys)
|
|
4274
|
+
* @returns Array of matching keys
|
|
4275
|
+
*/
|
|
4276
|
+
async keys(pattern) {
|
|
4277
|
+
return this.storage.keys(pattern);
|
|
4278
|
+
}
|
|
4279
|
+
/**
|
|
4280
|
+
* Count keys matching a pattern.
|
|
4281
|
+
*
|
|
4282
|
+
* @param pattern - Glob pattern (default: '*' for all keys)
|
|
4283
|
+
* @returns Number of matching keys
|
|
4284
|
+
*/
|
|
4285
|
+
async count(pattern) {
|
|
4286
|
+
return this.storage.count(pattern);
|
|
4287
|
+
}
|
|
4288
|
+
/**
|
|
4289
|
+
* Re-encrypt a value with the active key.
|
|
4290
|
+
* Useful for key rotation - reads with old key, writes with new key.
|
|
4291
|
+
*
|
|
4292
|
+
* @param key - Storage key to re-encrypt
|
|
4293
|
+
* @param options - Optional TTL for the re-encrypted value
|
|
4294
|
+
* @returns true if value was re-encrypted, false if not found
|
|
4295
|
+
*/
|
|
4296
|
+
async reencrypt(key, options) {
|
|
4297
|
+
const value = await this.get(key);
|
|
4298
|
+
if (value === null) {
|
|
4299
|
+
return false;
|
|
4300
|
+
}
|
|
4301
|
+
await this.set(key, value, options);
|
|
4302
|
+
return true;
|
|
4303
|
+
}
|
|
4304
|
+
/**
|
|
4305
|
+
* Rotate the active encryption key.
|
|
4306
|
+
* New encryptions will use the new key; old keys remain for decryption.
|
|
4307
|
+
*
|
|
4308
|
+
* @param newKey - New encryption key to use for writes
|
|
4309
|
+
*/
|
|
4310
|
+
rotateKey(newKey) {
|
|
4311
|
+
if (newKey.key.length !== 32) {
|
|
4312
|
+
throw new EncryptedStorageError(`New encryption key must be 32 bytes (AES-256), got ${newKey.key.length} bytes`);
|
|
4313
|
+
}
|
|
4314
|
+
if (!this.keyMap.has(newKey.kid)) {
|
|
4315
|
+
this.keyMap.set(newKey.kid, newKey.key);
|
|
4316
|
+
}
|
|
4317
|
+
this.activeKey = newKey;
|
|
4318
|
+
}
|
|
4319
|
+
/**
|
|
4320
|
+
* Get the active key ID.
|
|
4321
|
+
*/
|
|
4322
|
+
get activeKeyId() {
|
|
4323
|
+
return this.activeKey.kid;
|
|
4324
|
+
}
|
|
4325
|
+
/**
|
|
4326
|
+
* Get all known key IDs.
|
|
4327
|
+
*/
|
|
4328
|
+
get keyIds() {
|
|
4329
|
+
return Array.from(this.keyMap.keys());
|
|
4330
|
+
}
|
|
4331
|
+
/**
|
|
4332
|
+
* Get the underlying storage adapter.
|
|
4333
|
+
* Use with caution - operations bypass encryption.
|
|
4334
|
+
*/
|
|
4335
|
+
get raw() {
|
|
4336
|
+
return this.storage;
|
|
4337
|
+
}
|
|
4338
|
+
/**
|
|
4339
|
+
* Check if client-side key binding is enabled.
|
|
4340
|
+
* When true, encryption keys are derived from both server key and client secret.
|
|
4341
|
+
*/
|
|
4342
|
+
get hasClientBinding() {
|
|
4343
|
+
return this.clientBinding !== void 0;
|
|
4344
|
+
}
|
|
4345
|
+
/**
|
|
4346
|
+
* Derive the actual encryption key.
|
|
4347
|
+
*
|
|
4348
|
+
* If clientBinding is configured, combines server key with client secret
|
|
4349
|
+
* using HKDF-SHA256 (RFC 5869) to produce the actual encryption key.
|
|
4350
|
+
* Otherwise, returns the server key as-is.
|
|
4351
|
+
*
|
|
4352
|
+
* This provides zero-knowledge encryption where:
|
|
4353
|
+
* - Server cannot decrypt without client secret (sessionId)
|
|
4354
|
+
* - Client cannot decrypt without server key
|
|
4355
|
+
* - Key derivation is deterministic (same inputs -> same derived key)
|
|
4356
|
+
*
|
|
4357
|
+
* @param serverKey - The server-side encryption key
|
|
4358
|
+
* @returns The key to use for actual encryption/decryption
|
|
4359
|
+
*/
|
|
4360
|
+
deriveKey(serverKey) {
|
|
4361
|
+
if (!this.clientBinding) {
|
|
4362
|
+
return serverKey;
|
|
4363
|
+
}
|
|
4364
|
+
const clientSecret = typeof this.clientBinding.secret === "string" ? textEncoder2.encode(this.clientBinding.secret) : this.clientBinding.secret;
|
|
4365
|
+
const ikm = new Uint8Array(serverKey.length + clientSecret.length);
|
|
4366
|
+
ikm.set(serverKey, 0);
|
|
4367
|
+
ikm.set(clientSecret, serverKey.length);
|
|
4368
|
+
const salt = this.clientBinding.salt ?? new Uint8Array(0);
|
|
4369
|
+
const info = textEncoder2.encode(this.clientBinding.info ?? "frontmcp-client-bound-v1");
|
|
4370
|
+
return hkdfSha256(ikm, salt, info, 32);
|
|
4371
|
+
}
|
|
4372
|
+
/**
|
|
4373
|
+
* Encrypt a value using the active key.
|
|
4374
|
+
* If client binding is configured, uses derived key from server key + client secret.
|
|
4375
|
+
*/
|
|
4376
|
+
encryptValue(value) {
|
|
4377
|
+
let jsonString;
|
|
4378
|
+
try {
|
|
4379
|
+
jsonString = JSON.stringify(value);
|
|
4380
|
+
} catch (error) {
|
|
4381
|
+
throw new EncryptedStorageError(`Failed to serialize value: ${error.message}`);
|
|
4382
|
+
}
|
|
4383
|
+
const plaintext = textEncoder2.encode(jsonString);
|
|
4384
|
+
const iv = randomBytes(12);
|
|
4385
|
+
const encryptionKey = this.deriveKey(this.activeKey.key);
|
|
4386
|
+
const { ciphertext, tag } = encryptAesGcm(encryptionKey, plaintext, iv);
|
|
4387
|
+
return {
|
|
4388
|
+
alg: "A256GCM",
|
|
4389
|
+
kid: this.activeKey.kid,
|
|
4390
|
+
iv: base64urlEncode(iv),
|
|
4391
|
+
tag: base64urlEncode(tag),
|
|
4392
|
+
data: base64urlEncode(ciphertext)
|
|
4393
|
+
};
|
|
4394
|
+
}
|
|
4395
|
+
/**
|
|
4396
|
+
* Decrypt and parse a stored value.
|
|
4397
|
+
*/
|
|
4398
|
+
decryptAndParse(raw, storageKey) {
|
|
4399
|
+
let blob;
|
|
4400
|
+
try {
|
|
4401
|
+
blob = JSON.parse(raw);
|
|
4402
|
+
} catch (_error) {
|
|
4403
|
+
if (this.throwOnError) {
|
|
4404
|
+
throw new EncryptedStorageError(`Failed to parse stored blob for key "${storageKey}"`);
|
|
4405
|
+
}
|
|
4406
|
+
return null;
|
|
4407
|
+
}
|
|
4408
|
+
if (!this.isValidBlob(blob)) {
|
|
4409
|
+
if (this.throwOnError) {
|
|
4410
|
+
throw new EncryptedStorageError(`Invalid encrypted blob structure for key "${storageKey}"`);
|
|
4411
|
+
}
|
|
4412
|
+
return null;
|
|
4413
|
+
}
|
|
4414
|
+
const serverKey = this.keyMap.get(blob.kid);
|
|
4415
|
+
if (!serverKey) {
|
|
4416
|
+
if (this.throwOnError) {
|
|
4417
|
+
throw new EncryptedStorageError(
|
|
4418
|
+
`Unknown encryption key "${blob.kid}" for key "${storageKey}". Known keys: ${Array.from(this.keyMap.keys()).join(", ")}`
|
|
4419
|
+
);
|
|
4420
|
+
}
|
|
4421
|
+
return null;
|
|
4422
|
+
}
|
|
4423
|
+
const decryptionKey = this.deriveKey(serverKey);
|
|
4424
|
+
let decrypted;
|
|
4425
|
+
try {
|
|
4426
|
+
const iv = base64urlDecode(blob.iv);
|
|
4427
|
+
const tag = base64urlDecode(blob.tag);
|
|
4428
|
+
const ciphertext = base64urlDecode(blob.data);
|
|
4429
|
+
const plaintext = decryptAesGcm(decryptionKey, ciphertext, iv, tag);
|
|
4430
|
+
decrypted = JSON.parse(textDecoder2.decode(plaintext));
|
|
4431
|
+
} catch (error) {
|
|
4432
|
+
if (this.throwOnError) {
|
|
4433
|
+
throw new EncryptedStorageError(`Decryption failed for key "${storageKey}": ${error.message}`);
|
|
4434
|
+
}
|
|
4435
|
+
return null;
|
|
4436
|
+
}
|
|
4437
|
+
if (this.schema) {
|
|
4438
|
+
const result = this.schema.safeParse(decrypted);
|
|
4439
|
+
if (!result.success) {
|
|
4440
|
+
if (this.throwOnError) {
|
|
4441
|
+
throw new EncryptedStorageError(`Schema validation failed for key "${storageKey}": ${result.error.message}`);
|
|
4442
|
+
}
|
|
4443
|
+
return null;
|
|
4444
|
+
}
|
|
4445
|
+
decrypted = result.data;
|
|
4446
|
+
}
|
|
4447
|
+
if (blob.kid !== this.activeKey.kid && this.onKeyRotationNeeded) {
|
|
4448
|
+
this.onKeyRotationNeeded(storageKey, blob.kid, this.activeKey.kid);
|
|
4449
|
+
}
|
|
4450
|
+
return decrypted;
|
|
4451
|
+
}
|
|
4452
|
+
/**
|
|
4453
|
+
* Validate blob structure.
|
|
4454
|
+
*/
|
|
4455
|
+
isValidBlob(blob) {
|
|
4456
|
+
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";
|
|
4457
|
+
}
|
|
4458
|
+
};
|
|
4459
|
+
|
|
3266
4460
|
// libs/utils/src/storage/adapters/index.ts
|
|
3267
4461
|
init_base();
|
|
3268
4462
|
init_redis();
|
|
@@ -3270,6 +4464,7 @@ init_vercel_kv();
|
|
|
3270
4464
|
init_upstash();
|
|
3271
4465
|
|
|
3272
4466
|
// libs/utils/src/storage/index.ts
|
|
4467
|
+
init_pattern();
|
|
3273
4468
|
init_ttl();
|
|
3274
4469
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3275
4470
|
0 && (module.exports = {
|
|
@@ -3277,6 +4472,10 @@ init_ttl();
|
|
|
3277
4472
|
DEFAULT_CODE_VERIFIER_LENGTH,
|
|
3278
4473
|
DEFAULT_MAX_INPUT_LENGTH,
|
|
3279
4474
|
EncryptedBlobError,
|
|
4475
|
+
EncryptedStorageError,
|
|
4476
|
+
EncryptedTypedStorage,
|
|
4477
|
+
FileSystemStorageAdapter,
|
|
4478
|
+
KeyPersistence,
|
|
3280
4479
|
MAX_CODE_VERIFIER_LENGTH,
|
|
3281
4480
|
MAX_TTL_SECONDS,
|
|
3282
4481
|
MIN_CODE_VERIFIER_LENGTH,
|
|
@@ -3294,11 +4493,17 @@ init_ttl();
|
|
|
3294
4493
|
StorageOperationError,
|
|
3295
4494
|
StoragePatternError,
|
|
3296
4495
|
StorageTTLError,
|
|
4496
|
+
TypedStorage,
|
|
3297
4497
|
UpstashStorageAdapter,
|
|
3298
4498
|
VercelKvStorageAdapter,
|
|
3299
4499
|
access,
|
|
3300
4500
|
analyzePattern,
|
|
4501
|
+
anyKeyDataSchema,
|
|
3301
4502
|
assertNode,
|
|
4503
|
+
asymmetricAlgSchema,
|
|
4504
|
+
asymmetricKeyDataSchema,
|
|
4505
|
+
base64Decode,
|
|
4506
|
+
base64Encode,
|
|
3302
4507
|
base64urlDecode,
|
|
3303
4508
|
base64urlEncode,
|
|
3304
4509
|
buildPrefix,
|
|
@@ -3308,6 +4513,8 @@ init_ttl();
|
|
|
3308
4513
|
collapseWhitespace,
|
|
3309
4514
|
copyFile,
|
|
3310
4515
|
cp,
|
|
4516
|
+
createKeyPersistence,
|
|
4517
|
+
createKeyPersistenceWithStorage,
|
|
3311
4518
|
createMemoryStorage,
|
|
3312
4519
|
createNamespacedStorage,
|
|
3313
4520
|
createRootStorage,
|
|
@@ -3349,6 +4556,7 @@ init_ttl();
|
|
|
3349
4556
|
hmacSha256,
|
|
3350
4557
|
idFromString,
|
|
3351
4558
|
inferMimeType,
|
|
4559
|
+
isAsymmetricKeyData,
|
|
3352
4560
|
isBrowser,
|
|
3353
4561
|
isDirEmpty,
|
|
3354
4562
|
isExpired,
|
|
@@ -3357,7 +4565,9 @@ init_ttl();
|
|
|
3357
4565
|
isPatternSafe,
|
|
3358
4566
|
isRsaPssAlg,
|
|
3359
4567
|
isSecretCached,
|
|
4568
|
+
isSecretKeyData,
|
|
3360
4569
|
isSecretPersistenceEnabled,
|
|
4570
|
+
isSignedData,
|
|
3361
4571
|
isUriTemplate,
|
|
3362
4572
|
isValidCodeChallenge,
|
|
3363
4573
|
isValidCodeVerifier,
|
|
@@ -3372,12 +4582,14 @@ init_ttl();
|
|
|
3372
4582
|
mkdir,
|
|
3373
4583
|
mkdtemp,
|
|
3374
4584
|
normalizeTTL,
|
|
4585
|
+
parseKeyData,
|
|
3375
4586
|
parseSecretData,
|
|
3376
4587
|
parseUriTemplate,
|
|
3377
4588
|
randomBytes,
|
|
3378
4589
|
randomUUID,
|
|
3379
4590
|
readFile,
|
|
3380
4591
|
readFileBuffer,
|
|
4592
|
+
readFileSync,
|
|
3381
4593
|
readJSON,
|
|
3382
4594
|
readdir,
|
|
3383
4595
|
rename,
|
|
@@ -3394,12 +4606,14 @@ init_ttl();
|
|
|
3394
4606
|
sanitizeToJson,
|
|
3395
4607
|
saveSecret,
|
|
3396
4608
|
secretDataSchema,
|
|
4609
|
+
secretKeyDataSchema,
|
|
3397
4610
|
sepFor,
|
|
3398
4611
|
serializeBlob,
|
|
3399
4612
|
sha256,
|
|
3400
4613
|
sha256Base64url,
|
|
3401
4614
|
sha256Hex,
|
|
3402
4615
|
shortHash,
|
|
4616
|
+
signData,
|
|
3403
4617
|
splitWords,
|
|
3404
4618
|
stat,
|
|
3405
4619
|
timingSafeEqual,
|
|
@@ -3415,11 +4629,14 @@ init_ttl();
|
|
|
3415
4629
|
ttlToExpiresAt,
|
|
3416
4630
|
unlink,
|
|
3417
4631
|
validateBaseUrl,
|
|
4632
|
+
validateKeyData,
|
|
3418
4633
|
validateOptionalTTL,
|
|
3419
4634
|
validatePattern,
|
|
3420
4635
|
validateSecretData,
|
|
3421
4636
|
validateTTL,
|
|
3422
4637
|
verifyCodeChallenge,
|
|
4638
|
+
verifyData,
|
|
4639
|
+
verifyOrParseData,
|
|
3423
4640
|
writeFile,
|
|
3424
4641
|
writeJSON
|
|
3425
4642
|
});
|