immortal-js 1.0.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/LICENSE +21 -0
- package/README.md +577 -0
- package/docs/CHANGELOG.md +21 -0
- package/docs/CONTRIBUTING.md +41 -0
- package/docs/api/chaos.md +179 -0
- package/docs/api/dashboard.md +109 -0
- package/docs/api/isolation.md +191 -0
- package/docs/api/lifecycle.md +187 -0
- package/docs/api/monitoring.md +313 -0
- package/docs/api/plugins.md +217 -0
- package/docs/api/recovery.md +267 -0
- package/docs/api/safe-zone.md +236 -0
- package/docs/api/supervision.md +285 -0
- package/docs/guides/configuration.md +168 -0
- package/docs/guides/express.md +171 -0
- package/docs/guides/fastify.md +188 -0
- package/docs/guides/koa.md +102 -0
- package/docs/guides/nestjs.md +182 -0
- package/docs/guides/testing.md +91 -0
- package/examples/express-basic/index.ts +462 -0
- package/examples/express-basic/package.json +21 -0
- package/examples/express-basic/tsconfig.json +24 -0
- package/examples/fastify-microservice/index.ts +342 -0
- package/examples/fastify-microservice/package.json +22 -0
- package/examples/fastify-microservice/tsconfig.json +24 -0
- package/examples/invoice-service/data/invoices.db +0 -0
- package/examples/invoice-service/data/invoices.db-shm +0 -0
- package/examples/invoice-service/data/invoices.db-wal +0 -0
- package/examples/invoice-service/package.json +25 -0
- package/examples/invoice-service/public/index.html +5025 -0
- package/examples/invoice-service/src/db.ts +608 -0
- package/examples/invoice-service/src/pdf.ts +358 -0
- package/examples/invoice-service/src/server.ts +527 -0
- package/examples/invoice-service/src/store.ts +159 -0
- package/examples/invoice-service/src/types.ts +193 -0
- package/examples/invoice-service/tsconfig.json +23 -0
- package/examples/nestjs-enterprise/app.module.ts +561 -0
- package/examples/nestjs-enterprise/main.ts +67 -0
- package/examples/nestjs-enterprise/package.json +26 -0
- package/examples/nestjs-enterprise/tsconfig.json +27 -0
- package/immortal-js-1.0.0.tgz +0 -0
- package/package.json +33 -0
- package/packages/adapter-express/package.json +34 -0
- package/packages/adapter-express/src/index.ts +349 -0
- package/packages/adapter-express/tsconfig.json +14 -0
- package/packages/adapter-fastify/package.json +56 -0
- package/packages/adapter-fastify/src/plugin.ts +226 -0
- package/packages/adapter-fastify/tsconfig.json +14 -0
- package/packages/adapter-koa/package.json +55 -0
- package/packages/adapter-koa/src/index.ts +207 -0
- package/packages/adapter-koa/tsconfig.json +14 -0
- package/packages/adapter-nestjs/package.json +61 -0
- package/packages/adapter-nestjs/src/immortal.module.ts +313 -0
- package/packages/adapter-nestjs/src/index.ts +14 -0
- package/packages/adapter-nestjs/tsconfig.json +16 -0
- package/packages/core/package.json +56 -0
- package/packages/core/src/chaos/ChaosEngine.ts +249 -0
- package/packages/core/src/config/defaults.ts +200 -0
- package/packages/core/src/config/schema.ts +199 -0
- package/packages/core/src/event-bus.ts +168 -0
- package/packages/core/src/index.ts +164 -0
- package/packages/core/src/isolation/BulkheadPool.ts +279 -0
- package/packages/core/src/isolation/WorkerSandbox.ts +306 -0
- package/packages/core/src/isolation/index.ts +8 -0
- package/packages/core/src/lifecycle/GracefulShutdown.ts +161 -0
- package/packages/core/src/logger.ts +104 -0
- package/packages/core/src/monitoring/DiagnosticsChannel.ts +248 -0
- package/packages/core/src/monitoring/HealthMonitor.ts +191 -0
- package/packages/core/src/monitoring/MemoryLeakGuard.ts +340 -0
- package/packages/core/src/monitoring/MetricsCollector.ts +219 -0
- package/packages/core/src/monitoring/index.ts +10 -0
- package/packages/core/src/plugins/BuiltinPlugins.ts +269 -0
- package/packages/core/src/recovery/CircuitBreaker.ts +334 -0
- package/packages/core/src/recovery/FallbackCache.ts +328 -0
- package/packages/core/src/recovery/RetryEngine.ts +225 -0
- package/packages/core/src/recovery/Timeout.ts +97 -0
- package/packages/core/src/recovery/index.ts +11 -0
- package/packages/core/src/runtime.ts +242 -0
- package/packages/core/src/safe-zone/AsyncBoundary.ts +114 -0
- package/packages/core/src/safe-zone/ErrorTrap.ts +347 -0
- package/packages/core/src/safe-zone/SafeWrapper.ts +317 -0
- package/packages/core/src/safe-zone/index.ts +23 -0
- package/packages/core/src/supervision/ClusterManager.ts +243 -0
- package/packages/core/src/supervision/RestartStrategy.ts +68 -0
- package/packages/core/src/supervision/Supervisor.ts +311 -0
- package/packages/core/src/supervision/index.ts +11 -0
- package/packages/core/src/types.ts +470 -0
- package/packages/core/test/bulkhead.test.ts +310 -0
- package/packages/core/test/circuit-breaker.test.ts +153 -0
- package/packages/core/test/memory-guard.test.ts +213 -0
- package/packages/core/test/retry.test.ts +110 -0
- package/packages/core/test/safe-zone.test.ts +271 -0
- package/packages/core/test/supervisor.test.ts +310 -0
- package/packages/core/tsconfig.json +13 -0
- package/packages/dashboard/package.json +56 -0
- package/packages/dashboard/server/DashboardServer.ts +454 -0
- package/packages/dashboard/tsconfig.json +14 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file types.ts
|
|
3
|
+
* @description Central type definitions for Immortal.js core
|
|
4
|
+
* All interfaces, enums, and shared types live here for cross-module consistency
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
// CORE CONFIGURATION TYPES
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export interface ImmortalConfig {
|
|
12
|
+
/** Safe zone configuration — controls error containment behavior */
|
|
13
|
+
safeZone?: SafeZoneConfig;
|
|
14
|
+
/** Retry engine configuration */
|
|
15
|
+
retry?: RetryConfig;
|
|
16
|
+
/** Circuit breaker configuration */
|
|
17
|
+
circuitBreaker?: CircuitBreakerConfig;
|
|
18
|
+
/** Bulkhead isolation pools */
|
|
19
|
+
bulkhead?: Record<string, BulkheadConfig>;
|
|
20
|
+
/** Supervisor configuration */
|
|
21
|
+
supervisor?: SupervisorConfig;
|
|
22
|
+
/** Health monitoring configuration */
|
|
23
|
+
healthMonitor?: HealthMonitorConfig;
|
|
24
|
+
/** Memory leak guard configuration */
|
|
25
|
+
memoryGuard?: MemoryGuardConfig;
|
|
26
|
+
/** Graceful shutdown configuration */
|
|
27
|
+
gracefulShutdown?: GracefulShutdownConfig;
|
|
28
|
+
/** Dashboard configuration */
|
|
29
|
+
dashboard?: DashboardConfig;
|
|
30
|
+
/** Plugin registry */
|
|
31
|
+
plugins?: ImmortalPlugin[];
|
|
32
|
+
/** Chaos testing — only active if NODE_ENV !== 'production' */
|
|
33
|
+
chaos?: ChaosConfig;
|
|
34
|
+
/** Logging configuration */
|
|
35
|
+
logger?: LoggerConfig;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SafeZoneConfig {
|
|
39
|
+
/** Enable automatic wrapping of all route handlers */
|
|
40
|
+
autoWrap?: boolean;
|
|
41
|
+
/** Custom fallback response generator */
|
|
42
|
+
defaultFallback?: FallbackGenerator;
|
|
43
|
+
/** Expose error details in response (never in production) */
|
|
44
|
+
exposeErrors?: boolean;
|
|
45
|
+
/** Sanitize sensitive fields from error logs */
|
|
46
|
+
sanitizeFields?: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RetryConfig {
|
|
50
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
51
|
+
maxAttempts?: number;
|
|
52
|
+
/** Base delay in milliseconds for exponential backoff (default: 200) */
|
|
53
|
+
baseDelayMs?: number;
|
|
54
|
+
/** Maximum delay cap in milliseconds (default: 10_000) */
|
|
55
|
+
maxDelayMs?: number;
|
|
56
|
+
/** Jitter factor 0-1 (default: 0.3) */
|
|
57
|
+
jitterFactor?: number;
|
|
58
|
+
/** Custom function to determine if error is retryable */
|
|
59
|
+
isRetryable?: (error: unknown) => boolean;
|
|
60
|
+
/** Global retry budget (max retries per second across all calls) */
|
|
61
|
+
retryBudget?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface CircuitBreakerConfig {
|
|
65
|
+
/** Number of failures before opening circuit (default: 5) */
|
|
66
|
+
threshold?: number;
|
|
67
|
+
/** Cooldown period in ms before attempting half-open (default: 15_000) */
|
|
68
|
+
cooldownMs?: number;
|
|
69
|
+
/** Sliding window size for failure rate calculation (default: 100) */
|
|
70
|
+
slidingWindowSize?: number;
|
|
71
|
+
/** Minimum requests before circuit can open (default: 10) */
|
|
72
|
+
minimumRequests?: number;
|
|
73
|
+
/** Failure rate percentage to open circuit (default: 50) */
|
|
74
|
+
failureRateThreshold?: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface BulkheadConfig {
|
|
78
|
+
/** Maximum concurrent operations (default: 10) */
|
|
79
|
+
maxConcurrent?: number;
|
|
80
|
+
/** Maximum queue size before rejection (default: 50) */
|
|
81
|
+
maxQueueSize?: number;
|
|
82
|
+
/** Timeout for queued operations in ms (default: 30_000) */
|
|
83
|
+
queueTimeoutMs?: number;
|
|
84
|
+
/** Priority levels: 'high' | 'medium' | 'low' */
|
|
85
|
+
enablePriority?: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type RestartStrategy = "one_for_one" | "one_for_all" | "rest_for_one";
|
|
89
|
+
|
|
90
|
+
export interface SupervisorConfig {
|
|
91
|
+
/** Restart strategy (default: 'one_for_one') */
|
|
92
|
+
strategy?: RestartStrategy;
|
|
93
|
+
/** Max restarts within time window (default: 5) */
|
|
94
|
+
maxRestartsInWindow?: number;
|
|
95
|
+
/** Time window in ms for restart counting (default: 60_000) */
|
|
96
|
+
windowMs?: number;
|
|
97
|
+
/** Base delay before first restart in ms (default: 100) */
|
|
98
|
+
baseRestartDelayMs?: number;
|
|
99
|
+
/** Enable cluster mode (multi-process) */
|
|
100
|
+
clusterMode?: boolean;
|
|
101
|
+
/** Number of cluster workers (default: os.cpus().length) */
|
|
102
|
+
clusterWorkers?: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface HealthMonitorConfig {
|
|
106
|
+
/** Monitoring interval in ms (default: 5_000) */
|
|
107
|
+
intervalMs?: number;
|
|
108
|
+
/** P99 event loop lag threshold in ms before warning (default: 100) */
|
|
109
|
+
eventLoopLagMs?: number;
|
|
110
|
+
/** P99 event loop lag threshold for critical action (default: 500) */
|
|
111
|
+
criticalLagMs?: number;
|
|
112
|
+
/** CPU usage % threshold for warning (default: 80) */
|
|
113
|
+
cpuWarningPercent?: number;
|
|
114
|
+
/** CPU usage % threshold for critical (default: 95) */
|
|
115
|
+
cpuCriticalPercent?: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface MemoryGuardConfig {
|
|
119
|
+
/** Check interval in ms (default: 10_000) */
|
|
120
|
+
checkIntervalMs?: number;
|
|
121
|
+
/** Heap usage MB that triggers warning (default: 1024) */
|
|
122
|
+
warningThresholdMb?: number;
|
|
123
|
+
/** Heap usage MB that triggers proactive restart (default: 1500) */
|
|
124
|
+
restartThresholdMb?: number;
|
|
125
|
+
/** Number of consecutive windows showing growth before action (default: 3) */
|
|
126
|
+
growthWindowCount?: number;
|
|
127
|
+
/** Enable WeakRef tracking for specific object types */
|
|
128
|
+
enableObjectTracking?: boolean;
|
|
129
|
+
/** Enable adaptive thresholds based on system RAM */
|
|
130
|
+
adaptiveThresholds?: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface GracefulShutdownConfig {
|
|
134
|
+
/** Grace period in ms before forced kill (default: 20_000) */
|
|
135
|
+
timeoutMs?: number;
|
|
136
|
+
/** Signals to listen for (default: ['SIGTERM', 'SIGINT']) */
|
|
137
|
+
signals?: NodeJS.Signals[];
|
|
138
|
+
/** Custom cleanup functions to run during shutdown */
|
|
139
|
+
onShutdown?: ShutdownHook[];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface DashboardConfig {
|
|
143
|
+
/** Enable dashboard (default: true) */
|
|
144
|
+
enabled?: boolean;
|
|
145
|
+
/** Dashboard port (default: 9999) */
|
|
146
|
+
port?: number;
|
|
147
|
+
/** Dashboard path prefix (default: '/__immortal') */
|
|
148
|
+
path?: string;
|
|
149
|
+
/** Enable WebSocket real-time feed */
|
|
150
|
+
enableWebSocket?: boolean;
|
|
151
|
+
/** Enable Server-Sent Events feed */
|
|
152
|
+
enableSSE?: boolean;
|
|
153
|
+
/** Enable OpenTelemetry export */
|
|
154
|
+
enableOTLP?: boolean;
|
|
155
|
+
/** OTLP endpoint (e.g. Grafana, Datadog) */
|
|
156
|
+
otlpEndpoint?: string;
|
|
157
|
+
/** Prometheus metrics endpoint */
|
|
158
|
+
enablePrometheus?: boolean;
|
|
159
|
+
/** Basic auth for dashboard */
|
|
160
|
+
auth?: { username: string; password: string };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface ChaosConfig {
|
|
164
|
+
/** Enable chaos mode (NEVER in production — enforced) */
|
|
165
|
+
enabled?: boolean;
|
|
166
|
+
/** Probability 0-1 of injecting latency per request */
|
|
167
|
+
latencyProbability?: number;
|
|
168
|
+
/** Latency range in ms [min, max] */
|
|
169
|
+
latencyRange?: [number, number];
|
|
170
|
+
/** Probability 0-1 of throwing random errors */
|
|
171
|
+
errorProbability?: number;
|
|
172
|
+
/** Probability 0-1 of killing a worker */
|
|
173
|
+
workerKillProbability?: number;
|
|
174
|
+
/** Memory pressure simulation MB */
|
|
175
|
+
memoryPressureMb?: number;
|
|
176
|
+
/** Specific routes to target (empty = all routes) */
|
|
177
|
+
targetRoutes?: string[];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface LoggerConfig {
|
|
181
|
+
/** Log level (default: 'info') */
|
|
182
|
+
level?: "debug" | "info" | "warn" | "error" | "silent";
|
|
183
|
+
/** Custom log transport */
|
|
184
|
+
transport?: LogTransport;
|
|
185
|
+
/** Redact sensitive fields */
|
|
186
|
+
redact?: string[];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
190
|
+
// EVENTS & METRICS TYPES
|
|
191
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
export type ImmortalEventType =
|
|
194
|
+
| "error:captured"
|
|
195
|
+
| "error:escalated"
|
|
196
|
+
| "retry:attempt"
|
|
197
|
+
| "retry:exhausted"
|
|
198
|
+
| "retry:success"
|
|
199
|
+
| "circuit:opened"
|
|
200
|
+
| "circuit:closed"
|
|
201
|
+
| "circuit:half-open"
|
|
202
|
+
| "circuit:rejected"
|
|
203
|
+
| "bulkhead:queued"
|
|
204
|
+
| "bulkhead:rejected"
|
|
205
|
+
| "bulkhead:timeout"
|
|
206
|
+
| "worker:started"
|
|
207
|
+
| "worker:restarted"
|
|
208
|
+
| "worker:escalated"
|
|
209
|
+
| "worker:crashed"
|
|
210
|
+
| "memory:warning"
|
|
211
|
+
| "memory:critical"
|
|
212
|
+
| "memory:leak-detected"
|
|
213
|
+
| "memory:proactive-restart"
|
|
214
|
+
| "eventloop:lag-warning"
|
|
215
|
+
| "eventloop:lag-critical"
|
|
216
|
+
| "cpu:warning"
|
|
217
|
+
| "cpu:critical"
|
|
218
|
+
| "shutdown:initiated"
|
|
219
|
+
| "shutdown:complete"
|
|
220
|
+
| "shutdown:forced"
|
|
221
|
+
| "fallback:cache-hit"
|
|
222
|
+
| "fallback:default-used"
|
|
223
|
+
| "chaos:latency-injected"
|
|
224
|
+
| "chaos:error-injected"
|
|
225
|
+
| "chaos:worker-killed";
|
|
226
|
+
|
|
227
|
+
export interface ImmortalEvent {
|
|
228
|
+
type: ImmortalEventType;
|
|
229
|
+
timestamp: number;
|
|
230
|
+
workerId?: string | number;
|
|
231
|
+
requestId?: string;
|
|
232
|
+
route?: string;
|
|
233
|
+
service?: string;
|
|
234
|
+
data?: Record<string, unknown>;
|
|
235
|
+
error?: SerializedError;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface SerializedError {
|
|
239
|
+
name: string;
|
|
240
|
+
message: string;
|
|
241
|
+
stack?: string;
|
|
242
|
+
code?: string;
|
|
243
|
+
statusCode?: number;
|
|
244
|
+
isOperational?: boolean;
|
|
245
|
+
context?: Record<string, unknown>;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export interface SystemMetrics {
|
|
249
|
+
timestamp: number;
|
|
250
|
+
eventLoopLag: {
|
|
251
|
+
p50: number;
|
|
252
|
+
p95: number;
|
|
253
|
+
p99: number;
|
|
254
|
+
max: number;
|
|
255
|
+
};
|
|
256
|
+
memory: {
|
|
257
|
+
heapUsedMb: number;
|
|
258
|
+
heapTotalMb: number;
|
|
259
|
+
externalMb: number;
|
|
260
|
+
rssMb: number;
|
|
261
|
+
heapGrowthRate: number; // MB per minute
|
|
262
|
+
gcFrequency: number; // GCs per minute
|
|
263
|
+
};
|
|
264
|
+
cpu: {
|
|
265
|
+
percentUser: number;
|
|
266
|
+
percentSystem: number;
|
|
267
|
+
};
|
|
268
|
+
handles: {
|
|
269
|
+
active: number;
|
|
270
|
+
};
|
|
271
|
+
requests: {
|
|
272
|
+
total: number;
|
|
273
|
+
active: number;
|
|
274
|
+
perSecond: number;
|
|
275
|
+
errorRate: number; // 0-1
|
|
276
|
+
};
|
|
277
|
+
circuits: Record<string, CircuitBreakerStatus>;
|
|
278
|
+
bulkheads: Record<string, BulkheadStatus>;
|
|
279
|
+
workers: WorkerStatus[];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface CircuitBreakerStatus {
|
|
283
|
+
name: string;
|
|
284
|
+
state: "CLOSED" | "OPEN" | "HALF_OPEN";
|
|
285
|
+
failures: number;
|
|
286
|
+
successes: number;
|
|
287
|
+
totalRequests: number;
|
|
288
|
+
failureRate: number;
|
|
289
|
+
lastFailureTime: number;
|
|
290
|
+
lastStateChange: number;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export interface BulkheadStatus {
|
|
294
|
+
name: string;
|
|
295
|
+
active: number;
|
|
296
|
+
queued: number;
|
|
297
|
+
maxConcurrent: number;
|
|
298
|
+
maxQueueSize: number;
|
|
299
|
+
rejected: number;
|
|
300
|
+
totalExecuted: number;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface WorkerStatus {
|
|
304
|
+
id: string | number;
|
|
305
|
+
pid?: number;
|
|
306
|
+
state: "running" | "restarting" | "stopped" | "escalated";
|
|
307
|
+
restartCount: number;
|
|
308
|
+
lastRestartTime?: number;
|
|
309
|
+
uptime: number;
|
|
310
|
+
memoryMb?: number;
|
|
311
|
+
cpuPercent?: number;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
315
|
+
// FUNCTIONAL TYPES
|
|
316
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
317
|
+
|
|
318
|
+
export type FallbackGenerator<T = unknown> = (
|
|
319
|
+
error: unknown,
|
|
320
|
+
context: RequestContext
|
|
321
|
+
) => T | Promise<T>;
|
|
322
|
+
|
|
323
|
+
export interface RequestContext {
|
|
324
|
+
requestId: string;
|
|
325
|
+
route?: string;
|
|
326
|
+
method?: string;
|
|
327
|
+
startedAt: number;
|
|
328
|
+
attempt?: number;
|
|
329
|
+
workerId?: string | number;
|
|
330
|
+
custom?: Record<string, unknown>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export type ShutdownHook = (signal: string) => Promise<void> | void;
|
|
334
|
+
|
|
335
|
+
export interface LogTransport {
|
|
336
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
337
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
338
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
339
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
343
|
+
// PLUGIN SYSTEM TYPES
|
|
344
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
export interface ImmortalPlugin {
|
|
347
|
+
name: string;
|
|
348
|
+
version: string;
|
|
349
|
+
description?: string;
|
|
350
|
+
/** Called once during immortal initialization */
|
|
351
|
+
onInit?: (context: PluginContext) => Promise<void> | void;
|
|
352
|
+
/** Called before each request */
|
|
353
|
+
onRequest?: (context: RequestContext) => Promise<void> | void;
|
|
354
|
+
/** Called after successful response */
|
|
355
|
+
onResponse?: (context: RequestContext, durationMs: number) => Promise<void> | void;
|
|
356
|
+
/** Called when an error is captured */
|
|
357
|
+
onError?: (error: unknown, context: RequestContext) => Promise<void> | void;
|
|
358
|
+
/** Called on every metrics tick */
|
|
359
|
+
onMetrics?: (metrics: SystemMetrics) => Promise<void> | void;
|
|
360
|
+
/** Called during graceful shutdown */
|
|
361
|
+
onShutdown?: ShutdownHook;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export interface PluginContext {
|
|
365
|
+
config: ImmortalConfig;
|
|
366
|
+
emit: (event: ImmortalEvent) => void;
|
|
367
|
+
getMetrics: () => SystemMetrics;
|
|
368
|
+
logger: LogTransport;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
372
|
+
// PRIORITY TYPES FOR BULKHEAD
|
|
373
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
374
|
+
|
|
375
|
+
export type Priority = "critical" | "high" | "medium" | "low";
|
|
376
|
+
|
|
377
|
+
export interface PriorityTask<T> {
|
|
378
|
+
task: () => Promise<T>;
|
|
379
|
+
priority: Priority;
|
|
380
|
+
timeoutMs?: number;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
384
|
+
// ERROR TYPES
|
|
385
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
386
|
+
|
|
387
|
+
export class ImmortalError extends Error {
|
|
388
|
+
public readonly isOperational: boolean;
|
|
389
|
+
public readonly code: string;
|
|
390
|
+
public readonly context?: Record<string, unknown>;
|
|
391
|
+
|
|
392
|
+
constructor(
|
|
393
|
+
message: string,
|
|
394
|
+
code: string,
|
|
395
|
+
isOperational = true,
|
|
396
|
+
context?: Record<string, unknown>
|
|
397
|
+
) {
|
|
398
|
+
super(message);
|
|
399
|
+
this.name = "ImmortalError";
|
|
400
|
+
this.code = code;
|
|
401
|
+
this.isOperational = isOperational;
|
|
402
|
+
if (context !== undefined) this.context = context;
|
|
403
|
+
Error.captureStackTrace(this, this.constructor);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export class BulkheadRejectedError extends ImmortalError {
|
|
408
|
+
constructor(poolName: string, reason: "saturated" | "timeout" = "saturated") {
|
|
409
|
+
super(
|
|
410
|
+
`Bulkhead pool '${poolName}' rejected request: ${reason}`,
|
|
411
|
+
"BULKHEAD_REJECTED",
|
|
412
|
+
true,
|
|
413
|
+
{ poolName, reason }
|
|
414
|
+
);
|
|
415
|
+
this.name = "BulkheadRejectedError";
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export class CircuitOpenError extends ImmortalError {
|
|
420
|
+
constructor(circuitName: string) {
|
|
421
|
+
super(
|
|
422
|
+
`Circuit breaker '${circuitName}' is OPEN — request rejected`,
|
|
423
|
+
"CIRCUIT_OPEN",
|
|
424
|
+
true,
|
|
425
|
+
{ circuitName }
|
|
426
|
+
);
|
|
427
|
+
this.name = "CircuitOpenError";
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export class RetryExhaustedError extends ImmortalError {
|
|
432
|
+
public readonly attempts: number;
|
|
433
|
+
public readonly lastError: unknown;
|
|
434
|
+
|
|
435
|
+
constructor(attempts: number, lastError: unknown) {
|
|
436
|
+
super(
|
|
437
|
+
`Retry exhausted after ${attempts} attempts`,
|
|
438
|
+
"RETRY_EXHAUSTED",
|
|
439
|
+
true,
|
|
440
|
+
{ attempts }
|
|
441
|
+
);
|
|
442
|
+
this.name = "RetryExhaustedError";
|
|
443
|
+
this.attempts = attempts;
|
|
444
|
+
this.lastError = lastError;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export class WorkerEscalatedError extends ImmortalError {
|
|
449
|
+
constructor(workerId: string, restartCount: number) {
|
|
450
|
+
super(
|
|
451
|
+
`Worker '${workerId}' escalated after ${restartCount} restarts — requires manual intervention`,
|
|
452
|
+
"WORKER_ESCALATED",
|
|
453
|
+
false,
|
|
454
|
+
{ workerId, restartCount }
|
|
455
|
+
);
|
|
456
|
+
this.name = "WorkerEscalatedError";
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export class TimeoutError extends ImmortalError {
|
|
461
|
+
constructor(operationName: string, timeoutMs: number) {
|
|
462
|
+
super(
|
|
463
|
+
`Operation '${operationName}' timed out after ${timeoutMs}ms`,
|
|
464
|
+
"OPERATION_TIMEOUT",
|
|
465
|
+
true,
|
|
466
|
+
{ operationName, timeoutMs }
|
|
467
|
+
);
|
|
468
|
+
this.name = "TimeoutError";
|
|
469
|
+
}
|
|
470
|
+
}
|