@catbee/utils 2.0.5 → 2.2.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.
@@ -26,23 +26,94 @@ import { AsyncLocalStorage } from 'async_hooks';
26
26
 
27
27
  var __defProp = Object.defineProperty;
28
28
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
29
+ var TypedContextKey = class {
30
+ static {
31
+ __name(this, "TypedContextKey");
32
+ }
33
+ symbol;
34
+ defaultValue;
35
+ /**
36
+ * Creates a new typed context key.
37
+ * @param symbol - The unique symbol for this key
38
+ * @param defaultValue - Optional default value if key is not found
39
+ */
40
+ constructor(symbol, defaultValue) {
41
+ this.symbol = symbol;
42
+ this.defaultValue = defaultValue;
43
+ }
44
+ /**
45
+ * Gets the current value for this key.
46
+ * @returns The value or defaultValue if not found
47
+ */
48
+ get() {
49
+ return ContextStore.get(this.symbol) ?? this.defaultValue;
50
+ }
51
+ /**
52
+ * Sets the value for this key.
53
+ * @param value The value to set
54
+ */
55
+ set(value) {
56
+ ContextStore.set(this.symbol, value);
57
+ }
58
+ /**
59
+ * Checks if this key exists in the context.
60
+ * @returns True if the key exists
61
+ */
62
+ exists() {
63
+ return ContextStore.has(this.symbol);
64
+ }
65
+ /**
66
+ * Deletes this key from the context.
67
+ * @returns True if the key was deleted
68
+ */
69
+ delete() {
70
+ return ContextStore.delete(this.symbol);
71
+ }
72
+ /**
73
+ * Gets the symbol for this key.
74
+ * @returns Symbol for this key
75
+ */
76
+ getSymbol() {
77
+ return this.symbol;
78
+ }
79
+ };
29
80
  var StoreKeys = {
30
81
  LOGGER: Symbol("LOGGER"),
31
82
  REQUEST_ID: Symbol("REQUEST_ID"),
32
- USER: Symbol("USER"),
33
- SESSION: Symbol("SESSION"),
34
- TRANSACTION_ID: Symbol("TRANSACTION_ID"),
83
+ CORRELATION_ID: Symbol("CORRELATION_ID"),
35
84
  USER_ID: Symbol("USER_ID"),
85
+ TRANSACTION_ID: Symbol("TRANSACTION_ID"),
36
86
  TENANT_ID: Symbol("TENANT_ID"),
37
87
  TRACE_ID: Symbol("TRACE_ID"),
38
- CORRELATION_ID: Symbol("CORRELATION_ID")
88
+ SPAN_ID: Symbol("SPAN_ID"),
89
+ MESSAGE_ID: Symbol("MESSAGE_ID"),
90
+ MESSAGE_TYPE: Symbol("MESSAGE_TYPE"),
91
+ QUEUE_NAME: Symbol("QUEUE_NAME")
92
+ };
93
+ var TypedStoreKeys = {
94
+ LOGGER: new TypedContextKey(StoreKeys.LOGGER),
95
+ REQUEST_ID: new TypedContextKey(StoreKeys.REQUEST_ID),
96
+ CORRELATION_ID: new TypedContextKey(StoreKeys.CORRELATION_ID),
97
+ USER_ID: new TypedContextKey(StoreKeys.USER_ID),
98
+ TRANSACTION_ID: new TypedContextKey(StoreKeys.TRANSACTION_ID),
99
+ TENANT_ID: new TypedContextKey(StoreKeys.TENANT_ID),
100
+ TRACE_ID: new TypedContextKey(StoreKeys.TRACE_ID),
101
+ SPAN_ID: new TypedContextKey(StoreKeys.SPAN_ID),
102
+ MESSAGE_ID: new TypedContextKey(StoreKeys.MESSAGE_ID),
103
+ MESSAGE_TYPE: new TypedContextKey(StoreKeys.MESSAGE_TYPE),
104
+ QUEUE_NAME: new TypedContextKey(StoreKeys.QUEUE_NAME)
39
105
  };
40
106
  function getRequestId() {
41
- return ContextStore.get(StoreKeys.REQUEST_ID);
107
+ return TypedStoreKeys.REQUEST_ID.get();
42
108
  }
43
109
  __name(getRequestId, "getRequestId");
110
+ function resolveKey(key) {
111
+ return key instanceof TypedContextKey ? key.getSymbol() : key;
112
+ }
113
+ __name(resolveKey, "resolveKey");
44
114
  function getFromContext(key) {
45
- return ContextStore.get(key);
115
+ const symbol = resolveKey(key);
116
+ return ContextStore.get(symbol);
46
117
  }
47
118
  __name(getFromContext, "getFromContext");
48
119
  var ContextStore = class _ContextStore {
@@ -70,8 +141,9 @@ var ContextStore = class _ContextStore {
70
141
  * @returns {T | undefined} The value found (typed) or undefined if not present.
71
142
  */
72
143
  static get(key) {
144
+ const symbol = resolveKey(key);
73
145
  const store = this.storage.getStore();
74
- return store?.[key];
146
+ return store?.[symbol];
75
147
  }
76
148
  /**
77
149
  * Sets a value in the current context store by symbol key.
@@ -82,11 +154,12 @@ var ContextStore = class _ContextStore {
82
154
  * @throws {Error} If called outside an active context (not within a .run call or in the wrong async boundaries).
83
155
  */
84
156
  static set(key, value) {
157
+ const symbol = resolveKey(key);
85
158
  const store = this.storage.getStore();
86
159
  if (!store) {
87
- throw new Error(`Failed to set ${String(key)}: AsyncLocalStorage store is not initialized.`);
160
+ throw new Error(`Failed to set ${String(symbol)}: AsyncLocalStorage store is not initialized.`);
88
161
  }
89
- store[key] = value;
162
+ store[symbol] = value;
90
163
  }
91
164
  /**
92
165
  * Retrieves the entire context store object for the current async context.
@@ -115,8 +188,9 @@ var ContextStore = class _ContextStore {
115
188
  * @returns {boolean} True if the key exists, false otherwise.
116
189
  */
117
190
  static has(key) {
191
+ const symbol = resolveKey(key);
118
192
  const store = this.storage.getStore();
119
- return store !== void 0 && key in store;
193
+ return store !== void 0 && symbol in store;
120
194
  }
121
195
  /**
122
196
  * Removes a value from the current context store by symbol key.
@@ -126,11 +200,12 @@ var ContextStore = class _ContextStore {
126
200
  * @throws {Error} If called outside an active context.
127
201
  */
128
202
  static delete(key) {
203
+ const symbol = resolveKey(key);
129
204
  const store = this.storage.getStore();
130
205
  if (!store) {
131
- throw new Error(`Failed to delete ${String(key)}: AsyncLocalStorage store is not initialized.`);
206
+ throw new Error(`Failed to delete ${String(symbol)}: AsyncLocalStorage store is not initialized.`);
132
207
  }
133
- return delete store[key];
208
+ return delete store[symbol];
134
209
  }
135
210
  /**
136
211
  * Updates multiple values in the current context store at once.
@@ -159,22 +234,16 @@ var ContextStore = class _ContextStore {
159
234
  * @throws {Error} If called outside an active context.
160
235
  */
161
236
  static withValue(key, value, callback) {
162
- const store = this.storage.getStore();
163
- if (!store) {
164
- throw new Error(`Failed to set temporary value: AsyncLocalStorage store is not initialized.`);
165
- }
166
- const hasOriginal = key in store;
167
- const originalValue = store[key];
168
- store[key] = value;
169
- try {
170
- return callback();
171
- } finally {
172
- if (hasOriginal) {
173
- store[key] = originalValue;
174
- } else {
175
- delete store[key];
176
- }
237
+ const symbol = resolveKey(key);
238
+ const currentStore = this.storage.getStore();
239
+ if (!currentStore) {
240
+ throw new Error("Failed to set temporary value: AsyncLocalStorage store is not initialized.");
177
241
  }
242
+ const newStore = {
243
+ ...currentStore,
244
+ [symbol]: value
245
+ };
246
+ return this.storage.run(newStore, callback);
178
247
  }
179
248
  /**
180
249
  * Creates a new context that inherits values from the current context.
@@ -207,54 +276,5 @@ var ContextStore = class _ContextStore {
207
276
  };
208
277
  }
209
278
  };
210
- var TypedContextKey = class {
211
- static {
212
- __name(this, "TypedContextKey");
213
- }
214
- symbol;
215
- defaultValue;
216
- /**
217
- * Creates a new typed context key.
218
- *
219
- * @param symbol - The unique symbol for this key
220
- * @param defaultValue - Optional default value if key is not found
221
- */
222
- constructor(symbol, defaultValue) {
223
- this.symbol = symbol;
224
- this.defaultValue = defaultValue;
225
- }
226
- /**
227
- * Gets the current value for this key.
228
- *
229
- * @returns The value or defaultValue if not found
230
- */
231
- get() {
232
- return ContextStore.get(this.symbol) ?? this.defaultValue;
233
- }
234
- /**
235
- * Sets the value for this key.
236
- *
237
- * @param value The value to set
238
- */
239
- set(value) {
240
- ContextStore.set(this.symbol, value);
241
- }
242
- /**
243
- * Checks if this key exists in the context.
244
- *
245
- * @returns True if the key exists
246
- */
247
- exists() {
248
- return ContextStore.has(this.symbol);
249
- }
250
- /**
251
- * Deletes this key from the context.
252
- *
253
- * @returns True if the key was deleted
254
- */
255
- delete() {
256
- return ContextStore.delete(this.symbol);
257
- }
258
- };
259
279
 
260
- export { ContextStore, StoreKeys, TypedContextKey, getFromContext, getRequestId };
280
+ export { ContextStore, StoreKeys, TypedContextKey, TypedStoreKeys, getFromContext, getRequestId };
@@ -0,0 +1,393 @@
1
+ /*
2
+ * The MIT License
3
+ *
4
+ * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ 'use strict';
26
+
27
+ var http = require('http');
28
+ var env = require('@catbee/utils/env');
29
+
30
+ var __defProp = Object.defineProperty;
31
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
32
+ function getDefaultHealthzConfig() {
33
+ return {
34
+ host: env.Env.get("HEALTHZ_HOST", "") || env.Env.get("SERVER_HEALTHZ_HOST", "") || env.Env.get("SERVER_HOST", "") || env.Env.get("HOST", "0.0.0.0"),
35
+ port: env.Env.getPort("HEALTHZ_PORT", env.Env.getPort("SERVER_HEALTHZ_PORT", 8282)),
36
+ healthzPath: env.Env.get("HEALTHZ_PATH", "") || env.Env.get("SERVER_HEALTHZ_PATH", "") || env.Env.get("SERVER_HEALTH_CHECK_PATH", "/healthz"),
37
+ readyzPath: env.Env.get("HEALTHZ_READYZ_PATH", "") || env.Env.get("SERVER_READYZ_PATH", "/readyz"),
38
+ startupzPath: env.Env.get("HEALTHZ_STARTUPZ_PATH", "") || env.Env.get("SERVER_STARTUPZ_PATH", "/startupz"),
39
+ detailed: env.Env.getBoolean("HEALTHZ_DETAILED", env.Env.getBoolean("SERVER_HEALTHZ_DETAILED", env.Env.getBoolean("SERVER_HEALTH_CHECK_DETAILED_OUTPUT", true))),
40
+ checks: [],
41
+ checkTimeoutMs: env.Env.getDuration("HEALTHZ_CHECK_TIMEOUT_MS", env.Env.getDuration("SERVER_HEALTHZ_CHECK_TIMEOUT_MS", 5e3)),
42
+ shutdownDelayMs: env.Env.getDuration("HEALTHZ_SHUTDOWN_DELAY_MS", env.Env.getDuration("SERVER_HEALTHZ_SHUTDOWN_DELAY_MS", 5e3)),
43
+ handleSignals: true
44
+ };
45
+ }
46
+ __name(getDefaultHealthzConfig, "getDefaultHealthzConfig");
47
+ function resolveConfig(userConfig) {
48
+ const defaults = getDefaultHealthzConfig();
49
+ if (!userConfig) return {
50
+ ...defaults
51
+ };
52
+ const cleaned = Object.fromEntries(Object.entries(userConfig).filter(([, v]) => v !== void 0));
53
+ return {
54
+ ...defaults,
55
+ ...cleaned
56
+ };
57
+ }
58
+ __name(resolveConfig, "resolveConfig");
59
+
60
+ // src/healthz-server/healthz-server.ts
61
+ var SINGLETON_KEY = Symbol.for("CatbeeHealthzServer");
62
+ var _global = globalThis;
63
+ var HealthzServer = class _HealthzServer {
64
+ static {
65
+ __name(this, "HealthzServer");
66
+ }
67
+ server;
68
+ config;
69
+ startedAt = Date.now();
70
+ /** Whether the health server has successfully started listening (for `/startupz`) */
71
+ started = false;
72
+ /** Whether the service is ready to receive traffic (for `/readyz`) */
73
+ ready = false;
74
+ shuttingDown = false;
75
+ onSigterm = /* @__PURE__ */ __name(() => this.initiateShutdown(), "onSigterm");
76
+ onSigint = /* @__PURE__ */ __name(() => this.initiateShutdown(), "onSigint");
77
+ constructor(config) {
78
+ this.config = config;
79
+ this.server = http.createServer((req, res) => this.handleRequest(req, res));
80
+ if (this.config.handleSignals !== false) {
81
+ process.once("SIGTERM", this.onSigterm);
82
+ process.once("SIGINT", this.onSigint);
83
+ }
84
+ }
85
+ /**
86
+ * Returns default Healthz server configuration resolved from environment variables.
87
+ */
88
+ static getDefaultConfig() {
89
+ return getDefaultHealthzConfig();
90
+ }
91
+ /**
92
+ * Boot the health-check server.
93
+ * Returns `null` if a server is already running in this process.
94
+ */
95
+ static async start(opts) {
96
+ if (_global[SINGLETON_KEY]) return null;
97
+ const config = resolveConfig(opts);
98
+ const instance = new _HealthzServer(config);
99
+ _global[SINGLETON_KEY] = instance;
100
+ return new Promise((resolve, reject) => {
101
+ const onError = /* @__PURE__ */ __name((err) => {
102
+ instance.cleanup();
103
+ delete _global[SINGLETON_KEY];
104
+ reject(err);
105
+ }, "onError");
106
+ instance.server.once("error", onError);
107
+ instance.server.listen({
108
+ host: config.host,
109
+ port: config.port
110
+ }, () => {
111
+ instance.server.off("error", onError);
112
+ instance.started = true;
113
+ const addr = instance.server.address();
114
+ if (!addr || typeof addr === "string") {
115
+ instance.cleanup();
116
+ delete _global[SINGLETON_KEY];
117
+ reject(new Error("Failed to resolve health server address"));
118
+ return;
119
+ }
120
+ resolve({
121
+ address: addr.address,
122
+ family: addr.family,
123
+ port: addr.port
124
+ });
125
+ });
126
+ });
127
+ }
128
+ /** Whether the health-check server is currently running and started. */
129
+ static isStarted() {
130
+ return _global[SINGLETON_KEY]?.started ?? false;
131
+ }
132
+ /** Mark the service as ready / not-ready for traffic. */
133
+ static setReady(ready) {
134
+ const instance = _global[SINGLETON_KEY];
135
+ if (!instance) return;
136
+ instance.ready = ready;
137
+ }
138
+ /** Whether the service is currently marked as ready. */
139
+ static isReady() {
140
+ return _global[SINGLETON_KEY]?.ready ?? false;
141
+ }
142
+ /** Gracefully stop the health-check server. */
143
+ static async stop() {
144
+ const instance = _global[SINGLETON_KEY];
145
+ if (!instance) return;
146
+ instance.cleanup();
147
+ return new Promise((resolve) => {
148
+ if (typeof instance.server.closeIdleConnections === "function") {
149
+ instance.server.closeIdleConnections();
150
+ }
151
+ instance.server.close(() => {
152
+ delete _global[SINGLETON_KEY];
153
+ resolve();
154
+ });
155
+ });
156
+ }
157
+ static getInstance() {
158
+ return _global[SINGLETON_KEY];
159
+ }
160
+ /**
161
+ * Register a named check on this HealthzServer instance dynamically.
162
+ *
163
+ * @param check - The named check to register
164
+ * @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
165
+ * @returns This instance for chaining
166
+ */
167
+ registerCheck(check, type = "readiness") {
168
+ if (type === "liveness" || type === "both") {
169
+ this.config.checks.push(check);
170
+ }
171
+ if (type === "readiness" || type === "both") {
172
+ this.config.readinessChecks ??= [
173
+ ...this.config.checks
174
+ ];
175
+ this.config.readinessChecks.push(check);
176
+ }
177
+ return this;
178
+ }
179
+ /**
180
+ * Register a named check on the active singleton HealthzServer instance (if started).
181
+ *
182
+ * @param check - The named check to register
183
+ * @param type - Which probe to attach this check to ('readiness', 'liveness', or 'both')
184
+ */
185
+ static registerCheck(check, type = "readiness") {
186
+ _global[SINGLETON_KEY]?.registerCheck(check, type);
187
+ }
188
+ async handleRequest(req, res) {
189
+ const isHead = req.method === "HEAD";
190
+ if (req.method !== "GET" && !isHead) {
191
+ this.sendJson(res, 405, {
192
+ error: "Method Not Allowed"
193
+ }, isHead);
194
+ return;
195
+ }
196
+ const url = req.url ?? "/";
197
+ try {
198
+ if (url === this.config.healthzPath) {
199
+ await this.handleLiveness(res, isHead);
200
+ } else if (url === this.config.readyzPath) {
201
+ await this.handleReadiness(res, isHead);
202
+ } else if (url === this.config.startupzPath) {
203
+ this.handleStartup(res, isHead);
204
+ } else {
205
+ this.sendJson(res, 404, {
206
+ error: "Not Found"
207
+ }, isHead);
208
+ }
209
+ } catch (err) {
210
+ const message = err instanceof Error ? err.message : "Internal Server Error";
211
+ this.sendJson(res, 500, {
212
+ error: message
213
+ }, isHead);
214
+ }
215
+ }
216
+ /** `/healthz` — Liveness probe */
217
+ async handleLiveness(res, isHead = false) {
218
+ const results = await this.runChecks(this.config.checks);
219
+ if (this.config.onHealthCheck) {
220
+ const start = Date.now();
221
+ try {
222
+ const ok = await this.executeCheck(this.config.onHealthCheck, this.config.checkTimeoutMs);
223
+ results.push({
224
+ name: "custom",
225
+ ok: !!ok,
226
+ durationMs: Date.now() - start,
227
+ ...!ok ? {
228
+ error: "Health check returned false"
229
+ } : {}
230
+ });
231
+ } catch (err) {
232
+ const message = err instanceof Error ? err.message : "Unknown error";
233
+ results.push({
234
+ name: "custom",
235
+ ok: false,
236
+ durationMs: Date.now() - start,
237
+ error: message
238
+ });
239
+ }
240
+ }
241
+ const allOk = results.every((r) => r.ok);
242
+ const status = allOk ? "ok" : "unhealthy";
243
+ const httpStatus = allOk ? 200 : 503;
244
+ this.sendProbe(res, httpStatus, status, results, isHead);
245
+ }
246
+ /** `/readyz` — Readiness probe */
247
+ async handleReadiness(res, isHead = false) {
248
+ if (this.shuttingDown || !this.ready) {
249
+ const status2 = "unhealthy";
250
+ this.sendProbe(res, 503, status2, [
251
+ {
252
+ name: "readiness",
253
+ ok: false,
254
+ durationMs: 0,
255
+ error: this.shuttingDown ? "Shutting down" : "Not ready"
256
+ }
257
+ ], isHead);
258
+ return;
259
+ }
260
+ const checksToRun = this.config.readinessChecks ?? this.config.checks;
261
+ const results = await this.runChecks(checksToRun);
262
+ if (this.config.onReadinessCheck) {
263
+ const start = Date.now();
264
+ try {
265
+ const ready = await this.executeCheck(this.config.onReadinessCheck, this.config.checkTimeoutMs);
266
+ results.push({
267
+ name: "custom-readiness",
268
+ ok: !!ready,
269
+ durationMs: Date.now() - start,
270
+ ...!ready ? {
271
+ error: "Readiness check returned false"
272
+ } : {}
273
+ });
274
+ } catch (err) {
275
+ const message = err instanceof Error ? err.message : "Unknown error";
276
+ results.push({
277
+ name: "custom-readiness",
278
+ ok: false,
279
+ durationMs: Date.now() - start,
280
+ error: message
281
+ });
282
+ }
283
+ }
284
+ const allOk = results.every((r) => r.ok);
285
+ const status = allOk ? "ok" : "unhealthy";
286
+ const httpStatus = allOk ? 200 : 503;
287
+ this.sendProbe(res, httpStatus, status, results, isHead);
288
+ }
289
+ /** `/startupz` — Startup probe */
290
+ handleStartup(res, isHead = false) {
291
+ if (this.started) {
292
+ this.sendProbe(res, 200, "ok", [], isHead);
293
+ } else {
294
+ this.sendProbe(res, 503, "unhealthy", [
295
+ {
296
+ name: "startup",
297
+ ok: false,
298
+ durationMs: 0,
299
+ error: "Service has not started yet"
300
+ }
301
+ ], isHead);
302
+ }
303
+ }
304
+ async runChecks(checks) {
305
+ if (checks.length === 0) return [];
306
+ return Promise.all(checks.map(async ({ name, check }) => {
307
+ const start = Date.now();
308
+ try {
309
+ const result = await this.executeCheck(check, this.config.checkTimeoutMs);
310
+ return {
311
+ name,
312
+ ok: !!result,
313
+ durationMs: Date.now() - start
314
+ };
315
+ } catch (err) {
316
+ const message = err instanceof Error ? err.message : "Unknown error";
317
+ return {
318
+ name,
319
+ ok: false,
320
+ durationMs: Date.now() - start,
321
+ error: message
322
+ };
323
+ }
324
+ }));
325
+ }
326
+ sendProbe(res, httpStatus, status, checks, isHead = false) {
327
+ const body = {
328
+ status,
329
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
330
+ uptimeSeconds: Math.floor((Date.now() - this.startedAt) / 1e3)
331
+ };
332
+ if (this.config.detailed && checks.length > 0) {
333
+ body.checks = checks;
334
+ }
335
+ this.sendJson(res, httpStatus, body, isHead);
336
+ }
337
+ sendJson(res, status, body, isHead = false) {
338
+ const payload = JSON.stringify(body);
339
+ res.writeHead(status, {
340
+ "Content-Type": "application/json; charset=utf-8",
341
+ "Content-Length": Buffer.byteLength(payload),
342
+ "Cache-Control": "no-cache, no-store, must-revalidate",
343
+ "X-Content-Type-Options": "nosniff"
344
+ });
345
+ if (isHead) {
346
+ res.end();
347
+ } else {
348
+ res.end(payload);
349
+ }
350
+ }
351
+ async executeCheck(fn, ms) {
352
+ const controller = new AbortController();
353
+ let timer;
354
+ const timeoutPromise = new Promise((_, reject) => {
355
+ timer = setTimeout(() => {
356
+ const error = new Error(`Check timed out after ${ms}ms`);
357
+ controller.abort(error);
358
+ reject(error);
359
+ }, ms);
360
+ });
361
+ try {
362
+ const checkPromise = Promise.resolve().then(() => fn(controller.signal));
363
+ checkPromise.catch(() => {
364
+ });
365
+ return await Promise.race([
366
+ checkPromise,
367
+ timeoutPromise
368
+ ]);
369
+ } finally {
370
+ if (timer) clearTimeout(timer);
371
+ }
372
+ }
373
+ async initiateShutdown() {
374
+ if (this.shuttingDown) return;
375
+ this.shuttingDown = true;
376
+ this.ready = false;
377
+ if (this.config.shutdownDelayMs > 0) {
378
+ await new Promise((r) => setTimeout(r, this.config.shutdownDelayMs));
379
+ }
380
+ await _HealthzServer.stop();
381
+ }
382
+ cleanup() {
383
+ process.off("SIGTERM", this.onSigterm);
384
+ process.off("SIGINT", this.onSigint);
385
+ this.ready = false;
386
+ this.started = false;
387
+ this.shuttingDown = false;
388
+ }
389
+ };
390
+
391
+ exports.HealthzServer = HealthzServer;
392
+ exports.getDefaultHealthzConfig = getDefaultHealthzConfig;
393
+ exports.resolveConfig = resolveConfig;