@digilogiclabs/platform-core 1.2.0 → 1.4.0
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/README.md +659 -545
- package/dist/{ConsoleEmail-hUDFsKoA.d.mts → ConsoleEmail-ubSVWgTa.d.mts} +187 -1
- package/dist/{ConsoleEmail-hUDFsKoA.d.ts → ConsoleEmail-ubSVWgTa.d.ts} +187 -1
- package/dist/index.d.mts +683 -35
- package/dist/index.d.ts +683 -35
- package/dist/index.js +5818 -462
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5798 -456
- package/dist/index.mjs.map +1 -1
- package/dist/migrate.js +91 -33
- package/dist/migrate.js.map +1 -1
- package/dist/migrations/index.js +33 -13
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +33 -13
- package/dist/migrations/index.mjs.map +1 -1
- package/dist/security-headers.d.mts +75 -0
- package/dist/security-headers.d.ts +75 -0
- package/dist/security-headers.js +137 -0
- package/dist/security-headers.js.map +1 -0
- package/dist/security-headers.mjs +111 -0
- package/dist/security-headers.mjs.map +1 -0
- package/dist/testing.d.mts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +292 -37
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +291 -36
- package/dist/testing.mjs.map +1 -1
- package/package.json +11 -1
package/dist/testing.mjs
CHANGED
|
@@ -346,6 +346,7 @@ var init_IAI = __esm({
|
|
|
346
346
|
});
|
|
347
347
|
|
|
348
348
|
// src/interfaces/IRAG.ts
|
|
349
|
+
import { randomBytes as randomBytes6 } from "crypto";
|
|
349
350
|
var ChunkingPresets, MemoryRAG;
|
|
350
351
|
var init_IRAG = __esm({
|
|
351
352
|
"src/interfaces/IRAG.ts"() {
|
|
@@ -475,7 +476,7 @@ var init_IRAG = __esm({
|
|
|
475
476
|
}
|
|
476
477
|
async ingestOne(collection, document, options) {
|
|
477
478
|
const startTime = Date.now();
|
|
478
|
-
const docId = `doc_${Date.now()}_${
|
|
479
|
+
const docId = `doc_${Date.now()}_${randomBytes6(4).toString("hex")}`;
|
|
479
480
|
const now = /* @__PURE__ */ new Date();
|
|
480
481
|
try {
|
|
481
482
|
const col = await this.getCollection(collection);
|
|
@@ -1126,7 +1127,11 @@ var MemoryQueryBuilder = class {
|
|
|
1126
1127
|
const updated = [];
|
|
1127
1128
|
const newTable = table.map((item) => {
|
|
1128
1129
|
if (this.matchesWhere(item)) {
|
|
1129
|
-
const updatedItem = {
|
|
1130
|
+
const updatedItem = {
|
|
1131
|
+
...item,
|
|
1132
|
+
...this._updateData,
|
|
1133
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1134
|
+
};
|
|
1130
1135
|
updated.push(updatedItem);
|
|
1131
1136
|
return updatedItem;
|
|
1132
1137
|
}
|
|
@@ -1141,8 +1146,10 @@ var MemoryQueryBuilder = class {
|
|
|
1141
1146
|
result.sort((a, b) => {
|
|
1142
1147
|
const aVal = a[column];
|
|
1143
1148
|
const bVal = b[column];
|
|
1144
|
-
if (aVal === null || aVal === void 0)
|
|
1145
|
-
|
|
1149
|
+
if (aVal === null || aVal === void 0)
|
|
1150
|
+
return direction === "asc" ? 1 : -1;
|
|
1151
|
+
if (bVal === null || bVal === void 0)
|
|
1152
|
+
return direction === "asc" ? -1 : 1;
|
|
1146
1153
|
const cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
1147
1154
|
return direction === "asc" ? cmp : -cmp;
|
|
1148
1155
|
});
|
|
@@ -1223,7 +1230,9 @@ var MemoryCache = class {
|
|
|
1223
1230
|
return Promise.all(keys.map((key) => this.get(key)));
|
|
1224
1231
|
}
|
|
1225
1232
|
async mset(entries) {
|
|
1226
|
-
await Promise.all(
|
|
1233
|
+
await Promise.all(
|
|
1234
|
+
entries.map(({ key, value, ttl }) => this.set(key, value, ttl))
|
|
1235
|
+
);
|
|
1227
1236
|
}
|
|
1228
1237
|
async incr(key, by = 1) {
|
|
1229
1238
|
const current = await this.get(key) || 0;
|
|
@@ -1272,7 +1281,10 @@ var MemoryCache = class {
|
|
|
1272
1281
|
var MemoryStorage = class {
|
|
1273
1282
|
files = /* @__PURE__ */ new Map();
|
|
1274
1283
|
async upload(key, data, options) {
|
|
1275
|
-
this.files.set(key, {
|
|
1284
|
+
this.files.set(key, {
|
|
1285
|
+
data: Buffer.from("mock"),
|
|
1286
|
+
contentType: options?.contentType
|
|
1287
|
+
});
|
|
1276
1288
|
return { url: "memory://" + key };
|
|
1277
1289
|
}
|
|
1278
1290
|
async download(key) {
|
|
@@ -1345,6 +1357,7 @@ var MemoryEmail = class {
|
|
|
1345
1357
|
};
|
|
1346
1358
|
|
|
1347
1359
|
// src/interfaces/IQueue.ts
|
|
1360
|
+
import { randomBytes } from "crypto";
|
|
1348
1361
|
function calculateBackoff(attempt, options) {
|
|
1349
1362
|
if (options.type === "fixed") {
|
|
1350
1363
|
return options.delay;
|
|
@@ -1355,19 +1368,20 @@ function calculateBackoff(attempt, options) {
|
|
|
1355
1368
|
}
|
|
1356
1369
|
function generateJobId() {
|
|
1357
1370
|
const timestamp = Date.now().toString(36);
|
|
1358
|
-
const random =
|
|
1371
|
+
const random = randomBytes(4).toString("hex");
|
|
1359
1372
|
return `job_${timestamp}_${random}`;
|
|
1360
1373
|
}
|
|
1361
1374
|
|
|
1362
1375
|
// src/context/CorrelationContext.ts
|
|
1363
1376
|
import { AsyncLocalStorage } from "async_hooks";
|
|
1377
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
1364
1378
|
var CorrelationContextManager = class {
|
|
1365
1379
|
storage = new AsyncLocalStorage();
|
|
1366
1380
|
idGenerator;
|
|
1367
1381
|
constructor() {
|
|
1368
1382
|
this.idGenerator = () => {
|
|
1369
1383
|
const timestamp = Date.now().toString(36);
|
|
1370
|
-
const random =
|
|
1384
|
+
const random = randomBytes2(4).toString("hex");
|
|
1371
1385
|
return `${timestamp}-${random}`;
|
|
1372
1386
|
};
|
|
1373
1387
|
}
|
|
@@ -1973,16 +1987,19 @@ var MemoryQueue = class {
|
|
|
1973
1987
|
};
|
|
1974
1988
|
|
|
1975
1989
|
// src/adapters/console/ConsoleEmail.ts
|
|
1990
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
1976
1991
|
var ConsoleEmail = class {
|
|
1977
1992
|
sentEmails = [];
|
|
1978
1993
|
async send(message) {
|
|
1979
|
-
const id = `console_${Date.now()}_${
|
|
1994
|
+
const id = `console_${Date.now()}_${randomBytes3(4).toString("hex")}`;
|
|
1980
1995
|
console.log("\n" + "=".repeat(60));
|
|
1981
1996
|
console.log("\u{1F4E7} EMAIL SENT (Console Adapter)");
|
|
1982
1997
|
console.log("=".repeat(60));
|
|
1983
1998
|
console.log(`ID: ${id}`);
|
|
1984
1999
|
console.log(`To: ${this.formatAddresses(message.to)}`);
|
|
1985
|
-
console.log(
|
|
2000
|
+
console.log(
|
|
2001
|
+
`From: ${message.from ? this.formatAddress(message.from) : "(default)"}`
|
|
2002
|
+
);
|
|
1986
2003
|
console.log(`Subject: ${message.subject}`);
|
|
1987
2004
|
if (message.replyTo) {
|
|
1988
2005
|
console.log(`Reply-To: ${this.formatAddress(message.replyTo)}`);
|
|
@@ -1991,15 +2008,21 @@ var ConsoleEmail = class {
|
|
|
1991
2008
|
console.log(`Tags: ${message.tags.join(", ")}`);
|
|
1992
2009
|
}
|
|
1993
2010
|
if (message.attachments && message.attachments.length > 0) {
|
|
1994
|
-
console.log(
|
|
2011
|
+
console.log(
|
|
2012
|
+
`Attachments: ${message.attachments.map((a) => a.filename).join(", ")}`
|
|
2013
|
+
);
|
|
1995
2014
|
}
|
|
1996
2015
|
console.log("-".repeat(60));
|
|
1997
2016
|
if (message.text) {
|
|
1998
2017
|
console.log("TEXT BODY:");
|
|
1999
|
-
console.log(
|
|
2018
|
+
console.log(
|
|
2019
|
+
message.text.slice(0, 500) + (message.text.length > 500 ? "\n...(truncated)" : "")
|
|
2020
|
+
);
|
|
2000
2021
|
}
|
|
2001
2022
|
if (message.html) {
|
|
2002
|
-
console.log(
|
|
2023
|
+
console.log(
|
|
2024
|
+
"HTML BODY: [HTML content - " + message.html.length + " chars]"
|
|
2025
|
+
);
|
|
2003
2026
|
}
|
|
2004
2027
|
console.log("=".repeat(60) + "\n");
|
|
2005
2028
|
this.sentEmails.push(message);
|
|
@@ -2043,6 +2066,7 @@ var ConsoleEmail = class {
|
|
|
2043
2066
|
};
|
|
2044
2067
|
|
|
2045
2068
|
// src/interfaces/ISecrets.ts
|
|
2069
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
2046
2070
|
var EnvSecrets = class {
|
|
2047
2071
|
prefix;
|
|
2048
2072
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -2243,12 +2267,7 @@ var MemorySecrets = class {
|
|
|
2243
2267
|
return true;
|
|
2244
2268
|
}
|
|
2245
2269
|
generateSecureValue(length = 32) {
|
|
2246
|
-
|
|
2247
|
-
let result = "";
|
|
2248
|
-
for (let i = 0; i < length; i++) {
|
|
2249
|
-
result += chars[Math.floor(Math.random() * chars.length)];
|
|
2250
|
-
}
|
|
2251
|
-
return result;
|
|
2270
|
+
return randomBytes4(length).toString("base64url").slice(0, length);
|
|
2252
2271
|
}
|
|
2253
2272
|
/**
|
|
2254
2273
|
* Clear all secrets (for testing)
|
|
@@ -2267,14 +2286,34 @@ var MemorySecrets = class {
|
|
|
2267
2286
|
|
|
2268
2287
|
// src/config.ts
|
|
2269
2288
|
import { z } from "zod";
|
|
2270
|
-
var DatabaseProviderSchema = z.enum([
|
|
2289
|
+
var DatabaseProviderSchema = z.enum([
|
|
2290
|
+
"memory",
|
|
2291
|
+
"postgres",
|
|
2292
|
+
"supabase"
|
|
2293
|
+
]);
|
|
2271
2294
|
var CacheProviderSchema = z.enum(["memory", "redis", "upstash"]);
|
|
2272
|
-
var StorageProviderSchema = z.enum([
|
|
2273
|
-
|
|
2295
|
+
var StorageProviderSchema = z.enum([
|
|
2296
|
+
"memory",
|
|
2297
|
+
"s3",
|
|
2298
|
+
"minio",
|
|
2299
|
+
"r2",
|
|
2300
|
+
"supabase"
|
|
2301
|
+
]);
|
|
2302
|
+
var EmailProviderSchema = z.enum([
|
|
2303
|
+
"memory",
|
|
2304
|
+
"console",
|
|
2305
|
+
"smtp",
|
|
2306
|
+
"resend"
|
|
2307
|
+
]);
|
|
2274
2308
|
var QueueProviderSchema = z.enum(["memory", "bullmq"]);
|
|
2275
2309
|
var TracingProviderSchema = z.enum(["noop", "memory", "otlp"]);
|
|
2276
2310
|
var LogLevelSchema = z.enum(["debug", "info", "warn", "error"]);
|
|
2277
|
-
var AIProviderSchema = z.enum([
|
|
2311
|
+
var AIProviderSchema = z.enum([
|
|
2312
|
+
"memory",
|
|
2313
|
+
"openai",
|
|
2314
|
+
"anthropic",
|
|
2315
|
+
"google"
|
|
2316
|
+
]);
|
|
2278
2317
|
var RAGProviderSchema = z.enum(["memory", "pinecone", "weaviate"]);
|
|
2279
2318
|
var DatabaseConfigSchema = z.object({
|
|
2280
2319
|
provider: DatabaseProviderSchema.default("memory"),
|
|
@@ -2285,7 +2324,10 @@ var DatabaseConfigSchema = z.object({
|
|
|
2285
2324
|
supabaseServiceRoleKey: z.string().optional().describe("Supabase service role key"),
|
|
2286
2325
|
poolSize: z.number().int().min(1).max(100).default(10).describe("Connection pool size"),
|
|
2287
2326
|
connectionTimeout: z.number().int().min(1e3).max(6e4).default(5e3).describe("Connection timeout in ms"),
|
|
2288
|
-
ssl: z.union([
|
|
2327
|
+
ssl: z.union([
|
|
2328
|
+
z.boolean(),
|
|
2329
|
+
z.object({ rejectUnauthorized: z.boolean().optional() })
|
|
2330
|
+
]).optional().describe("SSL configuration")
|
|
2289
2331
|
}).refine(
|
|
2290
2332
|
(data) => {
|
|
2291
2333
|
if (data.provider === "supabase") {
|
|
@@ -2416,7 +2458,9 @@ var RAGConfigSchema = z.object({
|
|
|
2416
2458
|
indexName: z.string().optional().describe("Pinecone index name or Weaviate class"),
|
|
2417
2459
|
namespace: z.string().optional().describe("Default namespace"),
|
|
2418
2460
|
host: z.string().url().optional().describe("Weaviate host URL"),
|
|
2419
|
-
embeddingProvider: AIProviderSchema.default("memory").describe(
|
|
2461
|
+
embeddingProvider: AIProviderSchema.default("memory").describe(
|
|
2462
|
+
"Provider for generating embeddings"
|
|
2463
|
+
),
|
|
2420
2464
|
embeddingApiKey: z.string().optional().describe("API key for embedding provider"),
|
|
2421
2465
|
embeddingModel: z.string().optional().describe("Model for generating embeddings")
|
|
2422
2466
|
}).refine(
|
|
@@ -2433,6 +2477,25 @@ var RAGConfigSchema = z.object({
|
|
|
2433
2477
|
message: "Pinecone requires apiKey and indexName; Weaviate requires host"
|
|
2434
2478
|
}
|
|
2435
2479
|
);
|
|
2480
|
+
var CryptoConfigSchema = z.object({
|
|
2481
|
+
enabled: z.boolean().default(false).describe("Enable field-level encryption"),
|
|
2482
|
+
masterKey: z.string().optional().describe("256-bit master key as hex (64 chars). Required when enabled."),
|
|
2483
|
+
hmacKey: z.string().optional().describe("HMAC key for deterministic hashing (derived from master key if not provided)")
|
|
2484
|
+
}).refine(
|
|
2485
|
+
(data) => {
|
|
2486
|
+
if (data.enabled) {
|
|
2487
|
+
return data.masterKey && data.masterKey.length >= 64;
|
|
2488
|
+
}
|
|
2489
|
+
return true;
|
|
2490
|
+
},
|
|
2491
|
+
{
|
|
2492
|
+
message: "Crypto requires a 256-bit master key (64 hex characters) when enabled"
|
|
2493
|
+
}
|
|
2494
|
+
);
|
|
2495
|
+
var SecurityConfigSchema = z.object({
|
|
2496
|
+
enforceTls: z.boolean().default(true).describe("Enforce TLS for production connections"),
|
|
2497
|
+
tlsWarnOnly: z.boolean().default(false).describe("Warn instead of throwing when TLS is missing in production")
|
|
2498
|
+
});
|
|
2436
2499
|
var RetryConfigSchema = z.object({
|
|
2437
2500
|
enabled: z.boolean().default(true).describe("Enable retry for failed operations"),
|
|
2438
2501
|
maxAttempts: z.number().int().min(1).max(10).default(3).describe("Maximum retry attempts"),
|
|
@@ -2522,6 +2585,10 @@ var PlatformConfigSchema = z.object({
|
|
|
2522
2585
|
// AI configurations
|
|
2523
2586
|
ai: AIConfigSchema.default({ enabled: false }),
|
|
2524
2587
|
rag: RAGConfigSchema.default({ enabled: false }),
|
|
2588
|
+
// Crypto configuration
|
|
2589
|
+
crypto: CryptoConfigSchema.default({ enabled: false }),
|
|
2590
|
+
// Security configuration
|
|
2591
|
+
security: SecurityConfigSchema.default({}),
|
|
2525
2592
|
// Resilience configuration
|
|
2526
2593
|
resilience: ResilienceConfigSchema.default({}),
|
|
2527
2594
|
// Observability configuration
|
|
@@ -2599,6 +2666,15 @@ function loadConfig() {
|
|
|
2599
2666
|
embeddingApiKey: process.env.EMBEDDING_API_KEY || process.env.OPENAI_API_KEY,
|
|
2600
2667
|
embeddingModel: process.env.EMBEDDING_MODEL
|
|
2601
2668
|
},
|
|
2669
|
+
crypto: {
|
|
2670
|
+
enabled: process.env.CRYPTO_ENABLED === "true",
|
|
2671
|
+
masterKey: process.env.CRYPTO_MASTER_KEY,
|
|
2672
|
+
hmacKey: process.env.CRYPTO_HMAC_KEY
|
|
2673
|
+
},
|
|
2674
|
+
security: {
|
|
2675
|
+
enforceTls: process.env.SECURITY_ENFORCE_TLS !== "false",
|
|
2676
|
+
tlsWarnOnly: process.env.SECURITY_TLS_WARN_ONLY === "true"
|
|
2677
|
+
},
|
|
2602
2678
|
resilience: {
|
|
2603
2679
|
retry: {
|
|
2604
2680
|
enabled: process.env.RESILIENCE_RETRY_ENABLED !== "false",
|
|
@@ -2649,6 +2725,7 @@ function loadConfig() {
|
|
|
2649
2725
|
}
|
|
2650
2726
|
|
|
2651
2727
|
// src/interfaces/ITracing.ts
|
|
2728
|
+
import { randomBytes as randomBytes5 } from "crypto";
|
|
2652
2729
|
var MemorySpan = class {
|
|
2653
2730
|
name;
|
|
2654
2731
|
context;
|
|
@@ -2668,7 +2745,7 @@ var MemorySpan = class {
|
|
|
2668
2745
|
};
|
|
2669
2746
|
}
|
|
2670
2747
|
generateSpanId() {
|
|
2671
|
-
return
|
|
2748
|
+
return randomBytes5(8).toString("hex");
|
|
2672
2749
|
}
|
|
2673
2750
|
setAttribute(key, value) {
|
|
2674
2751
|
this._attributes[key] = value;
|
|
@@ -2728,7 +2805,7 @@ var MemoryTracing = class {
|
|
|
2728
2805
|
this.traceId = this.generateTraceId();
|
|
2729
2806
|
}
|
|
2730
2807
|
generateTraceId() {
|
|
2731
|
-
return
|
|
2808
|
+
return randomBytes5(16).toString("hex");
|
|
2732
2809
|
}
|
|
2733
2810
|
startSpan(name, options) {
|
|
2734
2811
|
const span = new MemorySpan(
|
|
@@ -3168,6 +3245,147 @@ var NoopMetrics = class {
|
|
|
3168
3245
|
// src/factory.ts
|
|
3169
3246
|
init_IAI();
|
|
3170
3247
|
init_IRAG();
|
|
3248
|
+
|
|
3249
|
+
// src/adapters/memory/MemoryCrypto.ts
|
|
3250
|
+
import { randomBytes as randomBytes7, createCipheriv, createDecipheriv, createHmac } from "crypto";
|
|
3251
|
+
var MemoryCrypto = class {
|
|
3252
|
+
keys = /* @__PURE__ */ new Map();
|
|
3253
|
+
activeKeyId;
|
|
3254
|
+
hmacKey;
|
|
3255
|
+
constructor(options) {
|
|
3256
|
+
const masterKeyBuf = options?.masterKey ? Buffer.from(options.masterKey, "hex") : randomBytes7(32);
|
|
3257
|
+
this.hmacKey = options?.hmacKey ? Buffer.from(options.hmacKey, "hex") : randomBytes7(32);
|
|
3258
|
+
const keyId = this.generateKeyId();
|
|
3259
|
+
this.keys.set(keyId, {
|
|
3260
|
+
id: keyId,
|
|
3261
|
+
key: masterKeyBuf,
|
|
3262
|
+
status: "active",
|
|
3263
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3264
|
+
});
|
|
3265
|
+
this.activeKeyId = keyId;
|
|
3266
|
+
}
|
|
3267
|
+
async encrypt(plaintext, options) {
|
|
3268
|
+
const keyId = options?.keyId || this.activeKeyId;
|
|
3269
|
+
const stored = this.keys.get(keyId);
|
|
3270
|
+
if (!stored) {
|
|
3271
|
+
throw new Error(`Key not found: ${keyId}`);
|
|
3272
|
+
}
|
|
3273
|
+
if (stored.status === "retired") {
|
|
3274
|
+
throw new Error(`Key is retired: ${keyId}`);
|
|
3275
|
+
}
|
|
3276
|
+
if (stored.status === "decrypt-only" && !options?.keyId) {
|
|
3277
|
+
throw new Error(`Key is decrypt-only: ${keyId}`);
|
|
3278
|
+
}
|
|
3279
|
+
const iv = randomBytes7(12);
|
|
3280
|
+
const cipher = createCipheriv("aes-256-gcm", stored.key, iv);
|
|
3281
|
+
if (options?.aad) {
|
|
3282
|
+
cipher.setAAD(Buffer.from(options.aad, "utf8"));
|
|
3283
|
+
}
|
|
3284
|
+
const encrypted = Buffer.concat([
|
|
3285
|
+
cipher.update(plaintext, "utf8"),
|
|
3286
|
+
cipher.final()
|
|
3287
|
+
]);
|
|
3288
|
+
const tag = cipher.getAuthTag();
|
|
3289
|
+
return {
|
|
3290
|
+
ciphertext: encrypted.toString("base64"),
|
|
3291
|
+
iv: iv.toString("base64"),
|
|
3292
|
+
tag: tag.toString("base64"),
|
|
3293
|
+
keyId,
|
|
3294
|
+
algorithm: "aes-256-gcm",
|
|
3295
|
+
version: 1
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
async decrypt(field, options) {
|
|
3299
|
+
const stored = this.keys.get(field.keyId);
|
|
3300
|
+
if (!stored) {
|
|
3301
|
+
throw new Error(`Key not found: ${field.keyId}`);
|
|
3302
|
+
}
|
|
3303
|
+
if (stored.status === "retired") {
|
|
3304
|
+
throw new Error(`Key is retired and cannot decrypt: ${field.keyId}`);
|
|
3305
|
+
}
|
|
3306
|
+
const decipher = createDecipheriv(
|
|
3307
|
+
"aes-256-gcm",
|
|
3308
|
+
stored.key,
|
|
3309
|
+
Buffer.from(field.iv, "base64")
|
|
3310
|
+
);
|
|
3311
|
+
decipher.setAuthTag(Buffer.from(field.tag, "base64"));
|
|
3312
|
+
if (options?.aad) {
|
|
3313
|
+
decipher.setAAD(Buffer.from(options.aad, "utf8"));
|
|
3314
|
+
}
|
|
3315
|
+
const decrypted = Buffer.concat([
|
|
3316
|
+
decipher.update(Buffer.from(field.ciphertext, "base64")),
|
|
3317
|
+
decipher.final()
|
|
3318
|
+
]);
|
|
3319
|
+
return decrypted.toString("utf8");
|
|
3320
|
+
}
|
|
3321
|
+
async encryptDeterministic(plaintext, options) {
|
|
3322
|
+
const hash = await this.computeHash(plaintext);
|
|
3323
|
+
const encrypted = await this.encrypt(plaintext, options);
|
|
3324
|
+
return { hash, encrypted };
|
|
3325
|
+
}
|
|
3326
|
+
async computeHash(plaintext) {
|
|
3327
|
+
return createHmac("sha256", this.hmacKey).update(plaintext, "utf8").digest("hex");
|
|
3328
|
+
}
|
|
3329
|
+
async encryptBatch(fields, options) {
|
|
3330
|
+
const result = {};
|
|
3331
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
3332
|
+
result[key] = await this.encrypt(value, options);
|
|
3333
|
+
}
|
|
3334
|
+
return result;
|
|
3335
|
+
}
|
|
3336
|
+
async decryptBatch(fields, options) {
|
|
3337
|
+
const result = {};
|
|
3338
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
3339
|
+
result[key] = await this.decrypt(value, options);
|
|
3340
|
+
}
|
|
3341
|
+
return result;
|
|
3342
|
+
}
|
|
3343
|
+
async rotateKey() {
|
|
3344
|
+
const previousKeyId = this.activeKeyId;
|
|
3345
|
+
const currentKey = this.keys.get(previousKeyId);
|
|
3346
|
+
if (currentKey) {
|
|
3347
|
+
currentKey.status = "decrypt-only";
|
|
3348
|
+
}
|
|
3349
|
+
const newKeyId = this.generateKeyId();
|
|
3350
|
+
this.keys.set(newKeyId, {
|
|
3351
|
+
id: newKeyId,
|
|
3352
|
+
key: randomBytes7(32),
|
|
3353
|
+
status: "active",
|
|
3354
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3355
|
+
});
|
|
3356
|
+
this.activeKeyId = newKeyId;
|
|
3357
|
+
return { newKeyId, previousKeyId };
|
|
3358
|
+
}
|
|
3359
|
+
async reEncrypt(field, options) {
|
|
3360
|
+
const plaintext = await this.decrypt(field);
|
|
3361
|
+
return this.encrypt(plaintext, options);
|
|
3362
|
+
}
|
|
3363
|
+
async listKeys() {
|
|
3364
|
+
return Array.from(this.keys.values()).map((k) => ({
|
|
3365
|
+
keyId: k.id,
|
|
3366
|
+
createdAt: k.createdAt,
|
|
3367
|
+
status: k.status
|
|
3368
|
+
}));
|
|
3369
|
+
}
|
|
3370
|
+
async getActiveKeyId() {
|
|
3371
|
+
return this.activeKeyId;
|
|
3372
|
+
}
|
|
3373
|
+
async healthCheck() {
|
|
3374
|
+
try {
|
|
3375
|
+
const testPlain = "health-check-test";
|
|
3376
|
+
const encrypted = await this.encrypt(testPlain);
|
|
3377
|
+
const decrypted = await this.decrypt(encrypted);
|
|
3378
|
+
return decrypted === testPlain;
|
|
3379
|
+
} catch {
|
|
3380
|
+
return false;
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
generateKeyId() {
|
|
3384
|
+
return `key_${randomBytes7(8).toString("hex")}`;
|
|
3385
|
+
}
|
|
3386
|
+
};
|
|
3387
|
+
|
|
3388
|
+
// src/factory.ts
|
|
3171
3389
|
function createLogger(config) {
|
|
3172
3390
|
if (!config.observability.logging) {
|
|
3173
3391
|
return new NoopLogger();
|
|
@@ -3202,9 +3420,22 @@ function createPlatform(config) {
|
|
|
3202
3420
|
const tracing = finalConfig.observability.tracing.provider === "memory" ? new MemoryTracing() : new NoopTracing();
|
|
3203
3421
|
const ai = finalConfig.ai.enabled ? new MemoryAI() : null;
|
|
3204
3422
|
const rag = finalConfig.rag.enabled ? new MemoryRAG() : null;
|
|
3205
|
-
|
|
3423
|
+
const crypto2 = finalConfig.crypto.enabled ? new MemoryCrypto() : null;
|
|
3424
|
+
return createPlatformFromAdapters(
|
|
3425
|
+
db,
|
|
3426
|
+
cache,
|
|
3427
|
+
storage,
|
|
3428
|
+
email,
|
|
3429
|
+
queue,
|
|
3430
|
+
logger,
|
|
3431
|
+
metrics,
|
|
3432
|
+
tracing,
|
|
3433
|
+
ai,
|
|
3434
|
+
rag,
|
|
3435
|
+
crypto2
|
|
3436
|
+
);
|
|
3206
3437
|
}
|
|
3207
|
-
function createPlatformFromAdapters(db, cache, storage, email, queue, logger, metrics, tracing, ai, rag) {
|
|
3438
|
+
function createPlatformFromAdapters(db, cache, storage, email, queue, logger, metrics, tracing, ai, rag, crypto2) {
|
|
3208
3439
|
const platform = {
|
|
3209
3440
|
db,
|
|
3210
3441
|
cache,
|
|
@@ -3215,7 +3446,14 @@ function createPlatformFromAdapters(db, cache, storage, email, queue, logger, me
|
|
|
3215
3446
|
metrics,
|
|
3216
3447
|
tracing,
|
|
3217
3448
|
async healthCheck() {
|
|
3218
|
-
const [
|
|
3449
|
+
const [
|
|
3450
|
+
dbHealth,
|
|
3451
|
+
cacheHealth,
|
|
3452
|
+
storageHealth,
|
|
3453
|
+
emailHealth,
|
|
3454
|
+
queueHealth,
|
|
3455
|
+
tracingHealth
|
|
3456
|
+
] = await Promise.all([
|
|
3219
3457
|
db.healthCheck(),
|
|
3220
3458
|
cache.healthCheck(),
|
|
3221
3459
|
storage.healthCheck(),
|
|
@@ -3237,7 +3475,12 @@ function createPlatformFromAdapters(db, cache, storage, email, queue, logger, me
|
|
|
3237
3475
|
};
|
|
3238
3476
|
},
|
|
3239
3477
|
async close() {
|
|
3240
|
-
await Promise.all([
|
|
3478
|
+
await Promise.all([
|
|
3479
|
+
db.close(),
|
|
3480
|
+
cache.close(),
|
|
3481
|
+
queue.close(),
|
|
3482
|
+
tracing.close()
|
|
3483
|
+
]);
|
|
3241
3484
|
}
|
|
3242
3485
|
};
|
|
3243
3486
|
if (ai) {
|
|
@@ -3246,6 +3489,9 @@ function createPlatformFromAdapters(db, cache, storage, email, queue, logger, me
|
|
|
3246
3489
|
if (rag) {
|
|
3247
3490
|
platform.rag = rag;
|
|
3248
3491
|
}
|
|
3492
|
+
if (crypto2) {
|
|
3493
|
+
platform.crypto = crypto2;
|
|
3494
|
+
}
|
|
3249
3495
|
return platform;
|
|
3250
3496
|
}
|
|
3251
3497
|
function deepMerge(target, source) {
|
|
@@ -3304,7 +3550,11 @@ function createTestPlatformWithInternals() {
|
|
|
3304
3550
|
};
|
|
3305
3551
|
},
|
|
3306
3552
|
async close() {
|
|
3307
|
-
await Promise.all([
|
|
3553
|
+
await Promise.all([
|
|
3554
|
+
memoryDb.close(),
|
|
3555
|
+
memoryCache.close(),
|
|
3556
|
+
memoryQueue.close()
|
|
3557
|
+
]);
|
|
3308
3558
|
}
|
|
3309
3559
|
};
|
|
3310
3560
|
return {
|
|
@@ -3344,15 +3594,20 @@ function assertEmailSent(memoryEmail, expected) {
|
|
|
3344
3594
|
const matchesTo = (emailTo, expectedTo) => {
|
|
3345
3595
|
if (!emailTo) return false;
|
|
3346
3596
|
if (Array.isArray(emailTo)) {
|
|
3347
|
-
return emailTo.some(
|
|
3597
|
+
return emailTo.some(
|
|
3598
|
+
(addr) => addr === expectedTo || typeof addr === "object" && addr.email === expectedTo
|
|
3599
|
+
);
|
|
3348
3600
|
}
|
|
3349
3601
|
return emailTo === expectedTo || typeof emailTo === "object" && emailTo.email === expectedTo;
|
|
3350
3602
|
};
|
|
3351
3603
|
const found = emails.find((email) => {
|
|
3352
|
-
if (expected.to && !matchesTo(email.to, expected.to))
|
|
3604
|
+
if (expected.to && !matchesTo(email.to, expected.to))
|
|
3605
|
+
return false;
|
|
3353
3606
|
if (expected.subject) {
|
|
3354
|
-
if (typeof expected.subject === "string" && email.subject !== expected.subject)
|
|
3355
|
-
|
|
3607
|
+
if (typeof expected.subject === "string" && email.subject !== expected.subject)
|
|
3608
|
+
return false;
|
|
3609
|
+
if (expected.subject instanceof RegExp && !expected.subject.test(email.subject))
|
|
3610
|
+
return false;
|
|
3356
3611
|
}
|
|
3357
3612
|
if (expected.bodyContains) {
|
|
3358
3613
|
const body = email.html || email.text || "";
|