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,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file examples/fastify-microservice/index.ts
|
|
3
|
+
* @description Immortal.js — Fastify Microservice (Ultra Pro Edition)
|
|
4
|
+
*
|
|
5
|
+
* Demonstrates: Circuit Breaker, Bulkhead, Retry, Timeout, Fallback Cache,
|
|
6
|
+
* Graceful Shutdown, Health Monitor, Metrics endpoint.
|
|
7
|
+
*
|
|
8
|
+
* Run: npm run dev
|
|
9
|
+
* Build: npm run build
|
|
10
|
+
* Start: npm start
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import Fastify from "fastify";
|
|
14
|
+
import {
|
|
15
|
+
CircuitBreaker,
|
|
16
|
+
BulkheadPool,
|
|
17
|
+
withRetry,
|
|
18
|
+
withTimeout,
|
|
19
|
+
FallbackCache,
|
|
20
|
+
GracefulShutdown,
|
|
21
|
+
getEventBus,
|
|
22
|
+
} from "@immortal/core";
|
|
23
|
+
|
|
24
|
+
// ─── App Setup ───────────────────────────────────────────────────────────────
|
|
25
|
+
// Use plain JSON logger — no pino-pretty dependency required
|
|
26
|
+
const fastify = Fastify({
|
|
27
|
+
logger: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// ─── Resilient Primitives ─────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
// CircuitBreaker(name: string, config?) — positional constructor
|
|
33
|
+
const inventoryCircuit = new CircuitBreaker("inventory-service", {
|
|
34
|
+
threshold: 5,
|
|
35
|
+
cooldownMs: 12_000,
|
|
36
|
+
slidingWindowSize: 15,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const paymentCircuit = new CircuitBreaker("payment-gateway", {
|
|
40
|
+
threshold: 3,
|
|
41
|
+
cooldownMs: 30_000,
|
|
42
|
+
slidingWindowSize: 10,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// BulkheadPool(name: string, config?) — positional constructor
|
|
46
|
+
const ordersBulkhead = new BulkheadPool("orders", {
|
|
47
|
+
maxConcurrent: 20,
|
|
48
|
+
maxQueueSize: 100,
|
|
49
|
+
queueTimeoutMs: 5_000,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// FallbackCache({ maxSize?, defaultTtlMs? })
|
|
53
|
+
const productCache = new FallbackCache<unknown>({
|
|
54
|
+
maxSize: 500,
|
|
55
|
+
defaultTtlMs: 30_000,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// ─── Simulated External Services ─────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
async function fetchInventory(productId: string): Promise<{ stock: number }> {
|
|
61
|
+
if (Math.random() < 0.15) throw new Error("Inventory service unavailable");
|
|
62
|
+
await sleep(20 + Math.random() * 80);
|
|
63
|
+
return { stock: Math.floor(Math.random() * 100), productId } as { stock: number };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function processPayment(
|
|
67
|
+
orderId: string,
|
|
68
|
+
amount: number
|
|
69
|
+
): Promise<{ transactionId: string }> {
|
|
70
|
+
if (Math.random() < 0.05) throw new Error("Payment gateway timeout");
|
|
71
|
+
await sleep(100 + Math.random() * 200);
|
|
72
|
+
return { transactionId: `txn-${orderId}-${Date.now()}`, amount } as { transactionId: string };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function sleep(ms: number): Promise<void> {
|
|
76
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Routes ───────────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* GET /
|
|
83
|
+
* Welcome page with route map
|
|
84
|
+
*/
|
|
85
|
+
fastify.get("/", async (_request, reply) => {
|
|
86
|
+
return reply.send({
|
|
87
|
+
name: "Immortal.js Fastify Microservice",
|
|
88
|
+
version: "1.0.0",
|
|
89
|
+
status: "running",
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
routes: {
|
|
92
|
+
"GET /": "This route map",
|
|
93
|
+
"GET /health": "Live health + circuit status",
|
|
94
|
+
"GET /metrics": "Prometheus-compatible metrics",
|
|
95
|
+
"GET /products/:id": "Inventory lookup [Circuit + Cache]",
|
|
96
|
+
"POST /orders": "Create order [Bulkhead + Retry + Circuit]",
|
|
97
|
+
"GET /chaos/trip-inventory": "Force inventory circuit OPEN (dev)",
|
|
98
|
+
"GET /chaos/reset-circuits": "Reset all circuits (dev)",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* GET /products/:id
|
|
105
|
+
* Circuit Breaker wraps inventory fetch.
|
|
106
|
+
* On circuit open / timeout → serve from FallbackCache.
|
|
107
|
+
*/
|
|
108
|
+
fastify.get<{ Params: { id: string } }>("/products/:id", async (request, reply) => {
|
|
109
|
+
const { id } = request.params;
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const data = await withTimeout(
|
|
113
|
+
() =>
|
|
114
|
+
inventoryCircuit.execute(
|
|
115
|
+
() => fetchInventory(id),
|
|
116
|
+
() => ({ stock: -1, fromFallback: true })
|
|
117
|
+
),
|
|
118
|
+
3_000,
|
|
119
|
+
"inventory-fetch"
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// Cache successful (non-fallback) responses
|
|
123
|
+
if (!("fromFallback" in data)) {
|
|
124
|
+
productCache.set(`product:${id}`, data);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return reply
|
|
128
|
+
.header("X-Immortal-Circuit", inventoryCircuit.getStatus().state)
|
|
129
|
+
.send({ productId: id, ...data });
|
|
130
|
+
} catch {
|
|
131
|
+
// Serve stale cache on circuit open / timeout
|
|
132
|
+
const cached = productCache.get(`product:${id}`);
|
|
133
|
+
if (cached) {
|
|
134
|
+
return reply
|
|
135
|
+
.header("X-Immortal-Fallback", "true")
|
|
136
|
+
.header("X-Immortal-Fallback-Reason", "circuit-or-timeout")
|
|
137
|
+
.send({ productId: id, ...(cached as object), stale: true });
|
|
138
|
+
}
|
|
139
|
+
return reply.status(503).send({ error: "Product service unavailable" });
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* POST /orders
|
|
145
|
+
* Body: { productId: string, quantity: number, amount: number }
|
|
146
|
+
* Bulkhead → Retry inventory → CircuitBreaker payment
|
|
147
|
+
*/
|
|
148
|
+
fastify.post<{ Body: { productId: string; quantity: number; amount: number } }>(
|
|
149
|
+
"/orders",
|
|
150
|
+
async (request, reply) => {
|
|
151
|
+
const { productId, quantity, amount } = request.body;
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const result = await ordersBulkhead.run(async () => {
|
|
155
|
+
// Check inventory with retry
|
|
156
|
+
const inventory = await withRetry(
|
|
157
|
+
() => fetchInventory(productId),
|
|
158
|
+
{
|
|
159
|
+
maxAttempts: 3,
|
|
160
|
+
baseDelayMs: 100,
|
|
161
|
+
maxDelayMs: 2_000,
|
|
162
|
+
isRetryable: (e) =>
|
|
163
|
+
!(e instanceof Error && e.message.includes("insufficient")),
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
if (inventory.stock < quantity) {
|
|
168
|
+
return reply
|
|
169
|
+
.status(409)
|
|
170
|
+
.send({ error: "Insufficient stock", available: inventory.stock });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const orderId = `ord-${Date.now()}`;
|
|
174
|
+
|
|
175
|
+
const payment = await paymentCircuit.execute(
|
|
176
|
+
() => withTimeout(() => processPayment(orderId, amount), 10_000, "payment"),
|
|
177
|
+
() => ({ transactionId: `queued-${orderId}` })
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
orderId,
|
|
182
|
+
productId,
|
|
183
|
+
quantity,
|
|
184
|
+
payment,
|
|
185
|
+
timestamp: new Date().toISOString(),
|
|
186
|
+
};
|
|
187
|
+
}, "high");
|
|
188
|
+
|
|
189
|
+
return reply.status(201).send(result);
|
|
190
|
+
} catch (err: unknown) {
|
|
191
|
+
if (err instanceof Error && err.name === "BulkheadRejectedError") {
|
|
192
|
+
return reply.status(429).send({
|
|
193
|
+
error: "Order system overloaded — try again shortly",
|
|
194
|
+
retryAfter: 5,
|
|
195
|
+
bulkhead: ordersBulkhead.getStatus(),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
throw err;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* GET /health
|
|
205
|
+
* Real-time status of all resilience primitives
|
|
206
|
+
*/
|
|
207
|
+
fastify.get("/health", async (_request, reply) => {
|
|
208
|
+
const bulkStatus = ordersBulkhead.getStatus();
|
|
209
|
+
return reply.send({
|
|
210
|
+
status: "healthy",
|
|
211
|
+
uptime: Math.round(process.uptime()),
|
|
212
|
+
memory: {
|
|
213
|
+
heapUsedMb: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
|
|
214
|
+
rssM: Math.round(process.memoryUsage().rss / 1024 / 1024),
|
|
215
|
+
},
|
|
216
|
+
circuits: {
|
|
217
|
+
inventory: inventoryCircuit.getStatus().state,
|
|
218
|
+
payment: paymentCircuit.getStatus().state,
|
|
219
|
+
},
|
|
220
|
+
bulkhead: {
|
|
221
|
+
active: bulkStatus.active,
|
|
222
|
+
queued: bulkStatus.queued,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* GET /metrics
|
|
229
|
+
* Prometheus-compatible plain-text output
|
|
230
|
+
*/
|
|
231
|
+
fastify.get("/metrics", async (_request, reply) => {
|
|
232
|
+
const invState = inventoryCircuit.getStatus().state;
|
|
233
|
+
const payState = paymentCircuit.getStatus().state;
|
|
234
|
+
const bk = ordersBulkhead.getStatus();
|
|
235
|
+
|
|
236
|
+
return reply.header("Content-Type", "text/plain; version=0.0.4").send(
|
|
237
|
+
[
|
|
238
|
+
`# HELP immortal_circuit_state Circuit breaker state (1=CLOSED 2=HALF_OPEN 3=OPEN)`,
|
|
239
|
+
`immortal_circuit_state{name="inventory"} ${stateToNum(invState)}`,
|
|
240
|
+
`immortal_circuit_state{name="payment"} ${stateToNum(payState)}`,
|
|
241
|
+
`# HELP immortal_bulkhead_active Active tasks in bulkhead pool`,
|
|
242
|
+
`immortal_bulkhead_active{pool="orders"} ${bk.active}`,
|
|
243
|
+
`# HELP immortal_bulkhead_queued Queued tasks in bulkhead pool`,
|
|
244
|
+
`immortal_bulkhead_queued{pool="orders"} ${bk.queued}`,
|
|
245
|
+
`# HELP process_uptime_seconds Node.js process uptime`,
|
|
246
|
+
`process_uptime_seconds ${Math.round(process.uptime())}`,
|
|
247
|
+
].join("\n")
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
function stateToNum(state: string): number {
|
|
252
|
+
if (state === "CLOSED") return 1;
|
|
253
|
+
if (state === "HALF_OPEN") return 2;
|
|
254
|
+
return 3; // OPEN
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* GET /chaos/trip-inventory
|
|
259
|
+
* Force the inventory circuit breaker OPEN (dev/test only)
|
|
260
|
+
*/
|
|
261
|
+
fastify.get("/chaos/trip-inventory", async (_request, reply) => {
|
|
262
|
+
if (process.env["NODE_ENV"] === "production") {
|
|
263
|
+
return reply.status(403).send({ error: "Chaos endpoints disabled in production" });
|
|
264
|
+
}
|
|
265
|
+
inventoryCircuit.forceState("OPEN");
|
|
266
|
+
return reply.send({
|
|
267
|
+
message: "Inventory circuit forced OPEN",
|
|
268
|
+
state: inventoryCircuit.getStatus().state,
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* GET /chaos/reset-circuits
|
|
274
|
+
* Reset all circuits to CLOSED
|
|
275
|
+
*/
|
|
276
|
+
fastify.get("/chaos/reset-circuits", async (_request, reply) => {
|
|
277
|
+
if (process.env["NODE_ENV"] === "production") {
|
|
278
|
+
return reply.status(403).send({ error: "Chaos endpoints disabled in production" });
|
|
279
|
+
}
|
|
280
|
+
inventoryCircuit.forceState("CLOSED");
|
|
281
|
+
paymentCircuit.forceState("CLOSED");
|
|
282
|
+
return reply.send({
|
|
283
|
+
message: "All circuits reset to CLOSED",
|
|
284
|
+
inventory: inventoryCircuit.getStatus().state,
|
|
285
|
+
payment: paymentCircuit.getStatus().state,
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// ─── Graceful Shutdown ────────────────────────────────────────────────────────
|
|
290
|
+
// GracefulShutdown(config, bus) — 2 args required
|
|
291
|
+
|
|
292
|
+
const bus = getEventBus();
|
|
293
|
+
const shutdown = new GracefulShutdown(
|
|
294
|
+
{
|
|
295
|
+
timeoutMs: 20_000,
|
|
296
|
+
signals: ["SIGTERM", "SIGINT"],
|
|
297
|
+
onShutdown: [
|
|
298
|
+
async (_signal: string) => {
|
|
299
|
+
fastify.log.info("Draining bulkhead queue…");
|
|
300
|
+
ordersBulkhead.drain();
|
|
301
|
+
},
|
|
302
|
+
async (_signal: string) => {
|
|
303
|
+
fastify.log.info("Closing Fastify server…");
|
|
304
|
+
await fastify.close();
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
bus
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Keep reference alive (signal handlers installed in constructor)
|
|
312
|
+
void shutdown;
|
|
313
|
+
|
|
314
|
+
// ─── Start ────────────────────────────────────────────────────────────────────
|
|
315
|
+
|
|
316
|
+
const PORT = parseInt(process.env["PORT"] ?? "3000", 10);
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
await fastify.listen({ port: PORT, host: "0.0.0.0" });
|
|
320
|
+
|
|
321
|
+
// Banner after successful listen
|
|
322
|
+
console.log(`
|
|
323
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
324
|
+
║ Immortal.js — Fastify Microservice 🚀 ║
|
|
325
|
+
╠══════════════════════════════════════════════════════════════╣
|
|
326
|
+
║ Server → http://localhost:${PORT} ║
|
|
327
|
+
║ Health → http://localhost:${PORT}/health ║
|
|
328
|
+
║ Metrics → http://localhost:${PORT}/metrics ║
|
|
329
|
+
╠══════════════════════════════════════════════════════════════╣
|
|
330
|
+
║ Routes ║
|
|
331
|
+
║ GET /products/:id Circuit Breaker + Cache ║
|
|
332
|
+
║ POST /orders Bulkhead + Retry + Circuit ║
|
|
333
|
+
║ GET /health Live health snapshot ║
|
|
334
|
+
║ GET /metrics Prometheus-compatible metrics ║
|
|
335
|
+
║ GET /chaos/trip-inventory Force circuit OPEN (dev) ║
|
|
336
|
+
║ GET /chaos/reset-circuits Reset all circuits (dev) ║
|
|
337
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
338
|
+
`);
|
|
339
|
+
} catch (err) {
|
|
340
|
+
fastify.log.error(err);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "immortal-fastify-microservice-example",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Immortal.js — Fastify microservice example with full resilience patterns",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "node --import tsx/esm --no-warnings index.ts",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"build": "tsc"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@immortal/core": "*",
|
|
14
|
+
"fastify": "^4.28.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^20.0.0",
|
|
18
|
+
"pino-pretty": "^11.0.0",
|
|
19
|
+
"tsx": "^4.7.0",
|
|
20
|
+
"typescript": "^5.4.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"strictNullChecks": true,
|
|
9
|
+
"noUncheckedIndexedAccess": true,
|
|
10
|
+
"exactOptionalPropertyTypes": true,
|
|
11
|
+
"noImplicitOverride": true,
|
|
12
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
13
|
+
"declaration": false,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"allowSyntheticDefaultImports": true,
|
|
17
|
+
"forceConsistentCasingInFileNames": true,
|
|
18
|
+
"skipLibCheck": true,
|
|
19
|
+
"outDir": "dist",
|
|
20
|
+
"rootDir": "./"
|
|
21
|
+
},
|
|
22
|
+
"include": ["index.ts"],
|
|
23
|
+
"exclude": ["node_modules", "dist"]
|
|
24
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "immortal-invoice-service",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Immortal.js — Ultra Pro Invoice PDF Generator with SQLite, Light/Dark theme, Lucide icons",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "node --import tsx/esm --no-warnings src/server.ts",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/server.js"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@fastify/static": "^7.0.4",
|
|
14
|
+
"@immortal/core": "*",
|
|
15
|
+
"better-sqlite3": "^13.0.1",
|
|
16
|
+
"fastify": "^4.28.0",
|
|
17
|
+
"zod": "^3.22.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/better-sqlite3": "^7.6.8",
|
|
21
|
+
"@types/node": "^20.0.0",
|
|
22
|
+
"tsx": "^4.7.0",
|
|
23
|
+
"typescript": "^5.4.0"
|
|
24
|
+
}
|
|
25
|
+
}
|