@volidator/node 1.0.1 → 1.0.2
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/LICENSE +21 -0
- package/README.md +72 -2
- package/dist/{chunk-UUUKNFQ6.js → chunk-AJL3OYF2.js} +7 -3
- package/dist/chunk-AJL3OYF2.js.map +1 -0
- package/dist/{chunk-YI4DCCHT.js → chunk-UDBMJC25.js} +207 -26
- package/dist/chunk-UDBMJC25.js.map +1 -0
- package/dist/index.cjs +206 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +91 -3
- package/dist/index.d.ts +91 -3
- package/dist/index.js +1 -1
- package/dist/middleware/next.cjs +211 -26
- package/dist/middleware/next.cjs.map +1 -1
- package/dist/middleware/next.d.cts +2 -0
- package/dist/middleware/next.d.ts +2 -0
- package/dist/middleware/next.js +2 -2
- package/dist/plugins/agent-langchain.d.cts +2 -0
- package/dist/plugins/agent-langchain.d.ts +2 -0
- package/dist/plugins/agent-vercel.d.cts +2 -0
- package/dist/plugins/agent-vercel.d.ts +2 -0
- package/dist/plugins/clerk.cjs +211 -26
- package/dist/plugins/clerk.cjs.map +1 -1
- package/dist/plugins/clerk.d.cts +2 -0
- package/dist/plugins/clerk.d.ts +2 -0
- package/dist/plugins/clerk.js +2 -2
- package/dist/plugins/universal.cjs +211 -26
- package/dist/plugins/universal.cjs.map +1 -1
- package/dist/plugins/universal.d.cts +2 -0
- package/dist/plugins/universal.d.ts +2 -0
- package/dist/plugins/universal.js +2 -2
- package/package.json +3 -3
- package/dist/chunk-UUUKNFQ6.js.map +0 -1
- package/dist/chunk-YI4DCCHT.js.map +0 -1
package/dist/middleware/next.cjs
CHANGED
|
@@ -25,8 +25,10 @@ __export(next_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(next_exports);
|
|
26
26
|
|
|
27
27
|
// src/index.ts
|
|
28
|
-
var
|
|
28
|
+
var import_node_async_hooks = require("async_hooks");
|
|
29
|
+
var _VolidatorClient = class _VolidatorClient {
|
|
29
30
|
constructor(config) {
|
|
31
|
+
this.fallbackLogicalClock = 0;
|
|
30
32
|
this.apiKey = config.apiKey;
|
|
31
33
|
this.endpoint = config.endpoint || "https://ingestion.volidator.com";
|
|
32
34
|
this.projectId = config.projectId;
|
|
@@ -34,6 +36,8 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
34
36
|
this.redactKeys = config.redactKeys || [];
|
|
35
37
|
this.referenceKeys = config.referenceKeys || [];
|
|
36
38
|
this.maxMetadataSize = config.maxMetadataSize || 10240;
|
|
39
|
+
this.maxRetries = config.maxRetries !== void 0 ? config.maxRetries : 3;
|
|
40
|
+
this.onDeliveryFailure = config.onDeliveryFailure;
|
|
37
41
|
if (config.keyring && config.activeEncryptionKeyId) {
|
|
38
42
|
this.keyring = config.keyring;
|
|
39
43
|
this.activeKeyId = config.activeEncryptionKeyId;
|
|
@@ -54,6 +58,22 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
54
58
|
this.compliance = new VolidatorCompliance(this);
|
|
55
59
|
this.agent = new VolidatorAgent(this);
|
|
56
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Increments and returns the current Lamport Logical Clock counter.
|
|
63
|
+
* If a logicalClockStore context is active, it reads, syncs, and updates
|
|
64
|
+
* the thread-scoped clock; otherwise, it falls back to a global instance-scoped counter.
|
|
65
|
+
*
|
|
66
|
+
* @param incomingClock Optional logical clock sequence from incoming trace parent header
|
|
67
|
+
*/
|
|
68
|
+
getAndIncrementClock(incomingClock) {
|
|
69
|
+
const store = _VolidatorClient.logicalClockStore.getStore();
|
|
70
|
+
if (store) {
|
|
71
|
+
store.clock = Math.max(store.clock, incomingClock || 0) + 1;
|
|
72
|
+
return store.clock;
|
|
73
|
+
}
|
|
74
|
+
this.fallbackLogicalClock = Math.max(this.fallbackLogicalClock, incomingClock || 0) + 1;
|
|
75
|
+
return this.fallbackLogicalClock;
|
|
76
|
+
}
|
|
57
77
|
// ---------------------------------------------------------------------------
|
|
58
78
|
// Telemetry Context Parser (Zero-latency header parsing)
|
|
59
79
|
// ---------------------------------------------------------------------------
|
|
@@ -95,11 +115,23 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
95
115
|
}
|
|
96
116
|
return "";
|
|
97
117
|
};
|
|
118
|
+
const result = {};
|
|
119
|
+
const clockVal = getHeader("x-volidator-clock");
|
|
120
|
+
if (clockVal) {
|
|
121
|
+
const parsed = parseInt(clockVal, 10);
|
|
122
|
+
if (!isNaN(parsed)) {
|
|
123
|
+
result.logicalClock = parsed;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
98
126
|
const traceparent = getHeader("traceparent");
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
127
|
+
if (traceparent) {
|
|
128
|
+
const parts = traceparent.split("-");
|
|
129
|
+
if (parts.length === 4) {
|
|
130
|
+
result.traceId = parts[1];
|
|
131
|
+
result.spanId = parts[2];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
103
135
|
}
|
|
104
136
|
static resolveTelemetryConfig(config) {
|
|
105
137
|
const preset = config.preset || "standard";
|
|
@@ -222,6 +254,12 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
222
254
|
// prepareLogEntry() — Prepares, redacts, and encrypts a single log entry payload
|
|
223
255
|
// ---------------------------------------------------------------------------
|
|
224
256
|
async prepareLogEntry(payload, maxMetaOverride) {
|
|
257
|
+
const PAYLOAD_5MB_LIMIT = 5 * 1024 * 1024;
|
|
258
|
+
const metadata = payload.metadata || {};
|
|
259
|
+
const payloadSize = new TextEncoder().encode(JSON.stringify(metadata)).length;
|
|
260
|
+
if (payloadSize > PAYLOAD_5MB_LIMIT) {
|
|
261
|
+
throw new Error(`Volidator SDK Error: Audit log payload exceeds the 5MB hard limit (${payloadSize} bytes).`);
|
|
262
|
+
}
|
|
225
263
|
const actorRaw = payload.actor || payload.actorId || "unknown";
|
|
226
264
|
const targetRaw = payload.target || payload.targetId || "unknown";
|
|
227
265
|
const tenantRaw = payload.tenant || payload.tenantId || "";
|
|
@@ -303,13 +341,20 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
303
341
|
safeMetadata[k] = v;
|
|
304
342
|
}
|
|
305
343
|
}
|
|
344
|
+
let didTruncateDepth = false;
|
|
345
|
+
let didTruncateString = false;
|
|
306
346
|
const limitDepth = (obj, currentDepth = 1) => {
|
|
307
347
|
if (currentDepth > 5) {
|
|
348
|
+
didTruncateDepth = true;
|
|
308
349
|
return "[Truncated - Depth Exceeded]";
|
|
309
350
|
}
|
|
310
351
|
if (typeof obj !== "object" || obj === null) {
|
|
311
352
|
if (typeof obj === "string") {
|
|
312
|
-
|
|
353
|
+
const truncatedVal = truncate(obj, 1e3);
|
|
354
|
+
if (truncatedVal.length !== obj.length) {
|
|
355
|
+
didTruncateString = true;
|
|
356
|
+
}
|
|
357
|
+
return truncatedVal;
|
|
313
358
|
}
|
|
314
359
|
return obj;
|
|
315
360
|
}
|
|
@@ -323,6 +368,14 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
323
368
|
return result;
|
|
324
369
|
};
|
|
325
370
|
const processedMetadata = limitDepth(safeMetadata);
|
|
371
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
|
|
372
|
+
if (didTruncateDepth) {
|
|
373
|
+
console.warn("[Volidator] Warning: Log metadata exceeded maximum depth limit (5) and was truncated.");
|
|
374
|
+
}
|
|
375
|
+
if (didTruncateString) {
|
|
376
|
+
console.warn("[Volidator] Warning: One or more log metadata string values exceeded the 1000-character limit and were truncated.");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
326
379
|
const serializedMeta = JSON.stringify(processedMetadata);
|
|
327
380
|
const maxMetaLimit = maxMetaOverride || this.maxMetadataSize;
|
|
328
381
|
if (serializedMeta.length > maxMetaLimit) {
|
|
@@ -346,6 +399,7 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
346
399
|
let traceId = payload.traceId;
|
|
347
400
|
let spanId = payload.spanId;
|
|
348
401
|
let parentSpanId = payload.parentSpanId;
|
|
402
|
+
let logicalClock = payload.logicalClock;
|
|
349
403
|
if (payload.req) {
|
|
350
404
|
const extractedTrace = _VolidatorClient.extractTraceContext(payload.req);
|
|
351
405
|
if (!traceId && extractedTrace.traceId) {
|
|
@@ -354,7 +408,11 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
354
408
|
if (!spanId && extractedTrace.spanId) {
|
|
355
409
|
spanId = extractedTrace.spanId;
|
|
356
410
|
}
|
|
411
|
+
if (logicalClock === void 0 && extractedTrace.logicalClock !== void 0) {
|
|
412
|
+
logicalClock = extractedTrace.logicalClock;
|
|
413
|
+
}
|
|
357
414
|
}
|
|
415
|
+
const resolvedClock = this.getAndIncrementClock(logicalClock);
|
|
358
416
|
if (spanId) {
|
|
359
417
|
enrichedPayload.spanId = spanId;
|
|
360
418
|
}
|
|
@@ -367,14 +425,39 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
367
425
|
const targetBlindIndex = await this.generateBlindIndex(target, activeKeyBuffer);
|
|
368
426
|
const tenantBlindIndex = tenant ? await this.generateBlindIndex(tenant, activeKeyBuffer) : void 0;
|
|
369
427
|
const traceBlindIndex = traceId ? await this.generateBlindIndex(traceId, activeKeyBuffer) : void 0;
|
|
370
|
-
|
|
428
|
+
let encryptedPayload = await this.encryptPayload(enrichedPayload);
|
|
429
|
+
let isClaimCheck = false;
|
|
430
|
+
if (encryptedPayload.length > 30720) {
|
|
431
|
+
const binaryBuffer = new TextEncoder().encode(encryptedPayload);
|
|
432
|
+
const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", binaryBuffer);
|
|
433
|
+
const hashHex = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
434
|
+
try {
|
|
435
|
+
const uploadRes = await fetch(`${this.endpoint}/v1/log/upload/${hashHex}`, {
|
|
436
|
+
method: "PUT",
|
|
437
|
+
headers: {
|
|
438
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
439
|
+
"Content-Type": "application/octet-stream"
|
|
440
|
+
},
|
|
441
|
+
body: encryptedPayload
|
|
442
|
+
});
|
|
443
|
+
if (!uploadRes.ok) {
|
|
444
|
+
throw new Error(`Upload failed with status: ${uploadRes.status}`);
|
|
445
|
+
}
|
|
446
|
+
encryptedPayload = hashHex;
|
|
447
|
+
isClaimCheck = true;
|
|
448
|
+
} catch (err) {
|
|
449
|
+
console.error(`[Volidator] Claim check upload failed: ${err.message}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
371
452
|
return {
|
|
372
453
|
actorBlindIndex,
|
|
373
454
|
actionBlindIndex,
|
|
374
455
|
targetBlindIndex,
|
|
375
456
|
tenantBlindIndex,
|
|
376
457
|
traceBlindIndex,
|
|
377
|
-
encryptedPayload
|
|
458
|
+
encryptedPayload,
|
|
459
|
+
logicalClock: resolvedClock,
|
|
460
|
+
isClaimCheck
|
|
378
461
|
};
|
|
379
462
|
}
|
|
380
463
|
// ---------------------------------------------------------------------------
|
|
@@ -382,20 +465,44 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
382
465
|
// ---------------------------------------------------------------------------
|
|
383
466
|
async log(payload, maxMetaOverride) {
|
|
384
467
|
const entry = await this.prepareLogEntry(payload, maxMetaOverride);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
468
|
+
let attempt = 0;
|
|
469
|
+
let delay = 500;
|
|
470
|
+
let lastError = new Error("Unknown error");
|
|
471
|
+
while (attempt <= this.maxRetries) {
|
|
472
|
+
try {
|
|
473
|
+
const res = await fetch(`${this.endpoint}/v1/log`, {
|
|
474
|
+
method: "POST",
|
|
475
|
+
headers: {
|
|
476
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
477
|
+
"Content-Type": "application/json"
|
|
478
|
+
},
|
|
479
|
+
body: JSON.stringify(entry)
|
|
480
|
+
});
|
|
481
|
+
if (res.ok) {
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
lastError = new Error(`Server returned status ${res.status}`);
|
|
485
|
+
if (res.status >= 400 && res.status < 500) {
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
} catch (err) {
|
|
489
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
490
|
+
}
|
|
491
|
+
attempt++;
|
|
492
|
+
if (attempt <= this.maxRetries) {
|
|
493
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
494
|
+
delay *= 3;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
console.error(`[Volidator] Failed to send log after ${attempt} attempts: ${lastError.message}`);
|
|
498
|
+
if (this.onDeliveryFailure) {
|
|
499
|
+
try {
|
|
500
|
+
this.onDeliveryFailure(payload, lastError);
|
|
501
|
+
} catch (cbErr) {
|
|
502
|
+
console.error(`[Volidator] Error in onDeliveryFailure callback: ${cbErr.message}`);
|
|
503
|
+
}
|
|
398
504
|
}
|
|
505
|
+
return false;
|
|
399
506
|
}
|
|
400
507
|
// ---------------------------------------------------------------------------
|
|
401
508
|
// logBatch() — Bulk encrypt and ingest a batch of up to 100 audit events
|
|
@@ -442,12 +549,79 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
442
549
|
}
|
|
443
550
|
}
|
|
444
551
|
// ---------------------------------------------------------------------------
|
|
552
|
+
// batcher() — Convenience batcher for buffered log ingestion
|
|
553
|
+
// ---------------------------------------------------------------------------
|
|
554
|
+
/**
|
|
555
|
+
* Creates a convenience batcher instance for buffering and ingestion.
|
|
556
|
+
*
|
|
557
|
+
* ⚠️ SERVERLESS/EDGE CAVEAT:
|
|
558
|
+
* autoFlushInterval uses setInterval internally. In serverless/edge environments
|
|
559
|
+
* (e.g. Cloudflare Workers, Vercel Edge), the V8 isolate is frozen or destroyed
|
|
560
|
+
* once the response is sent. Background timers will silently fail to fire, leading
|
|
561
|
+
* to dropped logs. Only use autoFlushInterval in long-lived Node.js processes.
|
|
562
|
+
* In serverless/edge environments, always call await batcher.flush() explicitly
|
|
563
|
+
* before returning the response, or wrap it in ctx.waitUntil().
|
|
564
|
+
*/
|
|
565
|
+
batcher(options) {
|
|
566
|
+
const client = this;
|
|
567
|
+
let buffer = [];
|
|
568
|
+
let intervalId = null;
|
|
569
|
+
const flush = async () => {
|
|
570
|
+
if (intervalId) {
|
|
571
|
+
clearInterval(intervalId);
|
|
572
|
+
intervalId = null;
|
|
573
|
+
}
|
|
574
|
+
if (buffer.length === 0) {
|
|
575
|
+
if (options?.autoFlushInterval) {
|
|
576
|
+
startTimer();
|
|
577
|
+
}
|
|
578
|
+
return { accepted: 0, rejected: 0 };
|
|
579
|
+
}
|
|
580
|
+
const payloadsToFlush = [...buffer];
|
|
581
|
+
buffer = [];
|
|
582
|
+
if (options?.autoFlushInterval) {
|
|
583
|
+
startTimer();
|
|
584
|
+
}
|
|
585
|
+
return client.logBatch(payloadsToFlush);
|
|
586
|
+
};
|
|
587
|
+
const startTimer = () => {
|
|
588
|
+
if (options?.autoFlushInterval && !intervalId) {
|
|
589
|
+
intervalId = setInterval(() => {
|
|
590
|
+
flush().catch((err) => {
|
|
591
|
+
console.error(`[Volidator] Batcher auto-flush failed: ${err.message}`);
|
|
592
|
+
});
|
|
593
|
+
}, options.autoFlushInterval);
|
|
594
|
+
if (intervalId && typeof intervalId.unref === "function") {
|
|
595
|
+
intervalId.unref();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
const push = (payload) => {
|
|
600
|
+
buffer.push(payload);
|
|
601
|
+
const maxCount = options?.autoFlushCount || 100;
|
|
602
|
+
if (buffer.length >= Math.min(maxCount, 100)) {
|
|
603
|
+
flush().catch((err) => {
|
|
604
|
+
console.error(`[Volidator] Batcher auto-flush on count failed: ${err.message}`);
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
const size = () => buffer.length;
|
|
609
|
+
startTimer();
|
|
610
|
+
return {
|
|
611
|
+
push,
|
|
612
|
+
flush,
|
|
613
|
+
size
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
// ---------------------------------------------------------------------------
|
|
445
617
|
// generateEmbedToken() — Sign a HS256 JWT for the embeddable dashboard widget
|
|
446
618
|
// ---------------------------------------------------------------------------
|
|
447
619
|
async generateEmbedToken(config) {
|
|
448
|
-
|
|
620
|
+
const projectId = config.projectId || this.projectId;
|
|
621
|
+
const clientSecret = config.clientSecret || this.clientSecret;
|
|
622
|
+
if (!projectId || !clientSecret) {
|
|
449
623
|
throw new Error(
|
|
450
|
-
"generateEmbedToken() requires projectId and clientSecret in the VolidatorClient constructor."
|
|
624
|
+
"generateEmbedToken() requires projectId and clientSecret to be provided in either the configuration or the VolidatorClient constructor."
|
|
451
625
|
);
|
|
452
626
|
}
|
|
453
627
|
const {
|
|
@@ -474,7 +648,7 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
474
648
|
const expiresInSeconds = Math.min(parsedExpiry, maxExpiry);
|
|
475
649
|
const now = Math.floor(Date.now() / 1e3);
|
|
476
650
|
const payload = {
|
|
477
|
-
pid:
|
|
651
|
+
pid: projectId,
|
|
478
652
|
scope: defaultScope,
|
|
479
653
|
iat: now,
|
|
480
654
|
exp: now + expiresInSeconds
|
|
@@ -507,7 +681,7 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
507
681
|
}
|
|
508
682
|
payload.view = compressed;
|
|
509
683
|
}
|
|
510
|
-
const token = await this.signHS256JWT(payload,
|
|
684
|
+
const token = await this.signHS256JWT(payload, clientSecret);
|
|
511
685
|
const keyringString = Object.entries(this.keyring).map(([id, key]) => `${id}:${key}`).join(",");
|
|
512
686
|
const hostParam = hostOrigin ? `?host=${encodeURIComponent(hostOrigin)}` : "";
|
|
513
687
|
const embedUrl = `${dashboardUrl}/embed/${token}${hostParam}#${keyringString}`;
|
|
@@ -556,6 +730,13 @@ var VolidatorClient = class _VolidatorClient {
|
|
|
556
730
|
}
|
|
557
731
|
}
|
|
558
732
|
};
|
|
733
|
+
/**
|
|
734
|
+
* Thread-local asynchronous context storage used to maintain
|
|
735
|
+
* and increment Lamport logical clock sequences across execution boundaries
|
|
736
|
+
* (e.g. within API requests or serverless handler contexts).
|
|
737
|
+
*/
|
|
738
|
+
_VolidatorClient.logicalClockStore = new import_node_async_hooks.AsyncLocalStorage();
|
|
739
|
+
var VolidatorClient = _VolidatorClient;
|
|
559
740
|
var VolidatorCompliance = class {
|
|
560
741
|
constructor(client) {
|
|
561
742
|
this.client = client;
|
|
@@ -685,6 +866,8 @@ var VolidatorAgent = class {
|
|
|
685
866
|
function withVolidator(client, handler) {
|
|
686
867
|
return async (req, ctx = {}, ...args) => {
|
|
687
868
|
const extractedContext = VolidatorClient.extractContext(req);
|
|
869
|
+
const extractedTrace = VolidatorClient.extractTraceContext(req);
|
|
870
|
+
const incomingClock = extractedTrace.logicalClock || 0;
|
|
688
871
|
const scopedLog = async (payload) => {
|
|
689
872
|
return client.log({
|
|
690
873
|
...payload,
|
|
@@ -725,7 +908,9 @@ function withVolidator(client, handler) {
|
|
|
725
908
|
compliance: scopedCompliance
|
|
726
909
|
}
|
|
727
910
|
};
|
|
728
|
-
return
|
|
911
|
+
return VolidatorClient.logicalClockStore.run({ clock: incomingClock }, () => {
|
|
912
|
+
return handler(req, newCtx, ...args);
|
|
913
|
+
});
|
|
729
914
|
};
|
|
730
915
|
}
|
|
731
916
|
// Annotate the CommonJS export names for ESM import in node:
|