monora-ai 1.7.0 → 1.9.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 +135 -1
- package/dist/alerts.d.ts.map +1 -1
- package/dist/alerts.js +4 -3
- package/dist/circuit_breaker.d.ts +116 -0
- package/dist/circuit_breaker.d.ts.map +1 -0
- package/dist/circuit_breaker.js +163 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -2
- package/dist/config_schema.d.ts +3171 -0
- package/dist/config_schema.d.ts.map +1 -0
- package/dist/config_schema.js +271 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +4 -3
- package/dist/data_handling.d.ts.map +1 -1
- package/dist/data_handling.js +2 -1
- package/dist/dispatcher.d.ts +5 -0
- package/dist/dispatcher.d.ts.map +1 -1
- package/dist/dispatcher.js +51 -13
- package/dist/hasher.d.ts.map +1 -1
- package/dist/hasher.js +2 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -2
- package/dist/instrumentation.d.ts.map +1 -1
- package/dist/instrumentation.js +3 -2
- package/dist/logger.d.ts +85 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +190 -0
- package/dist/middleware/nextjs.d.ts +172 -0
- package/dist/middleware/nextjs.d.ts.map +1 -0
- package/dist/middleware/nextjs.js +353 -0
- package/dist/pdf_report.d.ts +92 -0
- package/dist/pdf_report.d.ts.map +1 -0
- package/dist/pdf_report.js +742 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +3 -2
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +2 -1
- package/dist/reporting.d.ts.map +1 -1
- package/dist/reporting.js +6 -5
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +37 -27
- package/dist/sinks/https.d.ts +21 -13
- package/dist/sinks/https.d.ts.map +1 -1
- package/dist/sinks/https.js +43 -2
- package/dist/telemetry.d.ts +181 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +576 -0
- package/dist/wal.d.ts.map +1 -1
- package/dist/wal.js +7 -6
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Monora SDK for Node.js
|
|
1
|
+
# Monora SDK for Node.js v1.9.0
|
|
2
2
|
|
|
3
3
|
Lightweight governance and trace SDK for AI systems.
|
|
4
4
|
|
|
@@ -12,6 +12,13 @@ Lightweight governance and trace SDK for AI systems.
|
|
|
12
12
|
- **Multiple Sinks**: Output to stdout, file (JSON-lines), or HTTPS endpoints
|
|
13
13
|
- **Event Enrichment**: Automatic metadata (timestamp, host, process, environment)
|
|
14
14
|
|
|
15
|
+
### New in v1.9.0
|
|
16
|
+
|
|
17
|
+
- **🔄 Circuit Breaker**: Fault tolerance for HTTPS sinks with automatic recovery
|
|
18
|
+
- **📊 Telemetry/Analytics**: Prometheus and StatsD metrics export for observability
|
|
19
|
+
- **📄 PDF Reports**: Generate compliance and EU AI Act PDF reports
|
|
20
|
+
- **🌐 Next.js Middleware**: W3C Trace Context propagation for Next.js applications
|
|
21
|
+
|
|
15
22
|
## Installation
|
|
16
23
|
|
|
17
24
|
```bash
|
|
@@ -341,6 +348,133 @@ const httpsSink = new HttpSink(
|
|
|
341
348
|
);
|
|
342
349
|
```
|
|
343
350
|
|
|
351
|
+
## v1.9.0 Features
|
|
352
|
+
|
|
353
|
+
### Circuit Breaker
|
|
354
|
+
|
|
355
|
+
Prevent cascading failures with circuit breaker pattern for HTTPS sinks:
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import { HttpSink } from 'monora';
|
|
359
|
+
|
|
360
|
+
const httpsSink = new HttpSink(
|
|
361
|
+
'https://api.example.com/events',
|
|
362
|
+
{ 'Authorization': 'Bearer token' },
|
|
363
|
+
{
|
|
364
|
+
circuitBreaker: {
|
|
365
|
+
enabled: true,
|
|
366
|
+
failure_threshold: 5,
|
|
367
|
+
success_threshold: 2,
|
|
368
|
+
reset_timeout_sec: 60,
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Telemetry/Analytics
|
|
375
|
+
|
|
376
|
+
Export metrics to Prometheus or StatsD:
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { init, initMetrics, recordEvent, recordViolation } from 'monora';
|
|
380
|
+
|
|
381
|
+
// Configure telemetry
|
|
382
|
+
init({
|
|
383
|
+
configPath: './monora.yml',
|
|
384
|
+
config: {
|
|
385
|
+
telemetry: {
|
|
386
|
+
enabled: true,
|
|
387
|
+
backend: 'prometheus', // or 'statsd'
|
|
388
|
+
prometheus: {
|
|
389
|
+
port: 9090,
|
|
390
|
+
start_server: true,
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Metrics are automatically recorded:
|
|
397
|
+
// - monora_events_total (by event_type)
|
|
398
|
+
// - monora_violations_total (by policy_type)
|
|
399
|
+
// - monora_queue_depth
|
|
400
|
+
// - monora_tokens_total
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### PDF Report Generation
|
|
404
|
+
|
|
405
|
+
Generate compliance PDF reports:
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
import { generateCompliancePdf, generateAIActPdf } from 'monora';
|
|
409
|
+
|
|
410
|
+
// Generate compliance PDF
|
|
411
|
+
await generateCompliancePdf(complianceReport, 'compliance_report.pdf');
|
|
412
|
+
|
|
413
|
+
// Generate EU AI Act transparency PDF
|
|
414
|
+
await generateAIActPdf(aiActReport, 'ai_act_report.pdf');
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Requires optional dependency: `npm install puppeteer`
|
|
418
|
+
|
|
419
|
+
### Next.js Middleware
|
|
420
|
+
|
|
421
|
+
Automatic W3C Trace Context propagation for Next.js:
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// middleware.ts
|
|
425
|
+
import { withMonoraMiddleware } from 'monora';
|
|
426
|
+
|
|
427
|
+
export default withMonoraMiddleware({
|
|
428
|
+
skipPatterns: [/^\/_next/, /^\/api\/health/],
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
export const config = {
|
|
432
|
+
matcher: '/(.*)',
|
|
433
|
+
};
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
For API routes (Pages Router):
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
// pages/api/users.ts
|
|
440
|
+
import { withMonoraApi } from 'monora';
|
|
441
|
+
|
|
442
|
+
export default withMonoraApi(async (req, res) => {
|
|
443
|
+
const users = await fetchUsers();
|
|
444
|
+
res.json(users);
|
|
445
|
+
});
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
For route handlers (App Router):
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
// app/api/users/route.ts
|
|
452
|
+
import { withMonoraRoute } from 'monora';
|
|
453
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
454
|
+
|
|
455
|
+
export const GET = withMonoraRoute(async (req: NextRequest) => {
|
|
456
|
+
const users = await fetchUsers();
|
|
457
|
+
return NextResponse.json(users);
|
|
458
|
+
});
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
For Server Actions:
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
// app/actions.ts
|
|
465
|
+
'use server';
|
|
466
|
+
import { withMonoraAction } from 'monora';
|
|
467
|
+
|
|
468
|
+
export const createUser = withMonoraAction(
|
|
469
|
+
async (formData: FormData) => {
|
|
470
|
+
const name = formData.get('name');
|
|
471
|
+
// Create user logic
|
|
472
|
+
return { success: true };
|
|
473
|
+
},
|
|
474
|
+
'createUser'
|
|
475
|
+
);
|
|
476
|
+
```
|
|
477
|
+
|
|
344
478
|
## License
|
|
345
479
|
|
|
346
480
|
MIT
|
package/dist/alerts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiBrF;AAED,qBAAa,0BAA0B;IACrC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,KAAK,CAAC,CAAiB;IAC/B,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAU;gBAEd,OAAO,EAAE;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;QAC1C,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;KAC7C;IAaD,KAAK,IAAI,IAAI;IASb,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAWxC,KAAK,IAAI,IAAI;IAIb,KAAK,IAAI,IAAI;YAQC,YAAY;YAqBZ,WAAW;IAmBzB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,aAAa;CAStB"}
|
package/dist/alerts.js
CHANGED
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.ViolationWebhookDispatcher = exports.AlertError = void 0;
|
|
10
10
|
exports.buildViolationPayload = buildViolationPayload;
|
|
11
11
|
const axios_1 = __importDefault(require("axios"));
|
|
12
|
+
const logger_1 = require("./logger");
|
|
12
13
|
class AlertError extends Error {
|
|
13
14
|
constructor(message) {
|
|
14
15
|
super(message);
|
|
@@ -116,12 +117,12 @@ class ViolationWebhookDispatcher {
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
handleQueueFull() {
|
|
119
|
-
const message = '
|
|
120
|
+
const message = 'Violation webhook queue full; dropping alert';
|
|
120
121
|
if (this.queueFullMode === 'raise') {
|
|
121
122
|
throw new AlertError(message);
|
|
122
123
|
}
|
|
123
124
|
if (this.queueFullMode === 'warn') {
|
|
124
|
-
|
|
125
|
+
logger_1.logger.warning(message);
|
|
125
126
|
}
|
|
126
127
|
}
|
|
127
128
|
handleFailure(error) {
|
|
@@ -130,7 +131,7 @@ class ViolationWebhookDispatcher {
|
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
132
133
|
if (this.failureMode === 'warn') {
|
|
133
|
-
|
|
134
|
+
logger_1.logger.error('Violation webhook failed: %s', error);
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit breaker pattern for fault tolerance.
|
|
3
|
+
*
|
|
4
|
+
* Implements the circuit breaker pattern to prevent cascading failures
|
|
5
|
+
* when external services become unavailable. The circuit breaker has
|
|
6
|
+
* three states:
|
|
7
|
+
*
|
|
8
|
+
* - CLOSED: Normal operation, requests pass through
|
|
9
|
+
* - OPEN: Circuit tripped, requests fail fast without attempting
|
|
10
|
+
* - HALF_OPEN: Testing recovery, limited requests allowed
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Circuit breaker states.
|
|
14
|
+
*/
|
|
15
|
+
export declare enum CircuitState {
|
|
16
|
+
CLOSED = "closed",
|
|
17
|
+
OPEN = "open",
|
|
18
|
+
HALF_OPEN = "half_open"
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Configuration options for circuit breaker.
|
|
22
|
+
*/
|
|
23
|
+
export interface CircuitBreakerConfig {
|
|
24
|
+
/** Enable circuit breaker (default: true) */
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
/** Consecutive failures before opening circuit (default: 5) */
|
|
27
|
+
failure_threshold?: number;
|
|
28
|
+
/** Successes needed in half-open to close (default: 2) */
|
|
29
|
+
success_threshold?: number;
|
|
30
|
+
/** Seconds before attempting half-open (default: 60) */
|
|
31
|
+
reset_timeout_sec?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Error thrown when circuit breaker is open.
|
|
35
|
+
*/
|
|
36
|
+
export declare class CircuitBreakerError extends Error {
|
|
37
|
+
readonly state: CircuitState;
|
|
38
|
+
constructor(message: string, state: CircuitState);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Circuit breaker for protecting external service calls.
|
|
42
|
+
*
|
|
43
|
+
* Example usage:
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const cb = new CircuitBreaker({
|
|
46
|
+
* failure_threshold: 5,
|
|
47
|
+
* reset_timeout_sec: 60
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* async function callExternalService() {
|
|
51
|
+
* if (!cb.canExecute()) {
|
|
52
|
+
* throw new CircuitBreakerError('Circuit is open', cb.state);
|
|
53
|
+
* }
|
|
54
|
+
* try {
|
|
55
|
+
* const result = await makeRequest();
|
|
56
|
+
* cb.recordSuccess();
|
|
57
|
+
* return result;
|
|
58
|
+
* } catch (error) {
|
|
59
|
+
* cb.recordFailure();
|
|
60
|
+
* throw error;
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare class CircuitBreaker {
|
|
66
|
+
private readonly failureThreshold;
|
|
67
|
+
private readonly successThreshold;
|
|
68
|
+
private readonly resetTimeoutSec;
|
|
69
|
+
private readonly name;
|
|
70
|
+
private _state;
|
|
71
|
+
private _failureCount;
|
|
72
|
+
private _successCount;
|
|
73
|
+
private _lastFailureTime;
|
|
74
|
+
constructor(config?: CircuitBreakerConfig, name?: string);
|
|
75
|
+
/**
|
|
76
|
+
* Get current circuit state, checking for timeout transition.
|
|
77
|
+
*/
|
|
78
|
+
get state(): CircuitState;
|
|
79
|
+
/**
|
|
80
|
+
* Get current failure count.
|
|
81
|
+
*/
|
|
82
|
+
get failureCount(): number;
|
|
83
|
+
/**
|
|
84
|
+
* Get current success count (relevant in half-open state).
|
|
85
|
+
*/
|
|
86
|
+
get successCount(): number;
|
|
87
|
+
/**
|
|
88
|
+
* Check if request can proceed.
|
|
89
|
+
*
|
|
90
|
+
* @returns True if circuit is closed or half-open, false if open.
|
|
91
|
+
*/
|
|
92
|
+
canExecute(): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Record a successful operation.
|
|
95
|
+
*
|
|
96
|
+
* In half-open state, increments success count and may close circuit.
|
|
97
|
+
* In closed state, resets failure count.
|
|
98
|
+
*/
|
|
99
|
+
recordSuccess(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Record a failed operation.
|
|
102
|
+
*
|
|
103
|
+
* Increments failure count and may open circuit if threshold reached.
|
|
104
|
+
* In half-open state, immediately reopens circuit.
|
|
105
|
+
*/
|
|
106
|
+
recordFailure(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Manually reset circuit breaker to closed state.
|
|
109
|
+
*/
|
|
110
|
+
reset(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Return string representation.
|
|
113
|
+
*/
|
|
114
|
+
toString(): string;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=circuit_breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit_breaker.d.ts","sourceRoot":"","sources":["../src/circuit_breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;GAEG;AACH,oBAAY,YAAY;IACtB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,SAAS,cAAc;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+DAA+D;IAC/D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0DAA0D;IAC1D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAgB,KAAK,EAAE,YAAY,CAAC;gBAExB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY;CAKjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAE9B,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,MAAM,GAAE,oBAAyB,EAAE,IAAI,CAAC,EAAE,MAAM;IAO5D;;OAEG;IACH,IAAI,KAAK,IAAI,YAAY,CAcxB;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;;OAIG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;;OAKG;IACH,aAAa,IAAI,IAAI;IAwBrB;;;;;OAKG;IACH,aAAa,IAAI,IAAI;IAoBrB;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,QAAQ,IAAI,MAAM;CAMnB"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Circuit breaker pattern for fault tolerance.
|
|
4
|
+
*
|
|
5
|
+
* Implements the circuit breaker pattern to prevent cascading failures
|
|
6
|
+
* when external services become unavailable. The circuit breaker has
|
|
7
|
+
* three states:
|
|
8
|
+
*
|
|
9
|
+
* - CLOSED: Normal operation, requests pass through
|
|
10
|
+
* - OPEN: Circuit tripped, requests fail fast without attempting
|
|
11
|
+
* - HALF_OPEN: Testing recovery, limited requests allowed
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.CircuitBreaker = exports.CircuitBreakerError = exports.CircuitState = void 0;
|
|
15
|
+
const logger_1 = require("./logger");
|
|
16
|
+
/**
|
|
17
|
+
* Circuit breaker states.
|
|
18
|
+
*/
|
|
19
|
+
var CircuitState;
|
|
20
|
+
(function (CircuitState) {
|
|
21
|
+
CircuitState["CLOSED"] = "closed";
|
|
22
|
+
CircuitState["OPEN"] = "open";
|
|
23
|
+
CircuitState["HALF_OPEN"] = "half_open";
|
|
24
|
+
})(CircuitState || (exports.CircuitState = CircuitState = {}));
|
|
25
|
+
/**
|
|
26
|
+
* Error thrown when circuit breaker is open.
|
|
27
|
+
*/
|
|
28
|
+
class CircuitBreakerError extends Error {
|
|
29
|
+
constructor(message, state) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'CircuitBreakerError';
|
|
32
|
+
this.state = state;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.CircuitBreakerError = CircuitBreakerError;
|
|
36
|
+
/**
|
|
37
|
+
* Circuit breaker for protecting external service calls.
|
|
38
|
+
*
|
|
39
|
+
* Example usage:
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const cb = new CircuitBreaker({
|
|
42
|
+
* failure_threshold: 5,
|
|
43
|
+
* reset_timeout_sec: 60
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* async function callExternalService() {
|
|
47
|
+
* if (!cb.canExecute()) {
|
|
48
|
+
* throw new CircuitBreakerError('Circuit is open', cb.state);
|
|
49
|
+
* }
|
|
50
|
+
* try {
|
|
51
|
+
* const result = await makeRequest();
|
|
52
|
+
* cb.recordSuccess();
|
|
53
|
+
* return result;
|
|
54
|
+
* } catch (error) {
|
|
55
|
+
* cb.recordFailure();
|
|
56
|
+
* throw error;
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
class CircuitBreaker {
|
|
62
|
+
constructor(config = {}, name) {
|
|
63
|
+
this._state = CircuitState.CLOSED;
|
|
64
|
+
this._failureCount = 0;
|
|
65
|
+
this._successCount = 0;
|
|
66
|
+
this._lastFailureTime = 0;
|
|
67
|
+
this.failureThreshold = config.failure_threshold ?? 5;
|
|
68
|
+
this.successThreshold = config.success_threshold ?? 2;
|
|
69
|
+
this.resetTimeoutSec = config.reset_timeout_sec ?? 60.0;
|
|
70
|
+
this.name = name ?? 'circuit_breaker';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get current circuit state, checking for timeout transition.
|
|
74
|
+
*/
|
|
75
|
+
get state() {
|
|
76
|
+
if (this._state === CircuitState.OPEN) {
|
|
77
|
+
const elapsed = (Date.now() - this._lastFailureTime) / 1000;
|
|
78
|
+
if (elapsed >= this.resetTimeoutSec) {
|
|
79
|
+
logger_1.logger.debug('[%s] Circuit transitioning OPEN -> HALF_OPEN after %.1fs', this.name, elapsed);
|
|
80
|
+
this._state = CircuitState.HALF_OPEN;
|
|
81
|
+
this._successCount = 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return this._state;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get current failure count.
|
|
88
|
+
*/
|
|
89
|
+
get failureCount() {
|
|
90
|
+
return this._failureCount;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get current success count (relevant in half-open state).
|
|
94
|
+
*/
|
|
95
|
+
get successCount() {
|
|
96
|
+
return this._successCount;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if request can proceed.
|
|
100
|
+
*
|
|
101
|
+
* @returns True if circuit is closed or half-open, false if open.
|
|
102
|
+
*/
|
|
103
|
+
canExecute() {
|
|
104
|
+
return this.state !== CircuitState.OPEN;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Record a successful operation.
|
|
108
|
+
*
|
|
109
|
+
* In half-open state, increments success count and may close circuit.
|
|
110
|
+
* In closed state, resets failure count.
|
|
111
|
+
*/
|
|
112
|
+
recordSuccess() {
|
|
113
|
+
if (this._state === CircuitState.HALF_OPEN) {
|
|
114
|
+
this._successCount++;
|
|
115
|
+
logger_1.logger.debug('[%s] Success recorded in HALF_OPEN (%d/%d)', this.name, this._successCount, this.successThreshold);
|
|
116
|
+
if (this._successCount >= this.successThreshold) {
|
|
117
|
+
logger_1.logger.info('[%s] Circuit CLOSED after %d consecutive successes', this.name, this._successCount);
|
|
118
|
+
this._state = CircuitState.CLOSED;
|
|
119
|
+
this._failureCount = 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (this._state === CircuitState.CLOSED) {
|
|
123
|
+
// Reset failure count on success
|
|
124
|
+
this._failureCount = 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Record a failed operation.
|
|
129
|
+
*
|
|
130
|
+
* Increments failure count and may open circuit if threshold reached.
|
|
131
|
+
* In half-open state, immediately reopens circuit.
|
|
132
|
+
*/
|
|
133
|
+
recordFailure() {
|
|
134
|
+
this._failureCount++;
|
|
135
|
+
this._lastFailureTime = Date.now();
|
|
136
|
+
if (this._state === CircuitState.HALF_OPEN) {
|
|
137
|
+
logger_1.logger.warning('[%s] Circuit reopening HALF_OPEN -> OPEN after failure', this.name);
|
|
138
|
+
this._state = CircuitState.OPEN;
|
|
139
|
+
}
|
|
140
|
+
else if (this._failureCount >= this.failureThreshold) {
|
|
141
|
+
logger_1.logger.warning('[%s] Circuit OPEN after %d consecutive failures', this.name, this._failureCount);
|
|
142
|
+
this._state = CircuitState.OPEN;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Manually reset circuit breaker to closed state.
|
|
147
|
+
*/
|
|
148
|
+
reset() {
|
|
149
|
+
logger_1.logger.info('[%s] Circuit manually reset to CLOSED', this.name);
|
|
150
|
+
this._state = CircuitState.CLOSED;
|
|
151
|
+
this._failureCount = 0;
|
|
152
|
+
this._successCount = 0;
|
|
153
|
+
this._lastFailureTime = 0;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Return string representation.
|
|
157
|
+
*/
|
|
158
|
+
toString() {
|
|
159
|
+
return (`CircuitBreaker(name=${this.name}, state=${this.state}, ` +
|
|
160
|
+
`failures=${this._failureCount}, threshold=${this.failureThreshold})`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.CircuitBreaker = CircuitBreaker;
|
package/dist/config.d.ts
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* Configuration loading and normalization.
|
|
3
3
|
*/
|
|
4
4
|
export type PresetName = 'minimal' | 'development' | 'production' | 'compliance' | 'poc';
|
|
5
|
+
export interface CircuitBreakerConfig {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
failure_threshold?: number;
|
|
8
|
+
success_threshold?: number;
|
|
9
|
+
reset_timeout_sec?: number;
|
|
10
|
+
}
|
|
5
11
|
export interface SinkConfig {
|
|
6
12
|
type: string;
|
|
7
13
|
format?: string;
|
|
@@ -14,6 +20,7 @@ export interface SinkConfig {
|
|
|
14
20
|
retry_attempts?: number;
|
|
15
21
|
rotation?: string;
|
|
16
22
|
max_size_mb?: number;
|
|
23
|
+
circuit_breaker?: CircuitBreakerConfig;
|
|
17
24
|
[key: string]: any;
|
|
18
25
|
}
|
|
19
26
|
/**
|
|
@@ -100,6 +107,9 @@ export interface MonoraConfig {
|
|
|
100
107
|
batch_size?: number;
|
|
101
108
|
flush_interval_sec?: number;
|
|
102
109
|
queue_full_timeout_sec?: number | null;
|
|
110
|
+
adaptive_batching?: boolean;
|
|
111
|
+
min_batch_size?: number;
|
|
112
|
+
max_batch_size?: number;
|
|
103
113
|
};
|
|
104
114
|
wal?: {
|
|
105
115
|
enabled?: boolean;
|
|
@@ -124,6 +134,21 @@ export interface MonoraConfig {
|
|
|
124
134
|
generate_transparency_report?: boolean;
|
|
125
135
|
transparency_report_formats?: string[];
|
|
126
136
|
};
|
|
137
|
+
telemetry?: {
|
|
138
|
+
enabled?: boolean;
|
|
139
|
+
backend?: 'prometheus' | 'statsd';
|
|
140
|
+
prometheus?: {
|
|
141
|
+
port?: number;
|
|
142
|
+
start_server?: boolean;
|
|
143
|
+
push_gateway?: string;
|
|
144
|
+
job_name?: string;
|
|
145
|
+
};
|
|
146
|
+
statsd?: {
|
|
147
|
+
host?: string;
|
|
148
|
+
port?: number;
|
|
149
|
+
prefix?: string;
|
|
150
|
+
};
|
|
151
|
+
};
|
|
127
152
|
}
|
|
128
153
|
export declare function loadConfig(options?: {
|
|
129
154
|
configPath?: string;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
CHANGED
|
@@ -43,6 +43,7 @@ exports.getPresetConfig = getPresetConfig;
|
|
|
43
43
|
exports.buildConfigFromOptions = buildConfigFromOptions;
|
|
44
44
|
const fs = __importStar(require("fs"));
|
|
45
45
|
const yaml = __importStar(require("js-yaml"));
|
|
46
|
+
const logger_1 = require("./logger");
|
|
46
47
|
/**
|
|
47
48
|
* Preset configurations for quick setup.
|
|
48
49
|
*/
|
|
@@ -189,6 +190,9 @@ const DEFAULT_CONFIG = {
|
|
|
189
190
|
batch_size: 50,
|
|
190
191
|
flush_interval_sec: 1.0,
|
|
191
192
|
queue_full_timeout_sec: null,
|
|
193
|
+
adaptive_batching: true,
|
|
194
|
+
min_batch_size: 10,
|
|
195
|
+
max_batch_size: 500,
|
|
192
196
|
},
|
|
193
197
|
wal: {
|
|
194
198
|
enabled: false,
|
|
@@ -237,7 +241,7 @@ function loadConfig(options) {
|
|
|
237
241
|
function loadConfigFile(configPath, silent = false) {
|
|
238
242
|
if (!fs.existsSync(configPath)) {
|
|
239
243
|
if (!silent) {
|
|
240
|
-
|
|
244
|
+
logger_1.logger.warning('Config file not found: %s. Using defaults.', configPath);
|
|
241
245
|
}
|
|
242
246
|
return {};
|
|
243
247
|
}
|
|
@@ -395,7 +399,7 @@ function normalizeDataHandlingMode(config) {
|
|
|
395
399
|
}
|
|
396
400
|
const normalized = mode.toLowerCase();
|
|
397
401
|
if (normalized === 'mask' || normalized === 'encrypt') {
|
|
398
|
-
|
|
402
|
+
logger_1.logger.warning("data_handling.mode '%s' is deprecated; using 'redact' instead.", mode);
|
|
399
403
|
config.data_handling.mode = 'redact';
|
|
400
404
|
return;
|
|
401
405
|
}
|