universal-llm-client 4.2.0 → 4.5.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/CHANGELOG.md +142 -103
- package/LICENSE +21 -21
- package/README.md +640 -591
- package/dist/ai-model.d.ts +12 -1
- package/dist/ai-model.d.ts.map +1 -1
- package/dist/ai-model.js +36 -1
- package/dist/ai-model.js.map +1 -1
- package/dist/gemma-channel.d.ts +14 -0
- package/dist/gemma-channel.d.ts.map +1 -0
- package/dist/gemma-channel.js +38 -0
- package/dist/gemma-channel.js.map +1 -0
- package/dist/gemma-diffusion.d.ts +49 -0
- package/dist/gemma-diffusion.d.ts.map +1 -0
- package/dist/gemma-diffusion.js +147 -0
- package/dist/gemma-diffusion.js.map +1 -0
- package/dist/http.d.ts +4 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +14 -1
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +183 -7
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +28 -3
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/google.d.ts +22 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +225 -13
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/ollama.d.ts +2 -0
- package/dist/providers/ollama.d.ts.map +1 -1
- package/dist/providers/ollama.js +59 -30
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.d.ts +14 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +200 -22
- package/dist/providers/openai.js.map +1 -1
- package/dist/router.d.ts +2 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +4 -0
- package/dist/router.js.map +1 -1
- package/dist/stream-decoder.d.ts +12 -0
- package/dist/stream-decoder.d.ts.map +1 -1
- package/dist/stream-decoder.js +182 -5
- package/dist/stream-decoder.js.map +1 -1
- package/dist/thinking.d.ts +36 -0
- package/dist/thinking.d.ts.map +1 -0
- package/dist/thinking.js +52 -0
- package/dist/thinking.js.map +1 -0
- package/package.json +118 -116
- package/src/ai-model.ts +400 -350
- package/src/auditor.ts +213 -213
- package/src/client.ts +402 -402
- package/src/debug/debug-google-streaming.ts +1 -1
- package/src/demos/basic/universal-llm-examples.ts +3 -3
- package/src/demos/diffusion-gemma/.env +29 -0
- package/src/demos/diffusion-gemma/.env.example +27 -0
- package/src/demos/diffusion-gemma/CLAUDE.md +95 -0
- package/src/demos/diffusion-gemma/README.md +59 -0
- package/src/demos/diffusion-gemma/canvas.ts +1606 -0
- package/src/demos/diffusion-gemma/docker-compose.yml +29 -0
- package/src/demos/diffusion-gemma/probe-stream.ts +51 -0
- package/src/demos/diffusion-gemma/probe-tools.ts +55 -0
- package/src/demos/diffusion-gemma/server.ts +1205 -0
- package/src/demos/diffusion-gemma/start-vllm.sh +98 -0
- package/src/gemma-channel.ts +47 -0
- package/src/gemma-diffusion.ts +167 -0
- package/src/http.ts +261 -247
- package/src/index.ts +180 -161
- package/src/interfaces.ts +843 -657
- package/src/mcp.ts +345 -345
- package/src/providers/anthropic.ts +796 -762
- package/src/providers/google.ts +840 -620
- package/src/providers/index.ts +8 -8
- package/src/providers/ollama.ts +503 -469
- package/src/providers/openai.ts +587 -392
- package/src/router.ts +785 -780
- package/src/stream-decoder.ts +535 -361
- package/src/structured-output.ts +759 -759
- package/src/test-scripts/test-google-deep-research.ts +33 -0
- package/src/test-scripts/test-google-streaming-enhanced.ts +147 -147
- package/src/test-scripts/test-google-streaming.ts +1 -1
- package/src/test-scripts/test-google-system-prompt-comprehensive.ts +189 -189
- package/src/test-scripts/test-google-thinking.ts +46 -0
- package/src/test-scripts/test-system-message-positions.ts +163 -163
- package/src/test-scripts/test-system-prompt-improvement-demo.ts +83 -83
- package/src/test-scripts/test-vllm-qwen36.ts +256 -0
- package/src/tests/ai-model.test.ts +1614 -1614
- package/src/tests/auditor.test.ts +224 -224
- package/src/tests/gemma-diffusion.test.ts +115 -0
- package/src/tests/http.test.ts +200 -200
- package/src/tests/interfaces.test.ts +117 -117
- package/src/tests/providers/anthropic.test.ts +118 -0
- package/src/tests/providers/google.test.ts +841 -660
- package/src/tests/providers/ollama.test.ts +1034 -954
- package/src/tests/providers/openai.test.ts +1511 -1122
- package/src/tests/router.test.ts +254 -254
- package/src/tests/stream-decoder.test.ts +263 -179
- package/src/tests/structured-output.test.ts +1450 -1450
- package/src/tests/thinking.test.ts +65 -0
- package/src/tests/tools.test.ts +175 -175
- package/src/thinking.ts +73 -0
- package/src/tools.ts +246 -246
- package/src/zod-adapter.ts +72 -72
package/src/auditor.ts
CHANGED
|
@@ -1,213 +1,213 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Universal LLM Client v3 — Auditor (Observability)
|
|
3
|
-
*
|
|
4
|
-
* Every LLM interaction (request, response, tool call, retry, failover)
|
|
5
|
-
* is recorded through the Auditor interface. Frameworks inject their own
|
|
6
|
-
* Auditor for dashboards, cost tracking, or behavioral scoring.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { TokenUsageInfo, ToolExecutionResult } from './interfaces.js';
|
|
10
|
-
|
|
11
|
-
// ============================================================================
|
|
12
|
-
// Audit Event
|
|
13
|
-
// ============================================================================
|
|
14
|
-
|
|
15
|
-
export type AuditEventType =
|
|
16
|
-
| 'request'
|
|
17
|
-
| 'response'
|
|
18
|
-
| 'stream_start'
|
|
19
|
-
| 'stream_end'
|
|
20
|
-
| 'tool_call'
|
|
21
|
-
| 'tool_result'
|
|
22
|
-
| 'error'
|
|
23
|
-
| 'retry'
|
|
24
|
-
| 'failover'
|
|
25
|
-
| 'structured_request'
|
|
26
|
-
| 'structured_response'
|
|
27
|
-
| 'structured_validation_error';
|
|
28
|
-
|
|
29
|
-
export interface AuditEvent {
|
|
30
|
-
/** Unix timestamp in ms */
|
|
31
|
-
timestamp: number;
|
|
32
|
-
/** Event type */
|
|
33
|
-
type: AuditEventType;
|
|
34
|
-
/** Provider that generated this event */
|
|
35
|
-
provider?: string;
|
|
36
|
-
/** Model name */
|
|
37
|
-
model?: string;
|
|
38
|
-
/** Duration in ms (for request/response pairs) */
|
|
39
|
-
duration?: number;
|
|
40
|
-
/** Token usage (for response events) */
|
|
41
|
-
usage?: TokenUsageInfo;
|
|
42
|
-
/** Tool execution details (for tool_call/tool_result events) */
|
|
43
|
-
toolExecution?: ToolExecutionResult;
|
|
44
|
-
/** Error message (for error/retry events) */
|
|
45
|
-
error?: string;
|
|
46
|
-
/** Arbitrary metadata for framework-specific data */
|
|
47
|
-
metadata?: Record<string, unknown>;
|
|
48
|
-
/** Schema name for structured output events */
|
|
49
|
-
schemaName?: string;
|
|
50
|
-
/** Raw output snippet for validation errors */
|
|
51
|
-
rawOutput?: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ============================================================================
|
|
55
|
-
// Auditor Interface
|
|
56
|
-
// ============================================================================
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Interface for LLM observability.
|
|
60
|
-
*
|
|
61
|
-
* Implement this to capture all LLM lifecycle events.
|
|
62
|
-
* The library calls `record()` at every interaction point.
|
|
63
|
-
*/
|
|
64
|
-
export interface Auditor {
|
|
65
|
-
/** Record an audit event */
|
|
66
|
-
record(event: AuditEvent): void;
|
|
67
|
-
/** Flush any buffered events (optional) */
|
|
68
|
-
flush?(): Promise<void>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ============================================================================
|
|
72
|
-
// Built-in Auditors
|
|
73
|
-
// ============================================================================
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Zero-overhead auditor that discards all events.
|
|
77
|
-
* Used as the default when no auditor is configured.
|
|
78
|
-
*/
|
|
79
|
-
export class NoopAuditor implements Auditor {
|
|
80
|
-
record(_event: AuditEvent): void {
|
|
81
|
-
// Intentionally empty
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Structured console logging auditor.
|
|
87
|
-
* Useful for development and debugging.
|
|
88
|
-
*/
|
|
89
|
-
export class ConsoleAuditor implements Auditor {
|
|
90
|
-
private prefix: string;
|
|
91
|
-
|
|
92
|
-
constructor(prefix: string = '[LLM]') {
|
|
93
|
-
this.prefix = prefix;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
record(event: AuditEvent): void {
|
|
97
|
-
const parts = [
|
|
98
|
-
this.prefix,
|
|
99
|
-
event.type.toUpperCase(),
|
|
100
|
-
event.provider ? `[${event.provider}]` : '',
|
|
101
|
-
event.model ? `(${event.model})` : '',
|
|
102
|
-
].filter(Boolean);
|
|
103
|
-
|
|
104
|
-
switch (event.type) {
|
|
105
|
-
case 'request':
|
|
106
|
-
console.log(parts.join(' '), '→');
|
|
107
|
-
break;
|
|
108
|
-
case 'response':
|
|
109
|
-
console.log(
|
|
110
|
-
parts.join(' '),
|
|
111
|
-
event.duration ? `${event.duration}ms` : '',
|
|
112
|
-
event.usage ? `${event.usage.totalTokens} tokens` : '',
|
|
113
|
-
);
|
|
114
|
-
break;
|
|
115
|
-
case 'stream_start':
|
|
116
|
-
console.log(parts.join(' '), 'streaming...');
|
|
117
|
-
break;
|
|
118
|
-
case 'stream_end':
|
|
119
|
-
console.log(
|
|
120
|
-
parts.join(' '),
|
|
121
|
-
'done',
|
|
122
|
-
event.duration ? `${event.duration}ms` : '',
|
|
123
|
-
);
|
|
124
|
-
break;
|
|
125
|
-
case 'tool_call':
|
|
126
|
-
console.log(parts.join(' '), event.toolExecution?.tool_call_id ?? '');
|
|
127
|
-
break;
|
|
128
|
-
case 'tool_result':
|
|
129
|
-
console.log(
|
|
130
|
-
parts.join(' '),
|
|
131
|
-
event.toolExecution?.error ? '❌' : '✅',
|
|
132
|
-
event.toolExecution?.duration ? `${event.toolExecution.duration}ms` : '',
|
|
133
|
-
);
|
|
134
|
-
break;
|
|
135
|
-
case 'error':
|
|
136
|
-
console.error(parts.join(' '), event.error ?? 'Unknown error');
|
|
137
|
-
break;
|
|
138
|
-
case 'retry':
|
|
139
|
-
console.warn(parts.join(' '), event.error ?? '', event.metadata ?? '');
|
|
140
|
-
break;
|
|
141
|
-
case 'failover':
|
|
142
|
-
console.warn(parts.join(' '), '→', event.metadata?.['nextProvider'] ?? '');
|
|
143
|
-
break;
|
|
144
|
-
case 'structured_request':
|
|
145
|
-
console.log(
|
|
146
|
-
parts.join(' '),
|
|
147
|
-
`schema=${event.schemaName ?? 'unknown'}`,
|
|
148
|
-
'→',
|
|
149
|
-
);
|
|
150
|
-
break;
|
|
151
|
-
case 'structured_response':
|
|
152
|
-
console.log(
|
|
153
|
-
parts.join(' '),
|
|
154
|
-
event.duration ? `${event.duration}ms` : '',
|
|
155
|
-
`schema=${event.schemaName ?? 'unknown'}`,
|
|
156
|
-
);
|
|
157
|
-
break;
|
|
158
|
-
case 'structured_validation_error':
|
|
159
|
-
console.error(
|
|
160
|
-
parts.join(' '),
|
|
161
|
-
`schema=${event.schemaName ?? 'unknown'}`,
|
|
162
|
-
event.error ?? 'Validation failed',
|
|
163
|
-
event.rawOutput ? `raw=${event.rawOutput.slice(0, 50)}...` : '',
|
|
164
|
-
);
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Buffered auditor that collects events for batch processing.
|
|
172
|
-
* Useful for custom sinks (OpenTelemetry, DataDog, databases, etc.)
|
|
173
|
-
*/
|
|
174
|
-
export class BufferedAuditor implements Auditor {
|
|
175
|
-
private events: AuditEvent[] = [];
|
|
176
|
-
private maxBufferSize: number;
|
|
177
|
-
private onFlush?: (events: AuditEvent[]) => Promise<void>;
|
|
178
|
-
|
|
179
|
-
constructor(options: {
|
|
180
|
-
maxBufferSize?: number;
|
|
181
|
-
onFlush?: (events: AuditEvent[]) => Promise<void>;
|
|
182
|
-
} = {}) {
|
|
183
|
-
this.maxBufferSize = options.maxBufferSize ?? 1000;
|
|
184
|
-
this.onFlush = options.onFlush;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
record(event: AuditEvent): void {
|
|
188
|
-
this.events.push(event);
|
|
189
|
-
if (this.events.length >= this.maxBufferSize) {
|
|
190
|
-
// Auto-flush when buffer is full (fire and forget)
|
|
191
|
-
this.flush().catch(() => {});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** Get all buffered events */
|
|
196
|
-
getEvents(): ReadonlyArray<AuditEvent> {
|
|
197
|
-
return this.events;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/** Flush buffered events to the configured sink */
|
|
201
|
-
async flush(): Promise<void> {
|
|
202
|
-
if (this.events.length === 0) return;
|
|
203
|
-
const batch = this.events.splice(0);
|
|
204
|
-
if (this.onFlush) {
|
|
205
|
-
await this.onFlush(batch);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/** Clear all buffered events without flushing */
|
|
210
|
-
clear(): void {
|
|
211
|
-
this.events.length = 0;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Universal LLM Client v3 — Auditor (Observability)
|
|
3
|
+
*
|
|
4
|
+
* Every LLM interaction (request, response, tool call, retry, failover)
|
|
5
|
+
* is recorded through the Auditor interface. Frameworks inject their own
|
|
6
|
+
* Auditor for dashboards, cost tracking, or behavioral scoring.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { TokenUsageInfo, ToolExecutionResult } from './interfaces.js';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Audit Event
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export type AuditEventType =
|
|
16
|
+
| 'request'
|
|
17
|
+
| 'response'
|
|
18
|
+
| 'stream_start'
|
|
19
|
+
| 'stream_end'
|
|
20
|
+
| 'tool_call'
|
|
21
|
+
| 'tool_result'
|
|
22
|
+
| 'error'
|
|
23
|
+
| 'retry'
|
|
24
|
+
| 'failover'
|
|
25
|
+
| 'structured_request'
|
|
26
|
+
| 'structured_response'
|
|
27
|
+
| 'structured_validation_error';
|
|
28
|
+
|
|
29
|
+
export interface AuditEvent {
|
|
30
|
+
/** Unix timestamp in ms */
|
|
31
|
+
timestamp: number;
|
|
32
|
+
/** Event type */
|
|
33
|
+
type: AuditEventType;
|
|
34
|
+
/** Provider that generated this event */
|
|
35
|
+
provider?: string;
|
|
36
|
+
/** Model name */
|
|
37
|
+
model?: string;
|
|
38
|
+
/** Duration in ms (for request/response pairs) */
|
|
39
|
+
duration?: number;
|
|
40
|
+
/** Token usage (for response events) */
|
|
41
|
+
usage?: TokenUsageInfo;
|
|
42
|
+
/** Tool execution details (for tool_call/tool_result events) */
|
|
43
|
+
toolExecution?: ToolExecutionResult;
|
|
44
|
+
/** Error message (for error/retry events) */
|
|
45
|
+
error?: string;
|
|
46
|
+
/** Arbitrary metadata for framework-specific data */
|
|
47
|
+
metadata?: Record<string, unknown>;
|
|
48
|
+
/** Schema name for structured output events */
|
|
49
|
+
schemaName?: string;
|
|
50
|
+
/** Raw output snippet for validation errors */
|
|
51
|
+
rawOutput?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Auditor Interface
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Interface for LLM observability.
|
|
60
|
+
*
|
|
61
|
+
* Implement this to capture all LLM lifecycle events.
|
|
62
|
+
* The library calls `record()` at every interaction point.
|
|
63
|
+
*/
|
|
64
|
+
export interface Auditor {
|
|
65
|
+
/** Record an audit event */
|
|
66
|
+
record(event: AuditEvent): void;
|
|
67
|
+
/** Flush any buffered events (optional) */
|
|
68
|
+
flush?(): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Built-in Auditors
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Zero-overhead auditor that discards all events.
|
|
77
|
+
* Used as the default when no auditor is configured.
|
|
78
|
+
*/
|
|
79
|
+
export class NoopAuditor implements Auditor {
|
|
80
|
+
record(_event: AuditEvent): void {
|
|
81
|
+
// Intentionally empty
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Structured console logging auditor.
|
|
87
|
+
* Useful for development and debugging.
|
|
88
|
+
*/
|
|
89
|
+
export class ConsoleAuditor implements Auditor {
|
|
90
|
+
private prefix: string;
|
|
91
|
+
|
|
92
|
+
constructor(prefix: string = '[LLM]') {
|
|
93
|
+
this.prefix = prefix;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
record(event: AuditEvent): void {
|
|
97
|
+
const parts = [
|
|
98
|
+
this.prefix,
|
|
99
|
+
event.type.toUpperCase(),
|
|
100
|
+
event.provider ? `[${event.provider}]` : '',
|
|
101
|
+
event.model ? `(${event.model})` : '',
|
|
102
|
+
].filter(Boolean);
|
|
103
|
+
|
|
104
|
+
switch (event.type) {
|
|
105
|
+
case 'request':
|
|
106
|
+
console.log(parts.join(' '), '→');
|
|
107
|
+
break;
|
|
108
|
+
case 'response':
|
|
109
|
+
console.log(
|
|
110
|
+
parts.join(' '),
|
|
111
|
+
event.duration ? `${event.duration}ms` : '',
|
|
112
|
+
event.usage ? `${event.usage.totalTokens} tokens` : '',
|
|
113
|
+
);
|
|
114
|
+
break;
|
|
115
|
+
case 'stream_start':
|
|
116
|
+
console.log(parts.join(' '), 'streaming...');
|
|
117
|
+
break;
|
|
118
|
+
case 'stream_end':
|
|
119
|
+
console.log(
|
|
120
|
+
parts.join(' '),
|
|
121
|
+
'done',
|
|
122
|
+
event.duration ? `${event.duration}ms` : '',
|
|
123
|
+
);
|
|
124
|
+
break;
|
|
125
|
+
case 'tool_call':
|
|
126
|
+
console.log(parts.join(' '), event.toolExecution?.tool_call_id ?? '');
|
|
127
|
+
break;
|
|
128
|
+
case 'tool_result':
|
|
129
|
+
console.log(
|
|
130
|
+
parts.join(' '),
|
|
131
|
+
event.toolExecution?.error ? '❌' : '✅',
|
|
132
|
+
event.toolExecution?.duration ? `${event.toolExecution.duration}ms` : '',
|
|
133
|
+
);
|
|
134
|
+
break;
|
|
135
|
+
case 'error':
|
|
136
|
+
console.error(parts.join(' '), event.error ?? 'Unknown error');
|
|
137
|
+
break;
|
|
138
|
+
case 'retry':
|
|
139
|
+
console.warn(parts.join(' '), event.error ?? '', event.metadata ?? '');
|
|
140
|
+
break;
|
|
141
|
+
case 'failover':
|
|
142
|
+
console.warn(parts.join(' '), '→', event.metadata?.['nextProvider'] ?? '');
|
|
143
|
+
break;
|
|
144
|
+
case 'structured_request':
|
|
145
|
+
console.log(
|
|
146
|
+
parts.join(' '),
|
|
147
|
+
`schema=${event.schemaName ?? 'unknown'}`,
|
|
148
|
+
'→',
|
|
149
|
+
);
|
|
150
|
+
break;
|
|
151
|
+
case 'structured_response':
|
|
152
|
+
console.log(
|
|
153
|
+
parts.join(' '),
|
|
154
|
+
event.duration ? `${event.duration}ms` : '',
|
|
155
|
+
`schema=${event.schemaName ?? 'unknown'}`,
|
|
156
|
+
);
|
|
157
|
+
break;
|
|
158
|
+
case 'structured_validation_error':
|
|
159
|
+
console.error(
|
|
160
|
+
parts.join(' '),
|
|
161
|
+
`schema=${event.schemaName ?? 'unknown'}`,
|
|
162
|
+
event.error ?? 'Validation failed',
|
|
163
|
+
event.rawOutput ? `raw=${event.rawOutput.slice(0, 50)}...` : '',
|
|
164
|
+
);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Buffered auditor that collects events for batch processing.
|
|
172
|
+
* Useful for custom sinks (OpenTelemetry, DataDog, databases, etc.)
|
|
173
|
+
*/
|
|
174
|
+
export class BufferedAuditor implements Auditor {
|
|
175
|
+
private events: AuditEvent[] = [];
|
|
176
|
+
private maxBufferSize: number;
|
|
177
|
+
private onFlush?: (events: AuditEvent[]) => Promise<void>;
|
|
178
|
+
|
|
179
|
+
constructor(options: {
|
|
180
|
+
maxBufferSize?: number;
|
|
181
|
+
onFlush?: (events: AuditEvent[]) => Promise<void>;
|
|
182
|
+
} = {}) {
|
|
183
|
+
this.maxBufferSize = options.maxBufferSize ?? 1000;
|
|
184
|
+
this.onFlush = options.onFlush;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
record(event: AuditEvent): void {
|
|
188
|
+
this.events.push(event);
|
|
189
|
+
if (this.events.length >= this.maxBufferSize) {
|
|
190
|
+
// Auto-flush when buffer is full (fire and forget)
|
|
191
|
+
this.flush().catch(() => {});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Get all buffered events */
|
|
196
|
+
getEvents(): ReadonlyArray<AuditEvent> {
|
|
197
|
+
return this.events;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Flush buffered events to the configured sink */
|
|
201
|
+
async flush(): Promise<void> {
|
|
202
|
+
if (this.events.length === 0) return;
|
|
203
|
+
const batch = this.events.splice(0);
|
|
204
|
+
if (this.onFlush) {
|
|
205
|
+
await this.onFlush(batch);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Clear all buffered events without flushing */
|
|
210
|
+
clear(): void {
|
|
211
|
+
this.events.length = 0;
|
|
212
|
+
}
|
|
213
|
+
}
|