@firebreak/vitals 1.0.2 → 1.1.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 +115 -46
- package/dist/checks/databricks.cjs +57 -0
- package/dist/checks/databricks.d.cts +23 -0
- package/dist/checks/databricks.d.ts +23 -0
- package/dist/checks/databricks.mjs +30 -0
- package/dist/checks/http.cjs +63 -0
- package/dist/checks/http.d.cts +17 -0
- package/dist/checks/http.d.ts +17 -0
- package/dist/checks/http.mjs +36 -0
- package/dist/checks/postgres.cjs +4 -11
- package/dist/checks/postgres.d.cts +1 -1
- package/dist/checks/postgres.d.ts +1 -1
- package/dist/checks/postgres.mjs +4 -13
- package/dist/checks/redis.cjs +4 -11
- package/dist/checks/redis.d.cts +1 -1
- package/dist/checks/redis.d.ts +1 -1
- package/dist/checks/redis.mjs +4 -13
- package/dist/{core-9-MXAO0I.d.cts → core-BJ2Z0rRi.d.cts} +13 -3
- package/dist/{core-9-MXAO0I.d.ts → core-BJ2Z0rRi.d.ts} +13 -3
- package/dist/handler-Bccbso4b.d.cts +25 -0
- package/dist/handler-D0nYVQvu.d.ts +25 -0
- package/dist/index.cjs +24 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.mjs +23 -0
- package/dist/integrations/express.cjs +28 -14
- package/dist/integrations/express.d.cts +3 -6
- package/dist/integrations/express.d.ts +3 -6
- package/dist/integrations/express.mjs +28 -14
- package/dist/integrations/next.cjs +121 -0
- package/dist/integrations/next.d.cts +24 -0
- package/dist/integrations/next.d.ts +24 -0
- package/dist/integrations/next.mjs +94 -0
- package/package.json +32 -2
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Vitals (Node.js)
|
|
2
2
|
|
|
3
|
-
Structured deep healthcheck endpoints for Node.js services. Register health checks, run them concurrently with timeouts, and expose them via Express.
|
|
3
|
+
Structured deep healthcheck endpoints for Node.js services. Register health checks, run them concurrently with timeouts, and expose them via Express or Next.js.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install vitals
|
|
8
|
+
npm install @firebreak/vitals
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Peer dependencies are optional — install only what you need:
|
|
@@ -16,40 +16,51 @@ npm install ioredis # for Redis checks
|
|
|
16
16
|
npm install express # for Express integration
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
## Quick Start
|
|
19
|
+
## Quick Start (Express)
|
|
20
20
|
|
|
21
21
|
```typescript
|
|
22
|
-
import { HealthcheckRegistry
|
|
23
|
-
import { createHealthcheckMiddleware } from 'vitals/express';
|
|
24
|
-
import { pgPoolCheck } from 'vitals/checks/postgres';
|
|
25
|
-
import { ioredisClientCheck } from 'vitals/checks/redis';
|
|
22
|
+
import { HealthcheckRegistry } from '@firebreak/vitals';
|
|
23
|
+
import { createHealthcheckMiddleware } from '@firebreak/vitals/express';
|
|
24
|
+
import { pgPoolCheck } from '@firebreak/vitals/checks/postgres';
|
|
25
|
+
import { ioredisClientCheck } from '@firebreak/vitals/checks/redis';
|
|
26
|
+
import { httpCheck } from '@firebreak/vitals/checks/http';
|
|
26
27
|
import express from 'express';
|
|
27
28
|
|
|
28
29
|
const registry = new HealthcheckRegistry({ defaultTimeout: 5000 });
|
|
29
30
|
|
|
30
|
-
// Add checks using an existing connection pool / client
|
|
31
31
|
registry.add('postgres', pgPoolCheck(pool));
|
|
32
32
|
registry.add('redis', ioredisClientCheck(redisClient));
|
|
33
|
+
registry.add('api', httpCheck('https://api.example.com/health'));
|
|
33
34
|
|
|
34
|
-
// Or add a custom check
|
|
35
|
-
registry.add('api', async () => {
|
|
36
|
-
const start = Date.now();
|
|
37
|
-
const res = await fetch('https://api.example.com/ping');
|
|
38
|
-
return {
|
|
39
|
-
status: res.ok ? Status.HEALTHY : Status.OUTAGE,
|
|
40
|
-
latencyMs: Date.now() - start,
|
|
41
|
-
message: res.ok ? '' : `HTTP ${res.status}`,
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Mount as Express middleware
|
|
46
35
|
const app = express();
|
|
47
|
-
app.get('/
|
|
36
|
+
app.get('/vitals', createHealthcheckMiddleware({
|
|
48
37
|
registry,
|
|
49
|
-
token: process.env.
|
|
38
|
+
token: process.env.VITALS_TOKEN,
|
|
50
39
|
}));
|
|
51
40
|
```
|
|
52
41
|
|
|
42
|
+
## Quick Start (Next.js)
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// app/vitals/route.ts
|
|
46
|
+
import { HealthcheckRegistry } from '@firebreak/vitals';
|
|
47
|
+
import { createNextHandler } from '@firebreak/vitals/next';
|
|
48
|
+
import { pgClientCheck } from '@firebreak/vitals/checks/postgres';
|
|
49
|
+
import { httpCheck } from '@firebreak/vitals/checks/http';
|
|
50
|
+
|
|
51
|
+
const registry = new HealthcheckRegistry({ defaultTimeout: 5000 });
|
|
52
|
+
|
|
53
|
+
if (process.env.DATABASE_URL) {
|
|
54
|
+
registry.add('postgres', pgClientCheck(process.env.DATABASE_URL));
|
|
55
|
+
}
|
|
56
|
+
registry.add('api', httpCheck(`${process.env.API_BASE_URL}/health`));
|
|
57
|
+
|
|
58
|
+
export const GET = createNextHandler({
|
|
59
|
+
registry,
|
|
60
|
+
token: process.env.VITALS_TOKEN,
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
53
64
|
## API
|
|
54
65
|
|
|
55
66
|
### `HealthcheckRegistry`
|
|
@@ -65,18 +76,19 @@ Register a named async check function. Options: `{ timeout?: number }`.
|
|
|
65
76
|
```typescript
|
|
66
77
|
registry.add('service', async () => ({
|
|
67
78
|
status: Status.HEALTHY,
|
|
68
|
-
latencyMs: 0,
|
|
69
79
|
message: '',
|
|
70
80
|
}), { timeout: 3000 });
|
|
71
81
|
```
|
|
72
82
|
|
|
83
|
+
Check functions return a `CheckInput` with `status` and `message`. The `latencyMs` field is optional — the registry measures wall-clock latency automatically using `performance.now()`.
|
|
84
|
+
|
|
73
85
|
**`registry.check(name, checkFn?, options?)`**
|
|
74
86
|
|
|
75
87
|
Decorator-style registration. Can be used as a method decorator or called directly.
|
|
76
88
|
|
|
77
89
|
**`registry.run()`**
|
|
78
90
|
|
|
79
|
-
Execute all registered checks concurrently. Returns a `HealthcheckResponse` with the worst overall status.
|
|
91
|
+
Execute all registered checks concurrently. Returns a `HealthcheckResponse` with the worst overall status and registry-measured latency for each check.
|
|
80
92
|
|
|
81
93
|
```typescript
|
|
82
94
|
const response = await registry.run();
|
|
@@ -89,7 +101,7 @@ const json = toJson(response);
|
|
|
89
101
|
### Status
|
|
90
102
|
|
|
91
103
|
```typescript
|
|
92
|
-
import { Status, statusToLabel, statusFromString, httpStatusCode } from 'vitals';
|
|
104
|
+
import { Status, statusToLabel, statusFromString, httpStatusCode } from '@firebreak/vitals';
|
|
93
105
|
|
|
94
106
|
Status.HEALTHY // 2
|
|
95
107
|
Status.DEGRADED // 1
|
|
@@ -103,10 +115,26 @@ httpStatusCode(Status.OUTAGE) // 503
|
|
|
103
115
|
|
|
104
116
|
### Built-in Checks
|
|
105
117
|
|
|
118
|
+
#### HTTP
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { httpCheck } from '@firebreak/vitals/checks/http';
|
|
122
|
+
|
|
123
|
+
// Basic URL check — returns HEALTHY if response is 2xx
|
|
124
|
+
registry.add('api', httpCheck('https://api.example.com/health'));
|
|
125
|
+
|
|
126
|
+
// With options
|
|
127
|
+
registry.add('api', httpCheck('https://api.example.com/health', {
|
|
128
|
+
method: 'HEAD',
|
|
129
|
+
headers: { 'X-Api-Key': 'secret' },
|
|
130
|
+
timeout: 3000, // default: 5000ms
|
|
131
|
+
}));
|
|
132
|
+
```
|
|
133
|
+
|
|
106
134
|
#### PostgreSQL
|
|
107
135
|
|
|
108
136
|
```typescript
|
|
109
|
-
import { pgClientCheck, pgPoolCheck } from 'vitals/checks/postgres';
|
|
137
|
+
import { pgClientCheck, pgPoolCheck } from '@firebreak/vitals/checks/postgres';
|
|
110
138
|
|
|
111
139
|
// Fresh connection each time (good for validating connectivity)
|
|
112
140
|
registry.add('pg', pgClientCheck('postgresql://localhost:5432/mydb'));
|
|
@@ -118,7 +146,7 @@ registry.add('pg', pgPoolCheck(pool));
|
|
|
118
146
|
#### Redis
|
|
119
147
|
|
|
120
148
|
```typescript
|
|
121
|
-
import { ioredisCheck, ioredisClientCheck } from 'vitals/checks/redis';
|
|
149
|
+
import { ioredisCheck, ioredisClientCheck } from '@firebreak/vitals/checks/redis';
|
|
122
150
|
|
|
123
151
|
// Fresh connection each time
|
|
124
152
|
registry.add('redis', ioredisCheck('redis://localhost:6379'));
|
|
@@ -127,48 +155,89 @@ registry.add('redis', ioredisCheck('redis://localhost:6379'));
|
|
|
127
155
|
registry.add('redis', ioredisClientCheck(client));
|
|
128
156
|
```
|
|
129
157
|
|
|
158
|
+
### Custom Checks
|
|
159
|
+
|
|
160
|
+
Check functions only need to return `status` and `message`. The registry measures latency automatically:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
registry.add('databricks', async () => {
|
|
164
|
+
try {
|
|
165
|
+
await databricksService.query('SELECT 1');
|
|
166
|
+
return { status: Status.HEALTHY, message: '' };
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return { status: Status.OUTAGE, message: error.message };
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
130
173
|
### Sync Checks
|
|
131
174
|
|
|
132
175
|
Wrap synchronous functions to use as health checks:
|
|
133
176
|
|
|
134
177
|
```typescript
|
|
135
|
-
import { syncCheck, Status } from 'vitals';
|
|
136
|
-
|
|
137
|
-
registry.add('disk', syncCheck(() => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
latencyMs: 0,
|
|
142
|
-
message: `${free} bytes free`,
|
|
143
|
-
};
|
|
144
|
-
}));
|
|
178
|
+
import { syncCheck, Status } from '@firebreak/vitals';
|
|
179
|
+
|
|
180
|
+
registry.add('disk', syncCheck(() => ({
|
|
181
|
+
status: checkDiskSpace('/') > 1_000_000_000 ? Status.HEALTHY : Status.DEGRADED,
|
|
182
|
+
message: '',
|
|
183
|
+
})));
|
|
145
184
|
```
|
|
146
185
|
|
|
147
|
-
###
|
|
186
|
+
### Framework Integrations
|
|
187
|
+
|
|
188
|
+
#### Express
|
|
148
189
|
|
|
149
190
|
```typescript
|
|
150
|
-
import { createHealthcheckMiddleware } from 'vitals/express';
|
|
191
|
+
import { createHealthcheckMiddleware } from '@firebreak/vitals/express';
|
|
151
192
|
|
|
152
|
-
app.get('/
|
|
193
|
+
app.get('/vitals', createHealthcheckMiddleware({
|
|
153
194
|
registry,
|
|
154
195
|
token: 'my-secret-token', // optional — omit to disable auth
|
|
155
196
|
queryParamName: 'token', // default: 'token'
|
|
156
197
|
}));
|
|
157
198
|
```
|
|
158
199
|
|
|
200
|
+
#### Next.js (App Router)
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { createNextHandler } from '@firebreak/vitals/next';
|
|
204
|
+
|
|
205
|
+
export const GET = createNextHandler({
|
|
206
|
+
registry,
|
|
207
|
+
token: process.env.VITALS_TOKEN,
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Framework-Agnostic Handler
|
|
212
|
+
|
|
213
|
+
For other frameworks, use the generic handler directly:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { createHealthcheckHandler } from '@firebreak/vitals';
|
|
217
|
+
|
|
218
|
+
const handle = createHealthcheckHandler({ registry, token: process.env.VITALS_TOKEN });
|
|
219
|
+
|
|
220
|
+
// In any framework:
|
|
221
|
+
const result = await handle({
|
|
222
|
+
queryParams: { token: 'abc' },
|
|
223
|
+
authorizationHeader: 'Bearer abc',
|
|
224
|
+
});
|
|
225
|
+
// result = { status: 200, body: { status: 'healthy', ... } }
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Authentication
|
|
229
|
+
|
|
159
230
|
When a token is configured, requests must provide it via:
|
|
160
231
|
- Query parameter: `?token=my-secret-token`
|
|
161
232
|
- Bearer header: `Authorization: Bearer my-secret-token`
|
|
162
233
|
|
|
163
|
-
|
|
234
|
+
Tokens are compared using timing-safe SHA-256 comparison.
|
|
164
235
|
|
|
165
236
|
```typescript
|
|
166
|
-
import { verifyToken, extractToken } from 'vitals';
|
|
237
|
+
import { verifyToken, extractToken } from '@firebreak/vitals';
|
|
167
238
|
|
|
168
|
-
// Timing-safe token comparison
|
|
169
239
|
verifyToken('provided-token', 'expected-token'); // boolean
|
|
170
240
|
|
|
171
|
-
// Extract token from request
|
|
172
241
|
const token = extractToken({
|
|
173
242
|
queryParams: { token: 'abc' },
|
|
174
243
|
authorizationHeader: 'Bearer abc',
|
|
@@ -185,12 +254,12 @@ const token = extractToken({
|
|
|
185
254
|
"checks": {
|
|
186
255
|
"postgres": {
|
|
187
256
|
"status": "healthy",
|
|
188
|
-
"latencyMs": 4
|
|
257
|
+
"latencyMs": 4,
|
|
189
258
|
"message": ""
|
|
190
259
|
},
|
|
191
260
|
"redis": {
|
|
192
261
|
"status": "healthy",
|
|
193
|
-
"latencyMs": 1
|
|
262
|
+
"latencyMs": 1,
|
|
194
263
|
"message": ""
|
|
195
264
|
}
|
|
196
265
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/checks/databricks.ts
|
|
21
|
+
var databricks_exports = {};
|
|
22
|
+
__export(databricks_exports, {
|
|
23
|
+
databricksCheck: () => databricksCheck
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(databricks_exports);
|
|
26
|
+
|
|
27
|
+
// src/types.ts
|
|
28
|
+
var Status = {
|
|
29
|
+
HEALTHY: 2,
|
|
30
|
+
DEGRADED: 1,
|
|
31
|
+
OUTAGE: 0
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/checks/databricks.ts
|
|
35
|
+
function databricksCheck(client) {
|
|
36
|
+
return async () => {
|
|
37
|
+
let session;
|
|
38
|
+
let op;
|
|
39
|
+
try {
|
|
40
|
+
session = await client.openSession();
|
|
41
|
+
op = await session.executeStatement("SELECT 1");
|
|
42
|
+
return { status: Status.HEALTHY, message: "" };
|
|
43
|
+
} catch (error) {
|
|
44
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
45
|
+
return { status: Status.OUTAGE, message };
|
|
46
|
+
} finally {
|
|
47
|
+
op?.close().catch(() => {
|
|
48
|
+
});
|
|
49
|
+
session?.close().catch(() => {
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
55
|
+
0 && (module.exports = {
|
|
56
|
+
databricksCheck
|
|
57
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { A as AsyncCheckFn } from '../core-BJ2Z0rRi.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal interface for a connected Databricks SQL client.
|
|
5
|
+
* Avoids a hard dependency on `@databricks/sql`.
|
|
6
|
+
*/
|
|
7
|
+
interface DatabricksClient {
|
|
8
|
+
openSession(): Promise<DatabricksSession>;
|
|
9
|
+
}
|
|
10
|
+
interface DatabricksSession {
|
|
11
|
+
executeStatement(statement: string, options?: Record<string, unknown>): Promise<DatabricksOperation>;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
interface DatabricksOperation {
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates a healthcheck function that opens a session on an existing
|
|
19
|
+
* Databricks SQL client, runs `SELECT 1`, and closes the session.
|
|
20
|
+
*/
|
|
21
|
+
declare function databricksCheck(client: DatabricksClient): AsyncCheckFn;
|
|
22
|
+
|
|
23
|
+
export { AsyncCheckFn, type DatabricksClient, type DatabricksOperation, type DatabricksSession, databricksCheck };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { A as AsyncCheckFn } from '../core-BJ2Z0rRi.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal interface for a connected Databricks SQL client.
|
|
5
|
+
* Avoids a hard dependency on `@databricks/sql`.
|
|
6
|
+
*/
|
|
7
|
+
interface DatabricksClient {
|
|
8
|
+
openSession(): Promise<DatabricksSession>;
|
|
9
|
+
}
|
|
10
|
+
interface DatabricksSession {
|
|
11
|
+
executeStatement(statement: string, options?: Record<string, unknown>): Promise<DatabricksOperation>;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
interface DatabricksOperation {
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates a healthcheck function that opens a session on an existing
|
|
19
|
+
* Databricks SQL client, runs `SELECT 1`, and closes the session.
|
|
20
|
+
*/
|
|
21
|
+
declare function databricksCheck(client: DatabricksClient): AsyncCheckFn;
|
|
22
|
+
|
|
23
|
+
export { AsyncCheckFn, type DatabricksClient, type DatabricksOperation, type DatabricksSession, databricksCheck };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var Status = {
|
|
3
|
+
HEALTHY: 2,
|
|
4
|
+
DEGRADED: 1,
|
|
5
|
+
OUTAGE: 0
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// src/checks/databricks.ts
|
|
9
|
+
function databricksCheck(client) {
|
|
10
|
+
return async () => {
|
|
11
|
+
let session;
|
|
12
|
+
let op;
|
|
13
|
+
try {
|
|
14
|
+
session = await client.openSession();
|
|
15
|
+
op = await session.executeStatement("SELECT 1");
|
|
16
|
+
return { status: Status.HEALTHY, message: "" };
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
+
return { status: Status.OUTAGE, message };
|
|
20
|
+
} finally {
|
|
21
|
+
op?.close().catch(() => {
|
|
22
|
+
});
|
|
23
|
+
session?.close().catch(() => {
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
databricksCheck
|
|
30
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/checks/http.ts
|
|
21
|
+
var http_exports = {};
|
|
22
|
+
__export(http_exports, {
|
|
23
|
+
httpCheck: () => httpCheck
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(http_exports);
|
|
26
|
+
|
|
27
|
+
// src/types.ts
|
|
28
|
+
var Status = {
|
|
29
|
+
HEALTHY: 2,
|
|
30
|
+
DEGRADED: 1,
|
|
31
|
+
OUTAGE: 0
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/checks/http.ts
|
|
35
|
+
function httpCheck(url, options) {
|
|
36
|
+
const { method = "GET", headers, timeout = 5e3 } = options ?? {};
|
|
37
|
+
return async () => {
|
|
38
|
+
try {
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(url, {
|
|
43
|
+
method,
|
|
44
|
+
headers,
|
|
45
|
+
signal: controller.signal
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
status: res.ok ? Status.HEALTHY : Status.OUTAGE,
|
|
49
|
+
message: res.ok ? "" : `HTTP ${res.status}`
|
|
50
|
+
};
|
|
51
|
+
} finally {
|
|
52
|
+
clearTimeout(timeoutId);
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const message = error instanceof Error ? error.name === "AbortError" ? `Request to ${url} timed out after ${timeout}ms` : error.message : String(error);
|
|
56
|
+
return { status: Status.OUTAGE, message };
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
61
|
+
0 && (module.exports = {
|
|
62
|
+
httpCheck
|
|
63
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { A as AsyncCheckFn } from '../core-BJ2Z0rRi.cjs';
|
|
2
|
+
|
|
3
|
+
interface HttpCheckOptions {
|
|
4
|
+
/** HTTP method to use. Defaults to 'GET'. */
|
|
5
|
+
method?: string;
|
|
6
|
+
/** Request headers to include. */
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
/** Timeout in milliseconds for the fetch call. Defaults to 5000. */
|
|
9
|
+
timeout?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates a healthcheck function that fetches a URL and checks for an ok response.
|
|
13
|
+
* Returns HEALTHY if the response status is 2xx, OUTAGE otherwise.
|
|
14
|
+
*/
|
|
15
|
+
declare function httpCheck(url: string, options?: HttpCheckOptions): AsyncCheckFn;
|
|
16
|
+
|
|
17
|
+
export { AsyncCheckFn, type HttpCheckOptions, httpCheck };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { A as AsyncCheckFn } from '../core-BJ2Z0rRi.js';
|
|
2
|
+
|
|
3
|
+
interface HttpCheckOptions {
|
|
4
|
+
/** HTTP method to use. Defaults to 'GET'. */
|
|
5
|
+
method?: string;
|
|
6
|
+
/** Request headers to include. */
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
/** Timeout in milliseconds for the fetch call. Defaults to 5000. */
|
|
9
|
+
timeout?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates a healthcheck function that fetches a URL and checks for an ok response.
|
|
13
|
+
* Returns HEALTHY if the response status is 2xx, OUTAGE otherwise.
|
|
14
|
+
*/
|
|
15
|
+
declare function httpCheck(url: string, options?: HttpCheckOptions): AsyncCheckFn;
|
|
16
|
+
|
|
17
|
+
export { AsyncCheckFn, type HttpCheckOptions, httpCheck };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var Status = {
|
|
3
|
+
HEALTHY: 2,
|
|
4
|
+
DEGRADED: 1,
|
|
5
|
+
OUTAGE: 0
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// src/checks/http.ts
|
|
9
|
+
function httpCheck(url, options) {
|
|
10
|
+
const { method = "GET", headers, timeout = 5e3 } = options ?? {};
|
|
11
|
+
return async () => {
|
|
12
|
+
try {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch(url, {
|
|
17
|
+
method,
|
|
18
|
+
headers,
|
|
19
|
+
signal: controller.signal
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
status: res.ok ? Status.HEALTHY : Status.OUTAGE,
|
|
23
|
+
message: res.ok ? "" : `HTTP ${res.status}`
|
|
24
|
+
};
|
|
25
|
+
} finally {
|
|
26
|
+
clearTimeout(timeoutId);
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const message = error instanceof Error ? error.name === "AbortError" ? `Request to ${url} timed out after ${timeout}ms` : error.message : String(error);
|
|
30
|
+
return { status: Status.OUTAGE, message };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
httpCheck
|
|
36
|
+
};
|
package/dist/checks/postgres.cjs
CHANGED
|
@@ -34,7 +34,6 @@ __export(postgres_exports, {
|
|
|
34
34
|
pgPoolCheck: () => pgPoolCheck
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(postgres_exports);
|
|
37
|
-
var import_node_perf_hooks = require("perf_hooks");
|
|
38
37
|
|
|
39
38
|
// src/types.ts
|
|
40
39
|
var Status = {
|
|
@@ -46,18 +45,15 @@ var Status = {
|
|
|
46
45
|
// src/checks/postgres.ts
|
|
47
46
|
function pgClientCheck(connectionString, options) {
|
|
48
47
|
return async () => {
|
|
49
|
-
const start = import_node_perf_hooks.performance.now();
|
|
50
48
|
const { Client } = await import("pg");
|
|
51
49
|
const client = new Client({ connectionString, ...options });
|
|
52
50
|
try {
|
|
53
51
|
await client.connect();
|
|
54
52
|
await client.query("SELECT 1");
|
|
55
|
-
|
|
56
|
-
return { status: Status.HEALTHY, latencyMs, message: "" };
|
|
53
|
+
return { status: Status.HEALTHY, message: "" };
|
|
57
54
|
} catch (error) {
|
|
58
|
-
const latencyMs = Math.round(import_node_perf_hooks.performance.now() - start);
|
|
59
55
|
const message = error instanceof Error ? error.message : String(error);
|
|
60
|
-
return { status: Status.OUTAGE,
|
|
56
|
+
return { status: Status.OUTAGE, message };
|
|
61
57
|
} finally {
|
|
62
58
|
client.end().catch(() => {
|
|
63
59
|
});
|
|
@@ -66,17 +62,14 @@ function pgClientCheck(connectionString, options) {
|
|
|
66
62
|
}
|
|
67
63
|
function pgPoolCheck(pool) {
|
|
68
64
|
return async () => {
|
|
69
|
-
const start = import_node_perf_hooks.performance.now();
|
|
70
65
|
let client;
|
|
71
66
|
try {
|
|
72
67
|
client = await pool.connect();
|
|
73
68
|
await client.query("SELECT 1");
|
|
74
|
-
|
|
75
|
-
return { status: Status.HEALTHY, latencyMs, message: "" };
|
|
69
|
+
return { status: Status.HEALTHY, message: "" };
|
|
76
70
|
} catch (error) {
|
|
77
|
-
const latencyMs = Math.round(import_node_perf_hooks.performance.now() - start);
|
|
78
71
|
const message = error instanceof Error ? error.message : String(error);
|
|
79
|
-
return { status: Status.OUTAGE,
|
|
72
|
+
return { status: Status.OUTAGE, message };
|
|
80
73
|
} finally {
|
|
81
74
|
client?.release();
|
|
82
75
|
}
|
package/dist/checks/postgres.mjs
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// src/checks/postgres.ts
|
|
2
|
-
import { performance } from "perf_hooks";
|
|
3
|
-
|
|
4
1
|
// src/types.ts
|
|
5
2
|
var Status = {
|
|
6
3
|
HEALTHY: 2,
|
|
@@ -11,18 +8,15 @@ var Status = {
|
|
|
11
8
|
// src/checks/postgres.ts
|
|
12
9
|
function pgClientCheck(connectionString, options) {
|
|
13
10
|
return async () => {
|
|
14
|
-
const start = performance.now();
|
|
15
11
|
const { Client } = await import("pg");
|
|
16
12
|
const client = new Client({ connectionString, ...options });
|
|
17
13
|
try {
|
|
18
14
|
await client.connect();
|
|
19
15
|
await client.query("SELECT 1");
|
|
20
|
-
|
|
21
|
-
return { status: Status.HEALTHY, latencyMs, message: "" };
|
|
16
|
+
return { status: Status.HEALTHY, message: "" };
|
|
22
17
|
} catch (error) {
|
|
23
|
-
const latencyMs = Math.round(performance.now() - start);
|
|
24
18
|
const message = error instanceof Error ? error.message : String(error);
|
|
25
|
-
return { status: Status.OUTAGE,
|
|
19
|
+
return { status: Status.OUTAGE, message };
|
|
26
20
|
} finally {
|
|
27
21
|
client.end().catch(() => {
|
|
28
22
|
});
|
|
@@ -31,17 +25,14 @@ function pgClientCheck(connectionString, options) {
|
|
|
31
25
|
}
|
|
32
26
|
function pgPoolCheck(pool) {
|
|
33
27
|
return async () => {
|
|
34
|
-
const start = performance.now();
|
|
35
28
|
let client;
|
|
36
29
|
try {
|
|
37
30
|
client = await pool.connect();
|
|
38
31
|
await client.query("SELECT 1");
|
|
39
|
-
|
|
40
|
-
return { status: Status.HEALTHY, latencyMs, message: "" };
|
|
32
|
+
return { status: Status.HEALTHY, message: "" };
|
|
41
33
|
} catch (error) {
|
|
42
|
-
const latencyMs = Math.round(performance.now() - start);
|
|
43
34
|
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
-
return { status: Status.OUTAGE,
|
|
35
|
+
return { status: Status.OUTAGE, message };
|
|
45
36
|
} finally {
|
|
46
37
|
client?.release();
|
|
47
38
|
}
|