@firebreak/vitals 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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. Unauthenticated callers get a lightweight "alive" response with optional metadata; authenticated callers get full diagnostic results.
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
- token: process.env.VITALS_TOKEN,
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
- Provide the token (`?token=...` or `Authorization: Bearer ...`) to get the full deep response with check results.
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
- token: process.env.VITALS_TOKEN,
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
- token: 'my-secret-token', // optional — omit to disable auth
211
- queryParamName: 'token', // default: 'token'
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
- token: process.env.VITALS_TOKEN,
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,101 +259,120 @@ 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
- token: process.env.VITALS_TOKEN,
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
- // No token → shallow response
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
- // Valid token → deep response
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
- When a `token` is configured, the endpoint serves two tiers from a single route:
288
+ The endpoint serves two tiers from a single route, controlled by the `resolveDepth` option:
255
289
 
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) |
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 |
262
294
 
263
- 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.
264
-
265
- #### The `deep` Option
295
+ When `resolveDepth` is not provided, the handler always returns a shallow response.
266
296
 
267
- When no `token` is configured, the handler returns a **shallow** response by default. To expose full healthcheck results without requiring token authentication, set `deep: true`:
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
- deep: true, // no token → always return deep response
303
+ resolveDepth: () => 'deep',
273
304
  });
274
305
  ```
275
306
 
276
- This is useful for internal services behind a private network where token auth is unnecessary. The `deep` option defaults to `false`.
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.
277
308
 
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`.
309
+ ### Metadata
279
310
 
280
- When both `token` and `deep` are set, the `token` takes precedence — callers must still authenticate to see deep results:
311
+ Attach static key-value pairs that appear in both shallow and deep responses:
281
312
 
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 | — | — |
313
+ ```typescript
314
+ createHealthcheckHandler({
315
+ registry,
316
+ metadata: {
317
+ build: 'stg-45d76e5',
318
+ buildTimestamp: '2025-02-25T19:41:56Z',
319
+ region: 'us-east-1',
320
+ },
321
+ });
322
+ ```
288
323
 
289
- ### Metadata
324
+ Metadata values can be `string`, `number`, or `boolean`. Keys `status`, `timestamp`, `checks`, and `cachedAt` are reserved -- passing them throws at handler creation time.
290
325
 
291
- Attach static key-value pairs that appear in both shallow and deep responses:
326
+ #### `shallowMetadata`
327
+
328
+ By default, shallow responses include all `metadata` fields. Use `shallowMetadata` to override what appears in shallow responses while keeping `metadata` for deep responses:
292
329
 
293
330
  ```typescript
294
331
  createHealthcheckHandler({
295
332
  registry,
296
- token: process.env.VITALS_TOKEN,
297
333
  metadata: {
298
334
  build: 'stg-45d76e5',
299
335
  buildTimestamp: '2025-02-25T19:41:56Z',
300
336
  region: 'us-east-1',
301
337
  },
338
+ // Shallow responses only include status + timestamp (no build info)
339
+ shallowMetadata: {},
302
340
  });
303
341
  ```
304
342
 
305
- Metadata values can be `string`, `number`, or `boolean`. Keys `status`, `timestamp`, and `checks` are reserved — passing them throws at handler creation time.
343
+ You can also include a subset of fields:
344
+
345
+ ```typescript
346
+ createHealthcheckHandler({
347
+ registry,
348
+ metadata: { build: 'stg-45d76e5', region: 'us-east-1', version: '1.0.0' },
349
+ // Shallow responses include only version
350
+ shallowMetadata: { version: '1.0.0' },
351
+ });
352
+ ```
306
353
 
307
- ### Authentication
354
+ When `shallowMetadata` is omitted, shallow responses fall back to using `metadata` (existing behavior).
308
355
 
309
- Tokens can be provided via:
310
- - Query parameter: `?token=my-secret-token`
311
- - Bearer header: `Authorization: Bearer my-secret-token`
356
+ ### Utilities
312
357
 
313
- Tokens are compared using timing-safe SHA-256 comparison.
358
+ `extractToken` and `verifyToken` are exported helpers for use inside your `resolveDepth` function.
314
359
 
315
360
  ```typescript
316
361
  import { verifyToken, extractToken } from '@firebreak/vitals';
317
362
 
318
- verifyToken('provided-token', 'expected-token'); // boolean
319
-
363
+ // Extract a token from query params (?token=...) or Authorization header
320
364
  const token = extractToken({
321
365
  queryParams: { token: 'abc' },
322
366
  authorizationHeader: 'Bearer abc',
323
- queryParamName: 'token',
324
367
  });
368
+
369
+ // Timing-safe SHA-256 comparison
370
+ verifyToken('provided-token', 'expected-token'); // boolean
325
371
  ```
326
372
 
327
373
  ## Response Format
328
374
 
329
- **Shallow response** (no token provided):
375
+ **Shallow response** (`resolveDepth` returns `'shallow'` or is omitted):
330
376
 
331
377
  ```json
332
378
  {
@@ -337,7 +383,7 @@ const token = extractToken({
337
383
  }
338
384
  ```
339
385
 
340
- **Deep response** (valid token provided):
386
+ **Deep response** (`resolveDepth` returns `'deep'`):
341
387
 
342
388
  ```json
343
389
  {
@@ -362,11 +408,10 @@ const token = extractToken({
362
408
 
363
409
  | Condition | HTTP Code | Status |
364
410
  |-----------|-----------|--------|
365
- | Shallow (no token) | `200` | `"ok"` |
411
+ | Shallow | `200` | `"ok"` |
366
412
  | Deep — healthy | `200` | `"healthy"` |
367
413
  | Deep — degraded | `503` | `"degraded"` |
368
414
  | Deep — outage | `503` | `"outage"` |
369
- | Invalid token | `403` | — |
370
415
 
371
416
  ## Requirements
372
417
 
@@ -5,6 +5,7 @@ interface HealthcheckHandlerOptions {
5
5
  registry: HealthcheckRegistry;
6
6
  resolveDepth?: ResolveDepthFn;
7
7
  metadata?: Record<string, string | number | boolean>;
8
+ shallowMetadata?: Record<string, string | number | boolean>;
8
9
  }
9
10
  interface ShallowResponseJson {
10
11
  status: 'ok';
@@ -5,6 +5,7 @@ interface HealthcheckHandlerOptions {
5
5
  registry: HealthcheckRegistry;
6
6
  resolveDepth?: ResolveDepthFn;
7
7
  metadata?: Record<string, string | number | boolean>;
8
+ shallowMetadata?: Record<string, string | number | boolean>;
8
9
  }
9
10
  interface ShallowResponseJson {
10
11
  status: 'ok';
package/dist/index.cjs CHANGED
@@ -220,10 +220,12 @@ 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, resolveDepth, metadata = {} } = options;
224
- for (const key of Object.keys(metadata)) {
225
- if (RESERVED_METADATA_KEYS.includes(key)) {
226
- throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
223
+ const { registry, resolveDepth, metadata = {}, shallowMetadata } = options;
224
+ for (const bag of [metadata, shallowMetadata ?? {}]) {
225
+ for (const key of Object.keys(bag)) {
226
+ if (RESERVED_METADATA_KEYS.includes(key)) {
227
+ throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
228
+ }
227
229
  }
228
230
  }
229
231
  return async (req) => {
@@ -247,7 +249,7 @@ function createHealthcheckHandler(options) {
247
249
  const body = {
248
250
  status: "ok",
249
251
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
250
- ...metadata
252
+ ...shallowMetadata ?? metadata
251
253
  };
252
254
  return { status: 200, body };
253
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-TZOgZvY7.cjs';
4
- export { H as HealthcheckHandlerOptions, a as HealthcheckHandlerResult, R as ResolveDepthFn, c as createHealthcheckHandler } from './handler-TZOgZvY7.cjs';
3
+ import { S as ShallowResponseJson } from './handler-C3eTDuSQ.cjs';
4
+ export { H as HealthcheckHandlerOptions, a as HealthcheckHandlerResult, R as ResolveDepthFn, c as createHealthcheckHandler } from './handler-C3eTDuSQ.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-BvjN4Ot9.js';
4
- export { H as HealthcheckHandlerOptions, a as HealthcheckHandlerResult, R as ResolveDepthFn, c as createHealthcheckHandler } from './handler-BvjN4Ot9.js';
3
+ import { S as ShallowResponseJson } from './handler-DaFJivGK.js';
4
+ export { H as HealthcheckHandlerOptions, a as HealthcheckHandlerResult, R as ResolveDepthFn, c as createHealthcheckHandler } from './handler-DaFJivGK.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,10 +184,12 @@ 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, resolveDepth, metadata = {} } = options;
188
- for (const key of Object.keys(metadata)) {
189
- if (RESERVED_METADATA_KEYS.includes(key)) {
190
- throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
187
+ const { registry, resolveDepth, metadata = {}, shallowMetadata } = options;
188
+ for (const bag of [metadata, shallowMetadata ?? {}]) {
189
+ for (const key of Object.keys(bag)) {
190
+ if (RESERVED_METADATA_KEYS.includes(key)) {
191
+ throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
192
+ }
191
193
  }
192
194
  }
193
195
  return async (req) => {
@@ -211,7 +213,7 @@ function createHealthcheckHandler(options) {
211
213
  const body = {
212
214
  status: "ok",
213
215
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
214
- ...metadata
216
+ ...shallowMetadata ?? metadata
215
217
  };
216
218
  return { status: 200, body };
217
219
  };
@@ -61,10 +61,12 @@ function httpStatusCode(status) {
61
61
  // src/handler.ts
62
62
  var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
63
63
  function createHealthcheckHandler(options) {
64
- const { registry, resolveDepth, metadata = {} } = options;
65
- for (const key of Object.keys(metadata)) {
66
- if (RESERVED_METADATA_KEYS.includes(key)) {
67
- throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
64
+ const { registry, resolveDepth, metadata = {}, shallowMetadata } = options;
65
+ for (const bag of [metadata, shallowMetadata ?? {}]) {
66
+ for (const key of Object.keys(bag)) {
67
+ if (RESERVED_METADATA_KEYS.includes(key)) {
68
+ throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
69
+ }
68
70
  }
69
71
  }
70
72
  return async (req) => {
@@ -88,7 +90,7 @@ function createHealthcheckHandler(options) {
88
90
  const body = {
89
91
  status: "ok",
90
92
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
91
- ...metadata
93
+ ...shallowMetadata ?? metadata
92
94
  };
93
95
  return { status: 200, body };
94
96
  };
@@ -1,5 +1,5 @@
1
1
  import { RequestHandler } from 'express';
2
- import { H as HealthcheckHandlerOptions } from '../handler-TZOgZvY7.cjs';
2
+ import { H as HealthcheckHandlerOptions } from '../handler-C3eTDuSQ.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-BvjN4Ot9.js';
2
+ import { H as HealthcheckHandlerOptions } from '../handler-DaFJivGK.js';
3
3
  import '../core-Bee03bJm.js';
4
4
 
5
5
  type HealthcheckMiddlewareOptions = HealthcheckHandlerOptions;
@@ -35,10 +35,12 @@ function httpStatusCode(status) {
35
35
  // src/handler.ts
36
36
  var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
37
37
  function createHealthcheckHandler(options) {
38
- const { registry, resolveDepth, metadata = {} } = options;
39
- for (const key of Object.keys(metadata)) {
40
- if (RESERVED_METADATA_KEYS.includes(key)) {
41
- throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
38
+ const { registry, resolveDepth, metadata = {}, shallowMetadata } = options;
39
+ for (const bag of [metadata, shallowMetadata ?? {}]) {
40
+ for (const key of Object.keys(bag)) {
41
+ if (RESERVED_METADATA_KEYS.includes(key)) {
42
+ throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
43
+ }
42
44
  }
43
45
  }
44
46
  return async (req) => {
@@ -62,7 +64,7 @@ function createHealthcheckHandler(options) {
62
64
  const body = {
63
65
  status: "ok",
64
66
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
65
- ...metadata
67
+ ...shallowMetadata ?? metadata
66
68
  };
67
69
  return { status: 200, body };
68
70
  };
@@ -61,10 +61,12 @@ function httpStatusCode(status) {
61
61
  // src/handler.ts
62
62
  var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
63
63
  function createHealthcheckHandler(options) {
64
- const { registry, resolveDepth, metadata = {} } = options;
65
- for (const key of Object.keys(metadata)) {
66
- if (RESERVED_METADATA_KEYS.includes(key)) {
67
- throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
64
+ const { registry, resolveDepth, metadata = {}, shallowMetadata } = options;
65
+ for (const bag of [metadata, shallowMetadata ?? {}]) {
66
+ for (const key of Object.keys(bag)) {
67
+ if (RESERVED_METADATA_KEYS.includes(key)) {
68
+ throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
69
+ }
68
70
  }
69
71
  }
70
72
  return async (req) => {
@@ -88,7 +90,7 @@ function createHealthcheckHandler(options) {
88
90
  const body = {
89
91
  status: "ok",
90
92
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
91
- ...metadata
93
+ ...shallowMetadata ?? metadata
92
94
  };
93
95
  return { status: 200, body };
94
96
  };
@@ -1,4 +1,4 @@
1
- import { H as HealthcheckHandlerOptions } from '../handler-TZOgZvY7.cjs';
1
+ import { H as HealthcheckHandlerOptions } from '../handler-C3eTDuSQ.cjs';
2
2
  import '../core-Bee03bJm.cjs';
3
3
 
4
4
  type NextHealthcheckOptions = HealthcheckHandlerOptions;
@@ -1,4 +1,4 @@
1
- import { H as HealthcheckHandlerOptions } from '../handler-BvjN4Ot9.js';
1
+ import { H as HealthcheckHandlerOptions } from '../handler-DaFJivGK.js';
2
2
  import '../core-Bee03bJm.js';
3
3
 
4
4
  type NextHealthcheckOptions = HealthcheckHandlerOptions;
@@ -35,10 +35,12 @@ function httpStatusCode(status) {
35
35
  // src/handler.ts
36
36
  var RESERVED_METADATA_KEYS = ["status", "timestamp", "checks", "cachedAt"];
37
37
  function createHealthcheckHandler(options) {
38
- const { registry, resolveDepth, metadata = {} } = options;
39
- for (const key of Object.keys(metadata)) {
40
- if (RESERVED_METADATA_KEYS.includes(key)) {
41
- throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
38
+ const { registry, resolveDepth, metadata = {}, shallowMetadata } = options;
39
+ for (const bag of [metadata, shallowMetadata ?? {}]) {
40
+ for (const key of Object.keys(bag)) {
41
+ if (RESERVED_METADATA_KEYS.includes(key)) {
42
+ throw new Error(`Metadata key '${key}' is reserved. Use a different key name.`);
43
+ }
42
44
  }
43
45
  }
44
46
  return async (req) => {
@@ -62,7 +64,7 @@ function createHealthcheckHandler(options) {
62
64
  const body = {
63
65
  status: "ok",
64
66
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
65
- ...metadata
67
+ ...shallowMetadata ?? metadata
66
68
  };
67
69
  return { status: 200, body };
68
70
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firebreak/vitals",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Deep healthcheck endpoints following the HEALTHCHECK_SPEC format",
5
5
  "type": "module",
6
6
  "exports": {