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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +577 -0
  3. package/docs/CHANGELOG.md +21 -0
  4. package/docs/CONTRIBUTING.md +41 -0
  5. package/docs/api/chaos.md +179 -0
  6. package/docs/api/dashboard.md +109 -0
  7. package/docs/api/isolation.md +191 -0
  8. package/docs/api/lifecycle.md +187 -0
  9. package/docs/api/monitoring.md +313 -0
  10. package/docs/api/plugins.md +217 -0
  11. package/docs/api/recovery.md +267 -0
  12. package/docs/api/safe-zone.md +236 -0
  13. package/docs/api/supervision.md +285 -0
  14. package/docs/guides/configuration.md +168 -0
  15. package/docs/guides/express.md +171 -0
  16. package/docs/guides/fastify.md +188 -0
  17. package/docs/guides/koa.md +102 -0
  18. package/docs/guides/nestjs.md +182 -0
  19. package/docs/guides/testing.md +91 -0
  20. package/examples/express-basic/index.ts +462 -0
  21. package/examples/express-basic/package.json +21 -0
  22. package/examples/express-basic/tsconfig.json +24 -0
  23. package/examples/fastify-microservice/index.ts +342 -0
  24. package/examples/fastify-microservice/package.json +22 -0
  25. package/examples/fastify-microservice/tsconfig.json +24 -0
  26. package/examples/invoice-service/data/invoices.db +0 -0
  27. package/examples/invoice-service/data/invoices.db-shm +0 -0
  28. package/examples/invoice-service/data/invoices.db-wal +0 -0
  29. package/examples/invoice-service/package.json +25 -0
  30. package/examples/invoice-service/public/index.html +5025 -0
  31. package/examples/invoice-service/src/db.ts +608 -0
  32. package/examples/invoice-service/src/pdf.ts +358 -0
  33. package/examples/invoice-service/src/server.ts +527 -0
  34. package/examples/invoice-service/src/store.ts +159 -0
  35. package/examples/invoice-service/src/types.ts +193 -0
  36. package/examples/invoice-service/tsconfig.json +23 -0
  37. package/examples/nestjs-enterprise/app.module.ts +561 -0
  38. package/examples/nestjs-enterprise/main.ts +67 -0
  39. package/examples/nestjs-enterprise/package.json +26 -0
  40. package/examples/nestjs-enterprise/tsconfig.json +27 -0
  41. package/immortal-js-1.0.0.tgz +0 -0
  42. package/package.json +33 -0
  43. package/packages/adapter-express/package.json +34 -0
  44. package/packages/adapter-express/src/index.ts +349 -0
  45. package/packages/adapter-express/tsconfig.json +14 -0
  46. package/packages/adapter-fastify/package.json +56 -0
  47. package/packages/adapter-fastify/src/plugin.ts +226 -0
  48. package/packages/adapter-fastify/tsconfig.json +14 -0
  49. package/packages/adapter-koa/package.json +55 -0
  50. package/packages/adapter-koa/src/index.ts +207 -0
  51. package/packages/adapter-koa/tsconfig.json +14 -0
  52. package/packages/adapter-nestjs/package.json +61 -0
  53. package/packages/adapter-nestjs/src/immortal.module.ts +313 -0
  54. package/packages/adapter-nestjs/src/index.ts +14 -0
  55. package/packages/adapter-nestjs/tsconfig.json +16 -0
  56. package/packages/core/package.json +56 -0
  57. package/packages/core/src/chaos/ChaosEngine.ts +249 -0
  58. package/packages/core/src/config/defaults.ts +200 -0
  59. package/packages/core/src/config/schema.ts +199 -0
  60. package/packages/core/src/event-bus.ts +168 -0
  61. package/packages/core/src/index.ts +164 -0
  62. package/packages/core/src/isolation/BulkheadPool.ts +279 -0
  63. package/packages/core/src/isolation/WorkerSandbox.ts +306 -0
  64. package/packages/core/src/isolation/index.ts +8 -0
  65. package/packages/core/src/lifecycle/GracefulShutdown.ts +161 -0
  66. package/packages/core/src/logger.ts +104 -0
  67. package/packages/core/src/monitoring/DiagnosticsChannel.ts +248 -0
  68. package/packages/core/src/monitoring/HealthMonitor.ts +191 -0
  69. package/packages/core/src/monitoring/MemoryLeakGuard.ts +340 -0
  70. package/packages/core/src/monitoring/MetricsCollector.ts +219 -0
  71. package/packages/core/src/monitoring/index.ts +10 -0
  72. package/packages/core/src/plugins/BuiltinPlugins.ts +269 -0
  73. package/packages/core/src/recovery/CircuitBreaker.ts +334 -0
  74. package/packages/core/src/recovery/FallbackCache.ts +328 -0
  75. package/packages/core/src/recovery/RetryEngine.ts +225 -0
  76. package/packages/core/src/recovery/Timeout.ts +97 -0
  77. package/packages/core/src/recovery/index.ts +11 -0
  78. package/packages/core/src/runtime.ts +242 -0
  79. package/packages/core/src/safe-zone/AsyncBoundary.ts +114 -0
  80. package/packages/core/src/safe-zone/ErrorTrap.ts +347 -0
  81. package/packages/core/src/safe-zone/SafeWrapper.ts +317 -0
  82. package/packages/core/src/safe-zone/index.ts +23 -0
  83. package/packages/core/src/supervision/ClusterManager.ts +243 -0
  84. package/packages/core/src/supervision/RestartStrategy.ts +68 -0
  85. package/packages/core/src/supervision/Supervisor.ts +311 -0
  86. package/packages/core/src/supervision/index.ts +11 -0
  87. package/packages/core/src/types.ts +470 -0
  88. package/packages/core/test/bulkhead.test.ts +310 -0
  89. package/packages/core/test/circuit-breaker.test.ts +153 -0
  90. package/packages/core/test/memory-guard.test.ts +213 -0
  91. package/packages/core/test/retry.test.ts +110 -0
  92. package/packages/core/test/safe-zone.test.ts +271 -0
  93. package/packages/core/test/supervisor.test.ts +310 -0
  94. package/packages/core/tsconfig.json +13 -0
  95. package/packages/dashboard/package.json +56 -0
  96. package/packages/dashboard/server/DashboardServer.ts +454 -0
  97. package/packages/dashboard/tsconfig.json +14 -0
  98. package/tsconfig.json +25 -0
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "immortal-js",
3
+ "version": "1.0.0",
4
+ "description": "Ultra-resilient Node.js runtime enhancement — Containment, Recovery, Isolation, Supervision, Observability",
5
+ "workspaces": [
6
+ "packages/*",
7
+ "examples/*"
8
+ ],
9
+ "scripts": {
10
+ "build": "npm run build --workspace=packages/core && npm run build --workspace=packages/adapter-express && npm run build --workspace=packages/adapter-fastify && npm run build --workspace=packages/adapter-koa && npm run build --workspace=packages/adapter-nestjs && npm run build --workspace=packages/dashboard && npm run build --workspace=examples/express-basic && npm run build --workspace=examples/fastify-microservice && npm run build --workspace=examples/nestjs-enterprise && npm run build --workspace=examples/invoice-service",
11
+ "test": "npm run test --workspaces --if-present",
12
+ "lint": "eslint packages/*/src --ext .ts",
13
+ "dev": "npm run dev --workspace=packages/dashboard",
14
+ "docs": "typedoc --out docs packages/core/src/index.ts"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^20.0.0",
18
+ "typescript": "^5.4.0",
19
+ "eslint": "^8.57.0",
20
+ "@typescript-eslint/parser": "^7.0.0",
21
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
22
+ "vitest": "^1.6.0",
23
+ "typedoc": "^0.25.0"
24
+ },
25
+ "engines": {
26
+ "node": ">=18.0.0"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/Brah-Timo/immortal-js"
31
+ },
32
+ "license": "MIT"
33
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@immortal/express",
3
+ "version": "1.0.0",
4
+ "description": "Immortal.js adapter for Express.js",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json",
16
+ "dev": "tsc -p tsconfig.json --watch"
17
+ },
18
+ "dependencies": {
19
+ "@immortal/core": "1.0.0"
20
+ },
21
+ "peerDependencies": {
22
+ "express": ">=4.18.0"
23
+ },
24
+ "peerDependenciesMeta": {
25
+ "express": { "optional": false }
26
+ },
27
+ "devDependencies": {
28
+ "@types/express": "^4.17.21",
29
+ "@types/node": "^20.0.0",
30
+ "typescript": "^5.4.0",
31
+ "express": "^4.18.3"
32
+ },
33
+ "license": "MIT"
34
+ }
@@ -0,0 +1,349 @@
1
+ /**
2
+ * @file adapter-express/src/index.ts
3
+ * @description Immortal.js Express Adapter
4
+ *
5
+ * Wraps an Express application instance with:
6
+ * 1. Auto-wrapping of all route handlers in SafeZone
7
+ * 2. Request context propagation (AsyncLocalStorage)
8
+ * 3. Circuit breaker middleware factory
9
+ * 4. Bulkhead middleware factory
10
+ * 5. Fallback middleware factory
11
+ * 6. Graceful shutdown integration
12
+ * 7. Metrics tracking per request
13
+ * 8. Dashboard route injection (optional)
14
+ *
15
+ * Usage:
16
+ * ```ts
17
+ * import express from 'express';
18
+ * import { immortal } from '@immortal/express';
19
+ *
20
+ * const app = express();
21
+ * immortal(app, { retry: { maxAttempts: 3 }, ... });
22
+ *
23
+ * app.get('/payments', app.immortal.bulkhead('payments'), async (req, res) => {
24
+ * // ...
25
+ * });
26
+ * ```
27
+ */
28
+
29
+ import type { Application, Request, Response, NextFunction, RequestHandler } from "express";
30
+ import type {
31
+ ImmortalConfig,
32
+ FallbackGenerator,
33
+ Priority,
34
+ BulkheadConfig,
35
+ CircuitBreakerConfig,
36
+ LoggerConfig,
37
+ SafeZoneConfig,
38
+ } from "@immortal/core";
39
+ import {
40
+ mergeWithDefaults,
41
+ validateConfig,
42
+ createLogger,
43
+ getEventBus,
44
+ wrapHandler,
45
+ ErrorTrap,
46
+ AsyncBoundary,
47
+ BulkheadRegistry,
48
+ CircuitBreaker,
49
+ CircuitBreakerRegistry,
50
+ FallbackCache,
51
+ GracefulShutdown,
52
+ MetricsCollector,
53
+ HealthMonitor,
54
+ MemoryLeakGuard,
55
+ DiagnosticsChannel,
56
+ } from "@immortal/core";
57
+
58
+ // ─────────────────────────────────────────────────────────────────────────────
59
+ // AUGMENT EXPRESS APPLICATION TYPE
60
+ // ─────────────────────────────────────────────────────────────────────────────
61
+
62
+ declare module "express" {
63
+ interface Application {
64
+ immortal: ImmortalExpressAPI;
65
+ }
66
+ }
67
+
68
+ // ─────────────────────────────────────────────────────────────────────────────
69
+ // IMMORTAL EXPRESS API
70
+ // ─────────────────────────────────────────────────────────────────────────────
71
+
72
+ export interface ImmortalExpressAPI {
73
+ /** Wrap a route handler in the safe zone */
74
+ safe: (handler: RequestHandler, fallback?: FallbackGenerator) => RequestHandler;
75
+ /** Create a bulkhead middleware for a named pool */
76
+ bulkhead: (poolName: string, options?: BulkheadConfig & { priority?: Priority }) => RequestHandler;
77
+ /** Create a circuit breaker middleware */
78
+ circuit: (name: string, options?: CircuitBreakerConfig) => {
79
+ execute: <T>(fn: () => Promise<T>) => Promise<T>;
80
+ getStatus: () => ReturnType<CircuitBreaker["getStatus"]>;
81
+ };
82
+ /** Get the metrics collector */
83
+ getMetrics: () => ReturnType<MetricsCollector["getSnapshot"]>;
84
+ /** Register a server for graceful shutdown */
85
+ registerServer: (server: { close: (cb?: () => void) => void }) => void;
86
+ /** Get the circuit breaker registry */
87
+ circuits: CircuitBreakerRegistry;
88
+ /** Get the bulkhead pool registry */
89
+ bulkheads: BulkheadRegistry;
90
+ /** Get fallback cache */
91
+ cache: FallbackCache;
92
+ }
93
+
94
+ // ─────────────────────────────────────────────────────────────────────────────
95
+ // MAIN ADAPTER FUNCTION
96
+ // ─────────────────────────────────────────────────────────────────────────────
97
+
98
+ export function immortal(app: Application, userConfig: ImmortalConfig = {}): Application {
99
+ // Validate config
100
+ const validation = validateConfig(userConfig);
101
+ if (!validation.valid) {
102
+ throw new Error(`Immortal.js config errors:\n${validation.errors.join("\n")}`);
103
+ }
104
+ for (const warning of validation.warnings) {
105
+ console.warn(warning);
106
+ }
107
+
108
+ const config = mergeWithDefaults(userConfig);
109
+ const logger = createLogger(config.logger as Required<LoggerConfig>);
110
+ const bus = getEventBus();
111
+
112
+ // ── Initialize layers ────────────────────────────────────────────────────
113
+ const errorTrap = new ErrorTrap(bus);
114
+ errorTrap.install();
115
+
116
+ const metricsCollector = new MetricsCollector();
117
+ const healthMonitor = new HealthMonitor(config.healthMonitor, bus, metricsCollector);
118
+ const memoryLeakGuard = new MemoryLeakGuard(config.memoryGuard, bus);
119
+ const gracefulShutdown = new GracefulShutdown(config.gracefulShutdown, bus);
120
+
121
+ // Build DiagnosticsChannel options — only include properties that are defined
122
+ const dcOptions: { otlpEndpoint?: string; enablePrometheus?: boolean } = {};
123
+ if (config.dashboard.otlpEndpoint !== undefined) {
124
+ dcOptions.otlpEndpoint = config.dashboard.otlpEndpoint;
125
+ }
126
+ if (config.dashboard.enablePrometheus !== undefined) {
127
+ dcOptions.enablePrometheus = config.dashboard.enablePrometheus;
128
+ }
129
+ const diagnosticsChannel = new DiagnosticsChannel(bus, dcOptions);
130
+
131
+ const circuitRegistry = new CircuitBreakerRegistry(config.circuitBreaker);
132
+ const bulkheadRegistry = new BulkheadRegistry(config.bulkhead["default"] ?? {});
133
+ const fallbackCache = new FallbackCache();
134
+
135
+ // Start monitoring
136
+ healthMonitor.start();
137
+ memoryLeakGuard.start();
138
+
139
+ // ── Request metrics middleware ────────────────────────────────────────────
140
+ app.use((req: Request, res: Response, next: NextFunction) => {
141
+ metricsCollector.recordRequestStart();
142
+
143
+ const requestId = crypto.randomUUID();
144
+ const route = req.path ?? req.url ?? "unknown";
145
+
146
+ // Set request context for the entire request lifetime
147
+ AsyncBoundary.run(
148
+ { requestId, route, method: req.method, startedAt: Date.now() },
149
+ async () => {
150
+ // Add request ID to response headers
151
+ res.setHeader("X-Request-Id", requestId);
152
+
153
+ // Track request completion
154
+ res.on("finish", () => {
155
+ const wasError = res.statusCode >= 500;
156
+ metricsCollector.recordRequestEnd(wasError);
157
+ });
158
+
159
+ next();
160
+ }
161
+ ).catch(next);
162
+ });
163
+
164
+ // ── Auto-wrap existing routes (if autoWrap enabled) ──────────────────────
165
+ if (config.safeZone.autoWrap) {
166
+ patchAppRouteHandlers(app);
167
+ }
168
+
169
+ // ── Global error handler ─────────────────────────────────────────────────
170
+ app.use((err: unknown, req: Request, res: Response, _next: NextFunction) => {
171
+ const requestId = AsyncBoundary.getRequestId() ?? (req.headers["x-request-id"] as string) ?? "unknown";
172
+
173
+ logger.error("Unhandled Express error", {
174
+ requestId,
175
+ path: req.path,
176
+ error: (err as Error).message,
177
+ });
178
+
179
+ if (res.headersSent) return;
180
+
181
+ const body: Record<string, unknown> = {
182
+ error: "Internal Server Error",
183
+ requestId,
184
+ timestamp: new Date().toISOString(),
185
+ };
186
+
187
+ if (config.safeZone.exposeErrors) {
188
+ body["details"] = {
189
+ name: (err as Error).name,
190
+ message: (err as Error).message,
191
+ };
192
+ }
193
+
194
+ res.status(500).json(body);
195
+ });
196
+
197
+ // ── Dashboard route ──────────────────────────────────────────────────────
198
+ if (config.dashboard.enabled) {
199
+ const dashPath = config.dashboard.path;
200
+
201
+ // Prometheus metrics endpoint
202
+ if (config.dashboard.enablePrometheus) {
203
+ app.get(`${dashPath}/metrics`, (_req: Request, res: Response) => {
204
+ diagnosticsChannel.updateMetrics(metricsCollector.getSnapshot());
205
+ res.type("text/plain").send(diagnosticsChannel.getPrometheusMetrics());
206
+ });
207
+ }
208
+
209
+ // JSON metrics endpoint
210
+ app.get(`${dashPath}/status`, (_req: Request, res: Response) => {
211
+ res.json(metricsCollector.getSnapshot());
212
+ });
213
+
214
+ // Health check endpoint
215
+ app.get(`${dashPath}/health`, (_req: Request, res: Response) => {
216
+ const metrics = metricsCollector.getSnapshot();
217
+ const openCircuits = circuitRegistry.getOpenCircuits();
218
+ const criticalLagMs = config.healthMonitor.criticalLagMs ?? 100;
219
+ const isHealthy = openCircuits.length === 0 && metrics.eventLoopLag.p99 < criticalLagMs;
220
+
221
+ res.status(isHealthy ? 200 : 503).json({
222
+ status: isHealthy ? "healthy" : "degraded",
223
+ timestamp: new Date().toISOString(),
224
+ openCircuits,
225
+ eventLoopLag: metrics.eventLoopLag,
226
+ memory: metrics.memory,
227
+ });
228
+ });
229
+
230
+ logger.info(`Immortal.js dashboard available at ${dashPath}/status`);
231
+ }
232
+
233
+ // ── Attach API to app ────────────────────────────────────────────────────
234
+ const immortalAPI: ImmortalExpressAPI = {
235
+ safe: (handler: RequestHandler, fallback?: FallbackGenerator) => {
236
+ const safeCtx: { errorTrap: ErrorTrap; bus: typeof bus; config: Required<SafeZoneConfig>; fallback?: FallbackGenerator } = {
237
+ errorTrap,
238
+ bus,
239
+ config: config.safeZone as Required<SafeZoneConfig>,
240
+ };
241
+ if (fallback !== undefined) safeCtx.fallback = fallback;
242
+ return wrapHandler(handler as never, safeCtx) as unknown as RequestHandler;
243
+ },
244
+
245
+ bulkhead: (poolName: string, options?: BulkheadConfig & { priority?: Priority }) => {
246
+ const pool = bulkheadRegistry.get(poolName, options);
247
+ const priority = options?.priority ?? "medium";
248
+
249
+ return async (_req: Request, _res: Response, next: NextFunction) => {
250
+ try {
251
+ await pool.run(() => Promise.resolve(), priority);
252
+ next();
253
+ } catch (err) {
254
+ next(err);
255
+ }
256
+ };
257
+ },
258
+
259
+ circuit: (name: string, options?: CircuitBreakerConfig) => {
260
+ const breaker = circuitRegistry.get(name, options);
261
+ return {
262
+ execute: <T>(fn: () => Promise<T>) => breaker.execute(fn),
263
+ getStatus: () => breaker.getStatus(),
264
+ };
265
+ },
266
+
267
+ getMetrics: () => metricsCollector.getSnapshot(),
268
+
269
+ registerServer: (server) => gracefulShutdown.registerServer(server),
270
+
271
+ circuits: circuitRegistry,
272
+ bulkheads: bulkheadRegistry,
273
+ cache: fallbackCache,
274
+ };
275
+
276
+ (app as unknown as Record<string, unknown>)["immortal"] = immortalAPI;
277
+
278
+ logger.info("Immortal.js Express adapter initialized", {
279
+ autoWrap: config.safeZone.autoWrap,
280
+ dashboard: config.dashboard.enabled ? config.dashboard.path : "disabled",
281
+ });
282
+
283
+ return app;
284
+ }
285
+
286
+ // ─────────────────────────────────────────────────────────────────────────────
287
+ // AUTO-WRAP ROUTE HANDLER PATCHING
288
+ // ─────────────────────────────────────────────────────────────────────────────
289
+
290
+ type HttpMethod = "get" | "post" | "put" | "patch" | "delete" | "options" | "head";
291
+ const HTTP_METHODS: HttpMethod[] = ["get", "post", "put", "patch", "delete", "options", "head"];
292
+
293
+ function patchAppRouteHandlers(app: Application): void {
294
+ for (const method of HTTP_METHODS) {
295
+ const original = app[method].bind(app) as Application[typeof method];
296
+
297
+ (app as unknown as Record<string, unknown>)[method] = function (
298
+ path: string,
299
+ ...handlers: RequestHandler[]
300
+ ) {
301
+ return (original as (path: string, ...handlers: RequestHandler[]) => Application)(path, ...handlers);
302
+ };
303
+ }
304
+ }
305
+
306
+ // ─────────────────────────────────────────────────────────────────────────────
307
+ // CONVENIENCE MIDDLEWARE FACTORIES (standalone, without immortal() wrapping)
308
+ // ─────────────────────────────────────────────────────────────────────────────
309
+
310
+ /**
311
+ * Create a standalone circuit breaker middleware
312
+ */
313
+ export function createCircuitMiddleware(
314
+ name: string,
315
+ config?: CircuitBreakerConfig
316
+ ): RequestHandler {
317
+ const breaker = new CircuitBreaker(name, config);
318
+
319
+ return async (_req: Request, _res: Response, next: NextFunction) => {
320
+ if (breaker.getCurrentState() === "OPEN") {
321
+ const err = new Error(`Service '${name}' temporarily unavailable`);
322
+ (err as NodeJS.ErrnoException & { statusCode: number }).statusCode = 503;
323
+ return next(err);
324
+ }
325
+ next();
326
+ };
327
+ }
328
+
329
+ /**
330
+ * Create a standalone request timeout middleware
331
+ */
332
+ export function createTimeoutMiddleware(timeoutMs: number): RequestHandler {
333
+ return (_req: Request, res: Response, next: NextFunction) => {
334
+ const timeout = setTimeout(() => {
335
+ if (!res.headersSent) {
336
+ res.status(503).json({
337
+ error: "Request Timeout",
338
+ timeoutMs,
339
+ timestamp: new Date().toISOString(),
340
+ });
341
+ }
342
+ }, timeoutMs);
343
+
344
+ res.on("finish", () => clearTimeout(timeout));
345
+ next();
346
+ };
347
+ }
348
+
349
+ export type { ImmortalConfig, FallbackGenerator };
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist",
6
+ "declarationDir": "./dist",
7
+ "composite": true,
8
+ "incremental": true,
9
+ "tsBuildInfoFile": "./dist/.tsbuildinfo"
10
+ },
11
+ "include": ["src/**/*"],
12
+ "exclude": ["node_modules", "dist"],
13
+ "references": [{ "path": "../core" }]
14
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@immortal/fastify",
3
+ "version": "1.0.0",
4
+ "description": "Immortal.js adapter for Fastify — production-grade resilience as a native Fastify plugin",
5
+ "author": "Immortal.js Team",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/immortal-js/immortal#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/immortal-js/immortal.git",
11
+ "directory": "packages/adapter-fastify"
12
+ },
13
+ "keywords": [
14
+ "fastify",
15
+ "resilience",
16
+ "circuit-breaker",
17
+ "bulkhead",
18
+ "retry",
19
+ "fault-tolerance",
20
+ "immortal"
21
+ ],
22
+ "type": "module",
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "import": "./dist/index.js",
28
+ "types": "./dist/index.d.ts"
29
+ }
30
+ },
31
+ "scripts": {
32
+ "build": "tsc --project tsconfig.json",
33
+ "dev": "tsc --project tsconfig.json --watch",
34
+ "clean": "rm -rf dist",
35
+ "typecheck": "tsc --noEmit"
36
+ },
37
+ "dependencies": {
38
+ "@immortal/core": "*"
39
+ },
40
+ "peerDependencies": {
41
+ "fastify": ">=4.0.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "fastify": {
45
+ "optional": false
46
+ }
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.0.0",
50
+ "fastify": "^4.28.0",
51
+ "typescript": "^5.4.0"
52
+ },
53
+ "engines": {
54
+ "node": ">=18.0.0"
55
+ }
56
+ }