@firebreak/vitals 1.3.0 → 2.0.1
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 +67 -52
- package/dist/handler-BvjN4Ot9.d.ts +20 -0
- package/dist/handler-TZOgZvY7.d.cts +20 -0
- package/dist/index.cjs +19 -39
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +19 -39
- package/dist/integrations/express.cjs +21 -61
- package/dist/integrations/express.d.cts +1 -1
- package/dist/integrations/express.d.ts +1 -1
- package/dist/integrations/express.mjs +21 -61
- package/dist/integrations/next.cjs +21 -61
- package/dist/integrations/next.d.cts +2 -2
- package/dist/integrations/next.d.ts +2 -2
- package/dist/integrations/next.mjs +21 -61
- package/package.json +1 -1
- package/dist/handler-COH7lot9.d.cts +0 -27
- package/dist/handler-CVYUad84.d.ts +0 -27
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vitals (Node.js)
|
|
2
2
|
|
|
3
|
-
Structured healthcheck endpoints for Node.js services with shallow and deep response tiers. Register health checks, run them concurrently with timeouts, and expose them via Express or Next.js.
|
|
3
|
+
Structured healthcheck endpoints for Node.js services with shallow and deep response tiers. Register health checks, run them concurrently with timeouts, and expose them via Express or Next.js. A configurable depth policy controls whether callers get a lightweight "alive" response or full diagnostic results.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ npm install express # for Express integration
|
|
|
19
19
|
## Quick Start (Express)
|
|
20
20
|
|
|
21
21
|
```typescript
|
|
22
|
-
import { HealthcheckRegistry } from '@firebreak/vitals';
|
|
22
|
+
import { HealthcheckRegistry, extractToken, verifyToken } from '@firebreak/vitals';
|
|
23
23
|
import { createHealthcheckMiddleware } from '@firebreak/vitals/express';
|
|
24
24
|
import { pgPoolCheck } from '@firebreak/vitals/checks/postgres';
|
|
25
25
|
import { ioredisClientCheck } from '@firebreak/vitals/checks/redis';
|
|
@@ -35,7 +35,14 @@ registry.add('api', httpCheck('https://api.example.com/health'));
|
|
|
35
35
|
const app = express();
|
|
36
36
|
app.get('/vitals', createHealthcheckMiddleware({
|
|
37
37
|
registry,
|
|
38
|
-
|
|
38
|
+
resolveDepth: (req) => {
|
|
39
|
+
const token = extractToken({
|
|
40
|
+
queryParams: req.queryParams as Record<string, string>,
|
|
41
|
+
authorizationHeader: req.authorizationHeader as string | null,
|
|
42
|
+
});
|
|
43
|
+
if (token && verifyToken(token, process.env.VITALS_TOKEN!)) return 'deep';
|
|
44
|
+
return 'shallow';
|
|
45
|
+
},
|
|
39
46
|
metadata: {
|
|
40
47
|
build: process.env.BUILD_SHA,
|
|
41
48
|
buildTimestamp: process.env.BUILD_TIMESTAMP,
|
|
@@ -43,19 +50,19 @@ app.get('/vitals', createHealthcheckMiddleware({
|
|
|
43
50
|
}));
|
|
44
51
|
```
|
|
45
52
|
|
|
46
|
-
Hit `/vitals` without a token to get a shallow response:
|
|
53
|
+
Hit `/vitals` without a valid token to get a shallow response:
|
|
47
54
|
|
|
48
55
|
```json
|
|
49
56
|
{ "status": "ok", "timestamp": "...", "build": "stg-45d76e5", "buildTimestamp": "2025-02-25T19:41:56Z" }
|
|
50
57
|
```
|
|
51
58
|
|
|
52
|
-
|
|
59
|
+
When `resolveDepth` returns `'deep'`, the full deep response with check results is returned.
|
|
53
60
|
|
|
54
61
|
## Quick Start (Next.js)
|
|
55
62
|
|
|
56
63
|
```typescript
|
|
57
64
|
// app/vitals/route.ts
|
|
58
|
-
import { HealthcheckRegistry } from '@firebreak/vitals';
|
|
65
|
+
import { HealthcheckRegistry, extractToken, verifyToken } from '@firebreak/vitals';
|
|
59
66
|
import { createNextHandler } from '@firebreak/vitals/next';
|
|
60
67
|
import { pgClientCheck } from '@firebreak/vitals/checks/postgres';
|
|
61
68
|
import { httpCheck } from '@firebreak/vitals/checks/http';
|
|
@@ -69,7 +76,14 @@ registry.add('api', httpCheck(`${process.env.API_BASE_URL}/health`));
|
|
|
69
76
|
|
|
70
77
|
export const GET = createNextHandler({
|
|
71
78
|
registry,
|
|
72
|
-
|
|
79
|
+
resolveDepth: (req) => {
|
|
80
|
+
const token = extractToken({
|
|
81
|
+
queryParams: req.queryParams as Record<string, string>,
|
|
82
|
+
authorizationHeader: req.authorizationHeader as string | null,
|
|
83
|
+
});
|
|
84
|
+
if (token && verifyToken(token, process.env.VITALS_TOKEN!)) return 'deep';
|
|
85
|
+
return 'shallow';
|
|
86
|
+
},
|
|
73
87
|
metadata: {
|
|
74
88
|
build: process.env.BUILD_SHA,
|
|
75
89
|
},
|
|
@@ -207,8 +221,14 @@ import { createHealthcheckMiddleware } from '@firebreak/vitals/express';
|
|
|
207
221
|
|
|
208
222
|
app.get('/vitals', createHealthcheckMiddleware({
|
|
209
223
|
registry,
|
|
210
|
-
|
|
211
|
-
|
|
224
|
+
resolveDepth: (req) => { // optional — omit for always-shallow
|
|
225
|
+
const token = extractToken({
|
|
226
|
+
queryParams: req.queryParams as Record<string, string>,
|
|
227
|
+
authorizationHeader: req.authorizationHeader as string | null,
|
|
228
|
+
});
|
|
229
|
+
if (token && verifyToken(token, 'my-secret-token')) return 'deep';
|
|
230
|
+
return 'shallow';
|
|
231
|
+
},
|
|
212
232
|
metadata: { // optional — included in shallow & deep responses
|
|
213
233
|
build: 'stg-45d76e5',
|
|
214
234
|
},
|
|
@@ -222,7 +242,14 @@ import { createNextHandler } from '@firebreak/vitals/next';
|
|
|
222
242
|
|
|
223
243
|
export const GET = createNextHandler({
|
|
224
244
|
registry,
|
|
225
|
-
|
|
245
|
+
resolveDepth: (req) => {
|
|
246
|
+
const token = extractToken({
|
|
247
|
+
queryParams: req.queryParams as Record<string, string>,
|
|
248
|
+
authorizationHeader: req.authorizationHeader as string | null,
|
|
249
|
+
});
|
|
250
|
+
if (token && verifyToken(token, process.env.VITALS_TOKEN!)) return 'deep';
|
|
251
|
+
return 'shallow';
|
|
252
|
+
},
|
|
226
253
|
metadata: { build: process.env.BUILD_SHA },
|
|
227
254
|
});
|
|
228
255
|
```
|
|
@@ -232,59 +259,52 @@ export const GET = createNextHandler({
|
|
|
232
259
|
For other frameworks, use the generic handler directly:
|
|
233
260
|
|
|
234
261
|
```typescript
|
|
235
|
-
import { createHealthcheckHandler } from '@firebreak/vitals';
|
|
262
|
+
import { createHealthcheckHandler, extractToken, verifyToken } from '@firebreak/vitals';
|
|
236
263
|
|
|
237
264
|
const handle = createHealthcheckHandler({
|
|
238
265
|
registry,
|
|
239
|
-
|
|
266
|
+
resolveDepth: (req) => {
|
|
267
|
+
const token = extractToken({
|
|
268
|
+
queryParams: req.queryParams as Record<string, string>,
|
|
269
|
+
authorizationHeader: req.authorizationHeader as string | null,
|
|
270
|
+
});
|
|
271
|
+
if (token && verifyToken(token, process.env.VITALS_TOKEN!)) return 'deep';
|
|
272
|
+
return 'shallow';
|
|
273
|
+
},
|
|
240
274
|
metadata: { build: 'stg-45d76e5' },
|
|
241
275
|
});
|
|
242
276
|
|
|
243
|
-
//
|
|
277
|
+
// resolveDepth returns 'shallow' → shallow response
|
|
244
278
|
const shallow = await handle({});
|
|
245
279
|
// { status: 200, body: { status: 'ok', timestamp: '...', build: 'stg-45d76e5' } }
|
|
246
280
|
|
|
247
|
-
//
|
|
281
|
+
// resolveDepth returns 'deep' → deep response
|
|
248
282
|
const deep = await handle({ queryParams: { token: '...' } });
|
|
249
283
|
// { status: 200, body: { status: 'healthy', timestamp: '...', build: 'stg-45d76e5', checks: { ... } } }
|
|
250
284
|
```
|
|
251
285
|
|
|
252
286
|
### Shallow / Deep Responses
|
|
253
287
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
| Request | Response |
|
|
257
|
-
|---------|----------|
|
|
258
|
-
| No token provided | **Shallow** — `200` with `{ status: "ok", timestamp, ...metadata }` |
|
|
259
|
-
| Valid token provided | **Deep** — `200`/`503` with full check results + metadata |
|
|
260
|
-
| Invalid token provided | `403 Forbidden` |
|
|
261
|
-
| No token configured | **Shallow** by default (see `deep` option below) |
|
|
288
|
+
The endpoint serves two tiers from a single route, controlled by the `resolveDepth` option:
|
|
262
289
|
|
|
263
|
-
|
|
290
|
+
| `resolveDepth` returns | Response |
|
|
291
|
+
|------------------------|----------|
|
|
292
|
+
| `'shallow'` (or omitted) | **Shallow** — `200` with `{ status: "ok", timestamp, ...metadata }` |
|
|
293
|
+
| `'deep'` | **Deep** — `200`/`503` with full check results + metadata |
|
|
264
294
|
|
|
265
|
-
|
|
295
|
+
When `resolveDepth` is not provided, the handler always returns a shallow response.
|
|
266
296
|
|
|
267
|
-
|
|
297
|
+
The `resolveDepth` function receives the request object (`{ ip, headers, queryParams, authorizationHeader }`) and returns `'deep'` or `'shallow'` (or a promise of either). This lets you implement any depth policy — token-based, IP-based, header-based, or always-deep for internal services:
|
|
268
298
|
|
|
269
299
|
```typescript
|
|
300
|
+
// Always deep (internal service behind a private network)
|
|
270
301
|
createHealthcheckHandler({
|
|
271
302
|
registry,
|
|
272
|
-
|
|
303
|
+
resolveDepth: () => 'deep',
|
|
273
304
|
});
|
|
274
305
|
```
|
|
275
306
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
> **Security note:** Using `deep: true` with an empty string token (e.g. `token: ''`) will throw an error at handler creation time. This prevents misconfigured environments (e.g. `VITALS_TOKEN=""`) from silently exposing deep healthcheck data. If you intend to run without authentication, explicitly omit the `token` option or set it to `null`.
|
|
279
|
-
|
|
280
|
-
When both `token` and `deep` are set, the `token` takes precedence — callers must still authenticate to see deep results:
|
|
281
|
-
|
|
282
|
-
| Configuration | No token in request | Valid token | Invalid token |
|
|
283
|
-
|---------------|---------------------|-------------|---------------|
|
|
284
|
-
| `token` set, `deep` unset | Shallow | Deep | 403 |
|
|
285
|
-
| `token` set, `deep: true` | Shallow | Deep | 403 |
|
|
286
|
-
| No `token`, `deep` unset | Shallow | — | — |
|
|
287
|
-
| No `token`, `deep: true` | Deep | — | — |
|
|
307
|
+
The shallow response lets load balancers and uptime monitors confirm the process is alive without needing a secret. The deep response is a superset of shallow — it includes everything shallow returns plus the `checks` object and a real health status.
|
|
288
308
|
|
|
289
309
|
### Metadata
|
|
290
310
|
|
|
@@ -293,7 +313,6 @@ Attach static key-value pairs that appear in both shallow and deep responses:
|
|
|
293
313
|
```typescript
|
|
294
314
|
createHealthcheckHandler({
|
|
295
315
|
registry,
|
|
296
|
-
token: process.env.VITALS_TOKEN,
|
|
297
316
|
metadata: {
|
|
298
317
|
build: 'stg-45d76e5',
|
|
299
318
|
buildTimestamp: '2025-02-25T19:41:56Z',
|
|
@@ -304,29 +323,26 @@ createHealthcheckHandler({
|
|
|
304
323
|
|
|
305
324
|
Metadata values can be `string`, `number`, or `boolean`. Keys `status`, `timestamp`, and `checks` are reserved — passing them throws at handler creation time.
|
|
306
325
|
|
|
307
|
-
###
|
|
308
|
-
|
|
309
|
-
Tokens can be provided via:
|
|
310
|
-
- Query parameter: `?token=my-secret-token`
|
|
311
|
-
- Bearer header: `Authorization: Bearer my-secret-token`
|
|
326
|
+
### Utilities
|
|
312
327
|
|
|
313
|
-
|
|
328
|
+
`extractToken` and `verifyToken` are exported helpers for use inside your `resolveDepth` function.
|
|
314
329
|
|
|
315
330
|
```typescript
|
|
316
331
|
import { verifyToken, extractToken } from '@firebreak/vitals';
|
|
317
332
|
|
|
318
|
-
|
|
319
|
-
|
|
333
|
+
// Extract a token from query params (?token=...) or Authorization header
|
|
320
334
|
const token = extractToken({
|
|
321
335
|
queryParams: { token: 'abc' },
|
|
322
336
|
authorizationHeader: 'Bearer abc',
|
|
323
|
-
queryParamName: 'token',
|
|
324
337
|
});
|
|
338
|
+
|
|
339
|
+
// Timing-safe SHA-256 comparison
|
|
340
|
+
verifyToken('provided-token', 'expected-token'); // boolean
|
|
325
341
|
```
|
|
326
342
|
|
|
327
343
|
## Response Format
|
|
328
344
|
|
|
329
|
-
**Shallow response** (
|
|
345
|
+
**Shallow response** (`resolveDepth` returns `'shallow'` or is omitted):
|
|
330
346
|
|
|
331
347
|
```json
|
|
332
348
|
{
|
|
@@ -337,7 +353,7 @@ const token = extractToken({
|
|
|
337
353
|
}
|
|
338
354
|
```
|
|
339
355
|
|
|
340
|
-
**Deep response** (
|
|
356
|
+
**Deep response** (`resolveDepth` returns `'deep'`):
|
|
341
357
|
|
|
342
358
|
```json
|
|
343
359
|
{
|
|
@@ -362,11 +378,10 @@ const token = extractToken({
|
|
|
362
378
|
|
|
363
379
|
| Condition | HTTP Code | Status |
|
|
364
380
|
|-----------|-----------|--------|
|
|
365
|
-
| Shallow
|
|
381
|
+
| Shallow | `200` | `"ok"` |
|
|
366
382
|
| Deep — healthy | `200` | `"healthy"` |
|
|
367
383
|
| Deep — degraded | `503` | `"degraded"` |
|
|
368
384
|
| Deep — outage | `503` | `"outage"` |
|
|
369
|
-
| Invalid token | `403` | — |
|
|
370
385
|
|
|
371
386
|
## Requirements
|
|
372
387
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { b as HealthcheckRegistry, H as HealthcheckResponseJson } from './core-Bee03bJm.js';
|
|
2
|
+
|
|
3
|
+
type ResolveDepthFn = (req: Record<string, unknown>) => 'deep' | 'shallow' | Promise<'deep' | 'shallow'>;
|
|
4
|
+
interface HealthcheckHandlerOptions {
|
|
5
|
+
registry: HealthcheckRegistry;
|
|
6
|
+
resolveDepth?: ResolveDepthFn;
|
|
7
|
+
metadata?: Record<string, string | number | boolean>;
|
|
8
|
+
}
|
|
9
|
+
interface ShallowResponseJson {
|
|
10
|
+
status: 'ok';
|
|
11
|
+
timestamp: string;
|
|
12
|
+
[key: string]: string | number | boolean;
|
|
13
|
+
}
|
|
14
|
+
interface HealthcheckHandlerResult {
|
|
15
|
+
status: number;
|
|
16
|
+
body: HealthcheckResponseJson | ShallowResponseJson;
|
|
17
|
+
}
|
|
18
|
+
declare function createHealthcheckHandler(options: HealthcheckHandlerOptions): (req: Record<string, unknown>) => Promise<HealthcheckHandlerResult>;
|
|
19
|
+
|
|
20
|
+
export { type HealthcheckHandlerOptions as H, type ResolveDepthFn as R, type ShallowResponseJson as S, type HealthcheckHandlerResult as a, createHealthcheckHandler as c };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { b as HealthcheckRegistry, H as HealthcheckResponseJson } from './core-Bee03bJm.cjs';
|
|
2
|
+
|
|
3
|
+
type ResolveDepthFn = (req: Record<string, unknown>) => 'deep' | 'shallow' | Promise<'deep' | 'shallow'>;
|
|
4
|
+
interface HealthcheckHandlerOptions {
|
|
5
|
+
registry: HealthcheckRegistry;
|
|
6
|
+
resolveDepth?: ResolveDepthFn;
|
|
7
|
+
metadata?: Record<string, string | number | boolean>;
|
|
8
|
+
}
|
|
9
|
+
interface ShallowResponseJson {
|
|
10
|
+
status: 'ok';
|
|
11
|
+
timestamp: string;
|
|
12
|
+
[key: string]: string | number | boolean;
|
|
13
|
+
}
|
|
14
|
+
interface HealthcheckHandlerResult {
|
|
15
|
+
status: number;
|
|
16
|
+
body: HealthcheckResponseJson | ShallowResponseJson;
|
|
17
|
+
}
|
|
18
|
+
declare function createHealthcheckHandler(options: HealthcheckHandlerOptions): (req: Record<string, unknown>) => Promise<HealthcheckHandlerResult>;
|
|
19
|
+
|
|
20
|
+
export { type HealthcheckHandlerOptions as H, type ResolveDepthFn as R, type ShallowResponseJson as S, type HealthcheckHandlerResult as a, createHealthcheckHandler as c };
|
package/dist/index.cjs
CHANGED
|
@@ -220,56 +220,36 @@ function extractToken(options) {
|
|
|
220
220
|
// src/handler.ts
|
|
221
221
|
var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
|
|
222
222
|
function createHealthcheckHandler(options) {
|
|
223
|
-
const { registry,
|
|
224
|
-
const isEmptyToken = typeof rawToken === "string" && rawToken.trim() === "";
|
|
225
|
-
if (deep && isEmptyToken) {
|
|
226
|
-
throw new Error(
|
|
227
|
-
"Cannot use `deep: true` with an empty string token. This would expose deep healthcheck data without authentication. Either set a non-empty token or explicitly set `token: null`."
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
const token = rawToken == null || isEmptyToken ? null : rawToken;
|
|
223
|
+
const { registry, resolveDepth, metadata = {} } = options;
|
|
231
224
|
for (const key of Object.keys(metadata)) {
|
|
232
225
|
if (RESERVED_METADATA_KEYS.includes(key)) {
|
|
233
226
|
throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
|
|
234
227
|
}
|
|
235
228
|
}
|
|
236
229
|
return async (req) => {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
230
|
+
let depth = "shallow";
|
|
231
|
+
if (resolveDepth) {
|
|
232
|
+
try {
|
|
233
|
+
const result = await resolveDepth(req);
|
|
234
|
+
if (result === "deep" || result === "shallow") {
|
|
235
|
+
depth = result;
|
|
236
|
+
}
|
|
237
|
+
} catch {
|
|
244
238
|
}
|
|
245
|
-
const body = {
|
|
246
|
-
status: "ok",
|
|
247
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
248
|
-
...metadata
|
|
249
|
-
};
|
|
250
|
-
return { status: 200, body };
|
|
251
239
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (provided === null) {
|
|
258
|
-
const body = {
|
|
259
|
-
status: "ok",
|
|
260
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
261
|
-
...metadata
|
|
240
|
+
if (depth === "deep") {
|
|
241
|
+
const response = await registry.run();
|
|
242
|
+
return {
|
|
243
|
+
status: httpStatusCode(response.status),
|
|
244
|
+
body: { ...metadata, ...toJson(response) }
|
|
262
245
|
};
|
|
263
|
-
return { status: 200, body };
|
|
264
|
-
}
|
|
265
|
-
if (!verifyToken(provided, token)) {
|
|
266
|
-
return { status: 403, body: { error: "Forbidden" } };
|
|
267
246
|
}
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
247
|
+
const body = {
|
|
248
|
+
status: "ok",
|
|
249
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
250
|
+
...metadata
|
|
272
251
|
};
|
|
252
|
+
return { status: 200, body };
|
|
273
253
|
};
|
|
274
254
|
}
|
|
275
255
|
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { H as HealthcheckResponseJson } from './core-Bee03bJm.cjs';
|
|
2
2
|
export { A as AsyncCheckFn, C as CheckInput, a as CheckResult, b as HealthcheckRegistry, c as HealthcheckResponse, S as Status, d as StatusLabel, e as StatusValue, f as SyncCheckFn, h as httpStatusCode, s as statusFromString, g as statusToLabel, i as syncCheck, t as toJson } from './core-Bee03bJm.cjs';
|
|
3
|
-
import { S as ShallowResponseJson } from './handler-
|
|
4
|
-
export { H as HealthcheckHandlerOptions, a as HealthcheckHandlerResult,
|
|
3
|
+
import { S as ShallowResponseJson } from './handler-TZOgZvY7.cjs';
|
|
4
|
+
export { H as HealthcheckHandlerOptions, a as HealthcheckHandlerResult, R as ResolveDepthFn, c as createHealthcheckHandler } from './handler-TZOgZvY7.cjs';
|
|
5
5
|
|
|
6
6
|
declare function verifyToken(provided: string, expected: string): boolean;
|
|
7
7
|
declare function extractToken(options: {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { H as HealthcheckResponseJson } from './core-Bee03bJm.js';
|
|
2
2
|
export { A as AsyncCheckFn, C as CheckInput, a as CheckResult, b as HealthcheckRegistry, c as HealthcheckResponse, S as Status, d as StatusLabel, e as StatusValue, f as SyncCheckFn, h as httpStatusCode, s as statusFromString, g as statusToLabel, i as syncCheck, t as toJson } from './core-Bee03bJm.js';
|
|
3
|
-
import { S as ShallowResponseJson } from './handler-
|
|
4
|
-
export { H as HealthcheckHandlerOptions, a as HealthcheckHandlerResult,
|
|
3
|
+
import { S as ShallowResponseJson } from './handler-BvjN4Ot9.js';
|
|
4
|
+
export { H as HealthcheckHandlerOptions, a as HealthcheckHandlerResult, R as ResolveDepthFn, c as createHealthcheckHandler } from './handler-BvjN4Ot9.js';
|
|
5
5
|
|
|
6
6
|
declare function verifyToken(provided: string, expected: string): boolean;
|
|
7
7
|
declare function extractToken(options: {
|
package/dist/index.mjs
CHANGED
|
@@ -184,56 +184,36 @@ function extractToken(options) {
|
|
|
184
184
|
// src/handler.ts
|
|
185
185
|
var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
|
|
186
186
|
function createHealthcheckHandler(options) {
|
|
187
|
-
const { registry,
|
|
188
|
-
const isEmptyToken = typeof rawToken === "string" && rawToken.trim() === "";
|
|
189
|
-
if (deep && isEmptyToken) {
|
|
190
|
-
throw new Error(
|
|
191
|
-
"Cannot use `deep: true` with an empty string token. This would expose deep healthcheck data without authentication. Either set a non-empty token or explicitly set `token: null`."
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
const token = rawToken == null || isEmptyToken ? null : rawToken;
|
|
187
|
+
const { registry, resolveDepth, metadata = {} } = options;
|
|
195
188
|
for (const key of Object.keys(metadata)) {
|
|
196
189
|
if (RESERVED_METADATA_KEYS.includes(key)) {
|
|
197
190
|
throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
|
|
198
191
|
}
|
|
199
192
|
}
|
|
200
193
|
return async (req) => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
194
|
+
let depth = "shallow";
|
|
195
|
+
if (resolveDepth) {
|
|
196
|
+
try {
|
|
197
|
+
const result = await resolveDepth(req);
|
|
198
|
+
if (result === "deep" || result === "shallow") {
|
|
199
|
+
depth = result;
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
208
202
|
}
|
|
209
|
-
const body = {
|
|
210
|
-
status: "ok",
|
|
211
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
212
|
-
...metadata
|
|
213
|
-
};
|
|
214
|
-
return { status: 200, body };
|
|
215
203
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (provided === null) {
|
|
222
|
-
const body = {
|
|
223
|
-
status: "ok",
|
|
224
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
225
|
-
...metadata
|
|
204
|
+
if (depth === "deep") {
|
|
205
|
+
const response = await registry.run();
|
|
206
|
+
return {
|
|
207
|
+
status: httpStatusCode(response.status),
|
|
208
|
+
body: { ...metadata, ...toJson(response) }
|
|
226
209
|
};
|
|
227
|
-
return { status: 200, body };
|
|
228
|
-
}
|
|
229
|
-
if (!verifyToken(provided, token)) {
|
|
230
|
-
return { status: 403, body: { error: "Forbidden" } };
|
|
231
210
|
}
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
211
|
+
const body = {
|
|
212
|
+
status: "ok",
|
|
213
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
214
|
+
...metadata
|
|
236
215
|
};
|
|
216
|
+
return { status: 200, body };
|
|
237
217
|
};
|
|
238
218
|
}
|
|
239
219
|
|
|
@@ -24,28 +24,6 @@ __export(express_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(express_exports);
|
|
26
26
|
|
|
27
|
-
// src/auth.ts
|
|
28
|
-
var import_node_crypto = require("crypto");
|
|
29
|
-
function verifyToken(provided, expected) {
|
|
30
|
-
if (!provided || !expected) return false;
|
|
31
|
-
const providedHash = (0, import_node_crypto.createHash)("sha256").update(provided).digest();
|
|
32
|
-
const expectedHash = (0, import_node_crypto.createHash)("sha256").update(expected).digest();
|
|
33
|
-
return (0, import_node_crypto.timingSafeEqual)(providedHash, expectedHash);
|
|
34
|
-
}
|
|
35
|
-
function extractToken(options) {
|
|
36
|
-
const { queryParams, authorizationHeader, queryParamName = "token" } = options;
|
|
37
|
-
if (queryParams) {
|
|
38
|
-
const value = queryParams[queryParamName];
|
|
39
|
-
const str = Array.isArray(value) ? value[0] : value;
|
|
40
|
-
if (str) return str;
|
|
41
|
-
}
|
|
42
|
-
if (authorizationHeader?.startsWith("Bearer ")) {
|
|
43
|
-
const token = authorizationHeader.slice("Bearer ".length);
|
|
44
|
-
if (token) return token;
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
27
|
// src/types.ts
|
|
50
28
|
var Status = {
|
|
51
29
|
HEALTHY: 2,
|
|
@@ -83,56 +61,36 @@ function httpStatusCode(status) {
|
|
|
83
61
|
// src/handler.ts
|
|
84
62
|
var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
|
|
85
63
|
function createHealthcheckHandler(options) {
|
|
86
|
-
const { registry,
|
|
87
|
-
const isEmptyToken = typeof rawToken === "string" && rawToken.trim() === "";
|
|
88
|
-
if (deep && isEmptyToken) {
|
|
89
|
-
throw new Error(
|
|
90
|
-
"Cannot use `deep: true` with an empty string token. This would expose deep healthcheck data without authentication. Either set a non-empty token or explicitly set `token: null`."
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
const token = rawToken == null || isEmptyToken ? null : rawToken;
|
|
64
|
+
const { registry, resolveDepth, metadata = {} } = options;
|
|
94
65
|
for (const key of Object.keys(metadata)) {
|
|
95
66
|
if (RESERVED_METADATA_KEYS.includes(key)) {
|
|
96
67
|
throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
|
|
97
68
|
}
|
|
98
69
|
}
|
|
99
70
|
return async (req) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
71
|
+
let depth = "shallow";
|
|
72
|
+
if (resolveDepth) {
|
|
73
|
+
try {
|
|
74
|
+
const result = await resolveDepth(req);
|
|
75
|
+
if (result === "deep" || result === "shallow") {
|
|
76
|
+
depth = result;
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
107
79
|
}
|
|
108
|
-
const body = {
|
|
109
|
-
status: "ok",
|
|
110
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
111
|
-
...metadata
|
|
112
|
-
};
|
|
113
|
-
return { status: 200, body };
|
|
114
80
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (provided === null) {
|
|
121
|
-
const body = {
|
|
122
|
-
status: "ok",
|
|
123
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
124
|
-
...metadata
|
|
81
|
+
if (depth === "deep") {
|
|
82
|
+
const response = await registry.run();
|
|
83
|
+
return {
|
|
84
|
+
status: httpStatusCode(response.status),
|
|
85
|
+
body: { ...metadata, ...toJson(response) }
|
|
125
86
|
};
|
|
126
|
-
return { status: 200, body };
|
|
127
|
-
}
|
|
128
|
-
if (!verifyToken(provided, token)) {
|
|
129
|
-
return { status: 403, body: { error: "Forbidden" } };
|
|
130
87
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
88
|
+
const body = {
|
|
89
|
+
status: "ok",
|
|
90
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
91
|
+
...metadata
|
|
135
92
|
};
|
|
93
|
+
return { status: 200, body };
|
|
136
94
|
};
|
|
137
95
|
}
|
|
138
96
|
|
|
@@ -376,6 +334,8 @@ function createHealthcheckMiddleware(options) {
|
|
|
376
334
|
return async (req, res) => {
|
|
377
335
|
try {
|
|
378
336
|
const result = await handle({
|
|
337
|
+
ip: req.ip,
|
|
338
|
+
headers: req.headers,
|
|
379
339
|
queryParams: req.query,
|
|
380
340
|
authorizationHeader: req.headers.authorization ?? null
|
|
381
341
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestHandler } from 'express';
|
|
2
|
-
import { H as HealthcheckHandlerOptions } from '../handler-
|
|
2
|
+
import { H as HealthcheckHandlerOptions } from '../handler-TZOgZvY7.cjs';
|
|
3
3
|
import '../core-Bee03bJm.cjs';
|
|
4
4
|
|
|
5
5
|
type HealthcheckMiddlewareOptions = HealthcheckHandlerOptions;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestHandler } from 'express';
|
|
2
|
-
import { H as HealthcheckHandlerOptions } from '../handler-
|
|
2
|
+
import { H as HealthcheckHandlerOptions } from '../handler-BvjN4Ot9.js';
|
|
3
3
|
import '../core-Bee03bJm.js';
|
|
4
4
|
|
|
5
5
|
type HealthcheckMiddlewareOptions = HealthcheckHandlerOptions;
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
// src/auth.ts
|
|
2
|
-
import { createHash, timingSafeEqual } from "crypto";
|
|
3
|
-
function verifyToken(provided, expected) {
|
|
4
|
-
if (!provided || !expected) return false;
|
|
5
|
-
const providedHash = createHash("sha256").update(provided).digest();
|
|
6
|
-
const expectedHash = createHash("sha256").update(expected).digest();
|
|
7
|
-
return timingSafeEqual(providedHash, expectedHash);
|
|
8
|
-
}
|
|
9
|
-
function extractToken(options) {
|
|
10
|
-
const { queryParams, authorizationHeader, queryParamName = "token" } = options;
|
|
11
|
-
if (queryParams) {
|
|
12
|
-
const value = queryParams[queryParamName];
|
|
13
|
-
const str = Array.isArray(value) ? value[0] : value;
|
|
14
|
-
if (str) return str;
|
|
15
|
-
}
|
|
16
|
-
if (authorizationHeader?.startsWith("Bearer ")) {
|
|
17
|
-
const token = authorizationHeader.slice("Bearer ".length);
|
|
18
|
-
if (token) return token;
|
|
19
|
-
}
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
1
|
// src/types.ts
|
|
24
2
|
var Status = {
|
|
25
3
|
HEALTHY: 2,
|
|
@@ -57,56 +35,36 @@ function httpStatusCode(status) {
|
|
|
57
35
|
// src/handler.ts
|
|
58
36
|
var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
|
|
59
37
|
function createHealthcheckHandler(options) {
|
|
60
|
-
const { registry,
|
|
61
|
-
const isEmptyToken = typeof rawToken === "string" && rawToken.trim() === "";
|
|
62
|
-
if (deep && isEmptyToken) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
"Cannot use `deep: true` with an empty string token. This would expose deep healthcheck data without authentication. Either set a non-empty token or explicitly set `token: null`."
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
const token = rawToken == null || isEmptyToken ? null : rawToken;
|
|
38
|
+
const { registry, resolveDepth, metadata = {} } = options;
|
|
68
39
|
for (const key of Object.keys(metadata)) {
|
|
69
40
|
if (RESERVED_METADATA_KEYS.includes(key)) {
|
|
70
41
|
throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
|
|
71
42
|
}
|
|
72
43
|
}
|
|
73
44
|
return async (req) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
45
|
+
let depth = "shallow";
|
|
46
|
+
if (resolveDepth) {
|
|
47
|
+
try {
|
|
48
|
+
const result = await resolveDepth(req);
|
|
49
|
+
if (result === "deep" || result === "shallow") {
|
|
50
|
+
depth = result;
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
81
53
|
}
|
|
82
|
-
const body = {
|
|
83
|
-
status: "ok",
|
|
84
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
85
|
-
...metadata
|
|
86
|
-
};
|
|
87
|
-
return { status: 200, body };
|
|
88
54
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (provided === null) {
|
|
95
|
-
const body = {
|
|
96
|
-
status: "ok",
|
|
97
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
98
|
-
...metadata
|
|
55
|
+
if (depth === "deep") {
|
|
56
|
+
const response = await registry.run();
|
|
57
|
+
return {
|
|
58
|
+
status: httpStatusCode(response.status),
|
|
59
|
+
body: { ...metadata, ...toJson(response) }
|
|
99
60
|
};
|
|
100
|
-
return { status: 200, body };
|
|
101
|
-
}
|
|
102
|
-
if (!verifyToken(provided, token)) {
|
|
103
|
-
return { status: 403, body: { error: "Forbidden" } };
|
|
104
61
|
}
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
62
|
+
const body = {
|
|
63
|
+
status: "ok",
|
|
64
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
65
|
+
...metadata
|
|
109
66
|
};
|
|
67
|
+
return { status: 200, body };
|
|
110
68
|
};
|
|
111
69
|
}
|
|
112
70
|
|
|
@@ -350,6 +308,8 @@ function createHealthcheckMiddleware(options) {
|
|
|
350
308
|
return async (req, res) => {
|
|
351
309
|
try {
|
|
352
310
|
const result = await handle({
|
|
311
|
+
ip: req.ip,
|
|
312
|
+
headers: req.headers,
|
|
353
313
|
queryParams: req.query,
|
|
354
314
|
authorizationHeader: req.headers.authorization ?? null
|
|
355
315
|
});
|
|
@@ -24,28 +24,6 @@ __export(next_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(next_exports);
|
|
26
26
|
|
|
27
|
-
// src/auth.ts
|
|
28
|
-
var import_node_crypto = require("crypto");
|
|
29
|
-
function verifyToken(provided, expected) {
|
|
30
|
-
if (!provided || !expected) return false;
|
|
31
|
-
const providedHash = (0, import_node_crypto.createHash)("sha256").update(provided).digest();
|
|
32
|
-
const expectedHash = (0, import_node_crypto.createHash)("sha256").update(expected).digest();
|
|
33
|
-
return (0, import_node_crypto.timingSafeEqual)(providedHash, expectedHash);
|
|
34
|
-
}
|
|
35
|
-
function extractToken(options) {
|
|
36
|
-
const { queryParams, authorizationHeader, queryParamName = "token" } = options;
|
|
37
|
-
if (queryParams) {
|
|
38
|
-
const value = queryParams[queryParamName];
|
|
39
|
-
const str = Array.isArray(value) ? value[0] : value;
|
|
40
|
-
if (str) return str;
|
|
41
|
-
}
|
|
42
|
-
if (authorizationHeader?.startsWith("Bearer ")) {
|
|
43
|
-
const token = authorizationHeader.slice("Bearer ".length);
|
|
44
|
-
if (token) return token;
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
27
|
// src/types.ts
|
|
50
28
|
var Status = {
|
|
51
29
|
HEALTHY: 2,
|
|
@@ -83,56 +61,36 @@ function httpStatusCode(status) {
|
|
|
83
61
|
// src/handler.ts
|
|
84
62
|
var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
|
|
85
63
|
function createHealthcheckHandler(options) {
|
|
86
|
-
const { registry,
|
|
87
|
-
const isEmptyToken = typeof rawToken === "string" && rawToken.trim() === "";
|
|
88
|
-
if (deep && isEmptyToken) {
|
|
89
|
-
throw new Error(
|
|
90
|
-
"Cannot use `deep: true` with an empty string token. This would expose deep healthcheck data without authentication. Either set a non-empty token or explicitly set `token: null`."
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
const token = rawToken == null || isEmptyToken ? null : rawToken;
|
|
64
|
+
const { registry, resolveDepth, metadata = {} } = options;
|
|
94
65
|
for (const key of Object.keys(metadata)) {
|
|
95
66
|
if (RESERVED_METADATA_KEYS.includes(key)) {
|
|
96
67
|
throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
|
|
97
68
|
}
|
|
98
69
|
}
|
|
99
70
|
return async (req) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
71
|
+
let depth = "shallow";
|
|
72
|
+
if (resolveDepth) {
|
|
73
|
+
try {
|
|
74
|
+
const result = await resolveDepth(req);
|
|
75
|
+
if (result === "deep" || result === "shallow") {
|
|
76
|
+
depth = result;
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
107
79
|
}
|
|
108
|
-
const body = {
|
|
109
|
-
status: "ok",
|
|
110
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
111
|
-
...metadata
|
|
112
|
-
};
|
|
113
|
-
return { status: 200, body };
|
|
114
80
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (provided === null) {
|
|
121
|
-
const body = {
|
|
122
|
-
status: "ok",
|
|
123
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
124
|
-
...metadata
|
|
81
|
+
if (depth === "deep") {
|
|
82
|
+
const response = await registry.run();
|
|
83
|
+
return {
|
|
84
|
+
status: httpStatusCode(response.status),
|
|
85
|
+
body: { ...metadata, ...toJson(response) }
|
|
125
86
|
};
|
|
126
|
-
return { status: 200, body };
|
|
127
|
-
}
|
|
128
|
-
if (!verifyToken(provided, token)) {
|
|
129
|
-
return { status: 403, body: { error: "Forbidden" } };
|
|
130
87
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
88
|
+
const body = {
|
|
89
|
+
status: "ok",
|
|
90
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
91
|
+
...metadata
|
|
135
92
|
};
|
|
93
|
+
return { status: 200, body };
|
|
136
94
|
};
|
|
137
95
|
}
|
|
138
96
|
|
|
@@ -404,6 +362,8 @@ function createNextHandler(options) {
|
|
|
404
362
|
const url = new URL(request.url);
|
|
405
363
|
const queryParams = Object.fromEntries(url.searchParams);
|
|
406
364
|
const result = await handle({
|
|
365
|
+
ip: request.headers.get("x-forwarded-for"),
|
|
366
|
+
headers: Object.fromEntries(request.headers),
|
|
407
367
|
queryParams,
|
|
408
368
|
authorizationHeader: request.headers.get("authorization")
|
|
409
369
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as HealthcheckHandlerOptions } from '../handler-
|
|
1
|
+
import { H as HealthcheckHandlerOptions } from '../handler-TZOgZvY7.cjs';
|
|
2
2
|
import '../core-Bee03bJm.cjs';
|
|
3
3
|
|
|
4
4
|
type NextHealthcheckOptions = HealthcheckHandlerOptions;
|
|
@@ -15,7 +15,7 @@ type NextHealthcheckOptions = HealthcheckHandlerOptions;
|
|
|
15
15
|
*
|
|
16
16
|
* export const GET = createNextHandler({
|
|
17
17
|
* registry,
|
|
18
|
-
*
|
|
18
|
+
* resolveDepth: (req) => req.ip === '10.0.0.1' ? 'deep' : 'shallow',
|
|
19
19
|
* });
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as HealthcheckHandlerOptions } from '../handler-
|
|
1
|
+
import { H as HealthcheckHandlerOptions } from '../handler-BvjN4Ot9.js';
|
|
2
2
|
import '../core-Bee03bJm.js';
|
|
3
3
|
|
|
4
4
|
type NextHealthcheckOptions = HealthcheckHandlerOptions;
|
|
@@ -15,7 +15,7 @@ type NextHealthcheckOptions = HealthcheckHandlerOptions;
|
|
|
15
15
|
*
|
|
16
16
|
* export const GET = createNextHandler({
|
|
17
17
|
* registry,
|
|
18
|
-
*
|
|
18
|
+
* resolveDepth: (req) => req.ip === '10.0.0.1' ? 'deep' : 'shallow',
|
|
19
19
|
* });
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
// src/auth.ts
|
|
2
|
-
import { createHash, timingSafeEqual } from "crypto";
|
|
3
|
-
function verifyToken(provided, expected) {
|
|
4
|
-
if (!provided || !expected) return false;
|
|
5
|
-
const providedHash = createHash("sha256").update(provided).digest();
|
|
6
|
-
const expectedHash = createHash("sha256").update(expected).digest();
|
|
7
|
-
return timingSafeEqual(providedHash, expectedHash);
|
|
8
|
-
}
|
|
9
|
-
function extractToken(options) {
|
|
10
|
-
const { queryParams, authorizationHeader, queryParamName = "token" } = options;
|
|
11
|
-
if (queryParams) {
|
|
12
|
-
const value = queryParams[queryParamName];
|
|
13
|
-
const str = Array.isArray(value) ? value[0] : value;
|
|
14
|
-
if (str) return str;
|
|
15
|
-
}
|
|
16
|
-
if (authorizationHeader?.startsWith("Bearer ")) {
|
|
17
|
-
const token = authorizationHeader.slice("Bearer ".length);
|
|
18
|
-
if (token) return token;
|
|
19
|
-
}
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
1
|
// src/types.ts
|
|
24
2
|
var Status = {
|
|
25
3
|
HEALTHY: 2,
|
|
@@ -57,56 +35,36 @@ function httpStatusCode(status) {
|
|
|
57
35
|
// src/handler.ts
|
|
58
36
|
var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
|
|
59
37
|
function createHealthcheckHandler(options) {
|
|
60
|
-
const { registry,
|
|
61
|
-
const isEmptyToken = typeof rawToken === "string" && rawToken.trim() === "";
|
|
62
|
-
if (deep && isEmptyToken) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
"Cannot use `deep: true` with an empty string token. This would expose deep healthcheck data without authentication. Either set a non-empty token or explicitly set `token: null`."
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
const token = rawToken == null || isEmptyToken ? null : rawToken;
|
|
38
|
+
const { registry, resolveDepth, metadata = {} } = options;
|
|
68
39
|
for (const key of Object.keys(metadata)) {
|
|
69
40
|
if (RESERVED_METADATA_KEYS.includes(key)) {
|
|
70
41
|
throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
|
|
71
42
|
}
|
|
72
43
|
}
|
|
73
44
|
return async (req) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
45
|
+
let depth = "shallow";
|
|
46
|
+
if (resolveDepth) {
|
|
47
|
+
try {
|
|
48
|
+
const result = await resolveDepth(req);
|
|
49
|
+
if (result === "deep" || result === "shallow") {
|
|
50
|
+
depth = result;
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
81
53
|
}
|
|
82
|
-
const body = {
|
|
83
|
-
status: "ok",
|
|
84
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
85
|
-
...metadata
|
|
86
|
-
};
|
|
87
|
-
return { status: 200, body };
|
|
88
54
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (provided === null) {
|
|
95
|
-
const body = {
|
|
96
|
-
status: "ok",
|
|
97
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
98
|
-
...metadata
|
|
55
|
+
if (depth === "deep") {
|
|
56
|
+
const response = await registry.run();
|
|
57
|
+
return {
|
|
58
|
+
status: httpStatusCode(response.status),
|
|
59
|
+
body: { ...metadata, ...toJson(response) }
|
|
99
60
|
};
|
|
100
|
-
return { status: 200, body };
|
|
101
|
-
}
|
|
102
|
-
if (!verifyToken(provided, token)) {
|
|
103
|
-
return { status: 403, body: { error: "Forbidden" } };
|
|
104
61
|
}
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
62
|
+
const body = {
|
|
63
|
+
status: "ok",
|
|
64
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
65
|
+
...metadata
|
|
109
66
|
};
|
|
67
|
+
return { status: 200, body };
|
|
110
68
|
};
|
|
111
69
|
}
|
|
112
70
|
|
|
@@ -378,6 +336,8 @@ function createNextHandler(options) {
|
|
|
378
336
|
const url = new URL(request.url);
|
|
379
337
|
const queryParams = Object.fromEntries(url.searchParams);
|
|
380
338
|
const result = await handle({
|
|
339
|
+
ip: request.headers.get("x-forwarded-for"),
|
|
340
|
+
headers: Object.fromEntries(request.headers),
|
|
381
341
|
queryParams,
|
|
382
342
|
authorizationHeader: request.headers.get("authorization")
|
|
383
343
|
});
|
package/package.json
CHANGED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { b as HealthcheckRegistry, H as HealthcheckResponseJson } from './core-Bee03bJm.cjs';
|
|
2
|
-
|
|
3
|
-
interface HealthcheckHandlerOptions {
|
|
4
|
-
registry: HealthcheckRegistry;
|
|
5
|
-
token?: string | null;
|
|
6
|
-
deep?: boolean;
|
|
7
|
-
queryParamName?: string;
|
|
8
|
-
metadata?: Record<string, string | number | boolean>;
|
|
9
|
-
}
|
|
10
|
-
interface HealthcheckRequest {
|
|
11
|
-
queryParams?: Record<string, string | string[] | undefined>;
|
|
12
|
-
authorizationHeader?: string | null;
|
|
13
|
-
}
|
|
14
|
-
interface ShallowResponseJson {
|
|
15
|
-
status: 'ok';
|
|
16
|
-
timestamp: string;
|
|
17
|
-
[key: string]: string | number | boolean;
|
|
18
|
-
}
|
|
19
|
-
interface HealthcheckHandlerResult {
|
|
20
|
-
status: number;
|
|
21
|
-
body: HealthcheckResponseJson | ShallowResponseJson | {
|
|
22
|
-
error: string;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
declare function createHealthcheckHandler(options: HealthcheckHandlerOptions): (req: HealthcheckRequest) => Promise<HealthcheckHandlerResult>;
|
|
26
|
-
|
|
27
|
-
export { type HealthcheckHandlerOptions as H, type ShallowResponseJson as S, type HealthcheckHandlerResult as a, type HealthcheckRequest as b, createHealthcheckHandler as c };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { b as HealthcheckRegistry, H as HealthcheckResponseJson } from './core-Bee03bJm.js';
|
|
2
|
-
|
|
3
|
-
interface HealthcheckHandlerOptions {
|
|
4
|
-
registry: HealthcheckRegistry;
|
|
5
|
-
token?: string | null;
|
|
6
|
-
deep?: boolean;
|
|
7
|
-
queryParamName?: string;
|
|
8
|
-
metadata?: Record<string, string | number | boolean>;
|
|
9
|
-
}
|
|
10
|
-
interface HealthcheckRequest {
|
|
11
|
-
queryParams?: Record<string, string | string[] | undefined>;
|
|
12
|
-
authorizationHeader?: string | null;
|
|
13
|
-
}
|
|
14
|
-
interface ShallowResponseJson {
|
|
15
|
-
status: 'ok';
|
|
16
|
-
timestamp: string;
|
|
17
|
-
[key: string]: string | number | boolean;
|
|
18
|
-
}
|
|
19
|
-
interface HealthcheckHandlerResult {
|
|
20
|
-
status: number;
|
|
21
|
-
body: HealthcheckResponseJson | ShallowResponseJson | {
|
|
22
|
-
error: string;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
declare function createHealthcheckHandler(options: HealthcheckHandlerOptions): (req: HealthcheckRequest) => Promise<HealthcheckHandlerResult>;
|
|
26
|
-
|
|
27
|
-
export { type HealthcheckHandlerOptions as H, type ShallowResponseJson as S, type HealthcheckHandlerResult as a, type HealthcheckRequest as b, createHealthcheckHandler as c };
|