@fluojs/terminus 1.0.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,4 @@
1
1
  import { HealthCheckError } from './errors.js';
2
- const runningIndicatorChecks = new WeakMap();
3
2
  function normalizeIndicatorTimeoutMs(value) {
4
3
  if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
5
4
  return undefined;
@@ -17,24 +16,40 @@ function createInFlightResult(key) {
17
16
  }
18
17
  };
19
18
  }
20
- function startSerializedIndicatorCheck(indicator, key) {
19
+ function startSerializedIndicatorCheck(indicator, key, runningIndicatorChecks) {
21
20
  const runningCheck = runningIndicatorChecks.get(indicator);
22
21
  if (runningCheck) {
23
22
  return undefined;
24
23
  }
25
24
  const check = Promise.resolve().then(() => indicator.check(key));
26
25
  runningIndicatorChecks.set(indicator, check);
27
- check.then(() => {
28
- if (runningIndicatorChecks.get(indicator) === check) {
29
- runningIndicatorChecks.delete(indicator);
26
+ const releaseIndicatorOwnership = () => {
27
+ const settlement = indicator.getPendingHealthCheckSettlement?.();
28
+ if (settlement) {
29
+ void settlement.then(() => {
30
+ if (runningIndicatorChecks.get(indicator) === check) {
31
+ runningIndicatorChecks.delete(indicator);
32
+ }
33
+ }, () => {
34
+ if (runningIndicatorChecks.get(indicator) === check) {
35
+ runningIndicatorChecks.delete(indicator);
36
+ }
37
+ });
38
+ return;
30
39
  }
31
- }, () => {
32
40
  if (runningIndicatorChecks.get(indicator) === check) {
33
41
  runningIndicatorChecks.delete(indicator);
34
42
  }
35
- });
43
+ };
44
+ check.then(releaseIndicatorOwnership, releaseIndicatorOwnership);
36
45
  return check;
37
46
  }
47
+ function normalizeHealthCheckErrorCauses(key, causes) {
48
+ return normalizeIndicatorResult(key, causes).map(([entryKey, state]) => [entryKey, {
49
+ ...state,
50
+ status: 'down'
51
+ }]);
52
+ }
38
53
  async function withTimeout(promise, timeoutMs) {
39
54
  let timer;
40
55
  try {
@@ -161,10 +176,10 @@ function createDuplicateKeyFailure(indicatorKey, duplicateKeys, seenKeys) {
161
176
  status: 'down'
162
177
  }];
163
178
  }
164
- async function runIndicator(indicator, index, executionOptions) {
179
+ async function runIndicator(indicator, index, executionOptions, runningIndicatorChecks) {
165
180
  const key = inferIndicatorKey(indicator, index);
166
181
  const indicatorTimeoutMs = normalizeIndicatorTimeoutMs(executionOptions.indicatorTimeoutMs);
167
- const runningCheck = startSerializedIndicatorCheck(indicator, key);
182
+ const runningCheck = startSerializedIndicatorCheck(indicator, key, runningIndicatorChecks);
168
183
  if (!runningCheck) {
169
184
  return {
170
185
  entries: Object.entries(createInFlightResult(key)),
@@ -180,7 +195,7 @@ async function runIndicator(indicator, index, executionOptions) {
180
195
  } catch (error) {
181
196
  if (error instanceof HealthCheckError) {
182
197
  return {
183
- entries: normalizeIndicatorResult(key, error.causes),
198
+ entries: normalizeHealthCheckErrorCauses(key, error.causes),
184
199
  indicatorKey: key
185
200
  };
186
201
  }
@@ -212,16 +227,8 @@ function aggregateIndicatorEntries(checks) {
212
227
  }
213
228
  return aggregatedEntries;
214
229
  }
215
-
216
- /**
217
- * Run every registered health indicator and aggregate their results.
218
- *
219
- * @param indicators Indicator instances to execute for the current health probe.
220
- * @param executionOptions Optional timeout guardrails for indicator execution.
221
- * @returns A structured report containing `info`, `error`, and full `details` maps.
222
- */
223
- export async function runHealthCheck(indicators, executionOptions = {}) {
224
- const checks = aggregateIndicatorEntries(await Promise.all(indicators.map((indicator, index) => runIndicator(indicator, index, executionOptions))));
230
+ async function executeHealthCheck(indicators, executionOptions = {}, runningIndicatorChecks) {
231
+ const checks = aggregateIndicatorEntries(await Promise.all(indicators.map((indicator, index) => runIndicator(indicator, index, executionOptions, runningIndicatorChecks))));
225
232
  const details = Object.fromEntries(checks);
226
233
  const infoEntries = checks.filter(([, result]) => result.status === 'up');
227
234
  const errorEntries = checks.filter(([, result]) => result.status === 'down');
@@ -238,13 +245,29 @@ export async function runHealthCheck(indicators, executionOptions = {}) {
238
245
  };
239
246
  }
240
247
 
248
+ /**
249
+ * Run every registered health indicator and aggregate their results.
250
+ *
251
+ * @remarks
252
+ * Direct helper calls receive an isolated execution scope. Use `TerminusHealthService`
253
+ * when repeated checks should serialize overlapping probes for a container-owned
254
+ * indicator set.
255
+ *
256
+ * @param indicators Indicator instances to execute for the current health probe.
257
+ * @param executionOptions Optional timeout guardrails for indicator execution.
258
+ * @returns A structured report containing `info`, `error`, and full `details` maps.
259
+ */
260
+ export async function runHealthCheck(indicators, executionOptions = {}) {
261
+ return executeHealthCheck(indicators, executionOptions, new WeakMap());
262
+ }
263
+
241
264
  /**
242
265
  * Assert that an aggregated health report is fully healthy.
243
266
  *
244
267
  * @param report Health report returned by `runHealthCheck(...)` or `TerminusHealthService.check()`.
245
268
  * @param message Error message used when one or more indicators are down.
246
269
  * @returns The same health report when every indicator is healthy.
247
- * @throws {HealthCheckError} When the report contains at least one down indicator.
270
+ * @throws {HealthCheckError} When one or more indicators are down.
248
271
  */
249
272
  export function assertHealthCheck(report, message = 'Health check failed.') {
250
273
  if (report.status === 'error') {
@@ -255,6 +278,7 @@ export function assertHealthCheck(report, message = 'Health check failed.') {
255
278
 
256
279
  /** Service facade that resolves and runs the health indicators registered in Terminus. */
257
280
  export class TerminusHealthService {
281
+ runningIndicatorChecks = new WeakMap();
258
282
  constructor(indicators, executionOptions = {}) {
259
283
  this.indicators = indicators;
260
284
  this.executionOptions = executionOptions;
@@ -266,7 +290,7 @@ export class TerminusHealthService {
266
290
  * @returns The aggregated health report for this check cycle.
267
291
  */
268
292
  async check() {
269
- return runHealthCheck(this.indicators, this.executionOptions);
293
+ return executeHealthCheck(this.indicators, this.executionOptions, this.runningIndicatorChecks);
270
294
  }
271
295
 
272
296
  /**
@@ -277,4 +301,14 @@ export class TerminusHealthService {
277
301
  async isHealthy() {
278
302
  return (await this.check()).status === 'ok';
279
303
  }
304
+
305
+ /**
306
+ * Return whether every indicator participating in readiness currently reports `up`.
307
+ *
308
+ * @returns `true` when all indicators whose `readiness` setting is not `false` report `up`.
309
+ */
310
+ async isReady() {
311
+ const readinessIndicators = this.indicators.filter(indicator => indicator.readiness !== false);
312
+ return (await executeHealthCheck(readinessIndicators, this.executionOptions, this.runningIndicatorChecks)).status === 'ok';
313
+ }
280
314
  }
@@ -6,6 +6,8 @@ export interface DiskHealthIndicatorOptions {
6
6
  minFreeBytes?: number;
7
7
  minFreeRatio?: number;
8
8
  path?: string;
9
+ /** Whether this indicator participates in `/ready`. Defaults to `true`. */
10
+ readiness?: boolean;
9
11
  }
10
12
  /**
11
13
  * Create a disk-space health indicator.
@@ -25,6 +27,7 @@ export declare function createDiskHealthIndicatorProvider(options?: DiskHealthIn
25
27
  export declare class DiskHealthIndicator implements HealthIndicator {
26
28
  private readonly options;
27
29
  readonly key: string | undefined;
30
+ readonly readiness: boolean | undefined;
28
31
  constructor(options?: DiskHealthIndicatorOptions);
29
32
  check(key: string): Promise<HealthIndicatorResult>;
30
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"disk.d.ts","sourceRoot":"","sources":["../../src/indicators/disk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE1E,sEAAsE;AACtE,MAAM,WAAW,0BAA0B;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAcD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,0BAA+B,GAAG,eAAe,CAEnG;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,GAAE,0BAA+B,GAAG,QAAQ,CAOpG;AAED,yEAAyE;AACzE,qBAAa,mBAAoB,YAAW,eAAe;IAG7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEJ,OAAO,GAAE,0BAA+B;IAI/D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAsCzD"}
1
+ {"version":3,"file":"disk.d.ts","sourceRoot":"","sources":["../../src/indicators/disk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE1E,sEAAsE;AACtE,MAAM,WAAW,0BAA0B;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAcD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,0BAA+B,GAAG,eAAe,CAEnG;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,GAAE,0BAA+B,GAAG,QAAQ,CAOpG;AAED,yEAAyE;AACzE,qBAAa,mBAAoB,YAAW,eAAe;IAI7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;gBAEX,OAAO,GAAE,0BAA+B;IAK/D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAsCzD"}
@@ -40,9 +40,11 @@ export function createDiskHealthIndicatorProvider(options = {}) {
40
40
  /** Health indicator that inspects free space for one filesystem path. */
41
41
  export class DiskHealthIndicator {
42
42
  key;
43
+ readiness;
43
44
  constructor(options = {}) {
44
45
  this.options = options;
45
46
  this.key = options.key;
47
+ this.readiness = options.readiness;
46
48
  }
47
49
  async check(key) {
48
50
  const indicatorKey = resolveIndicatorKey('disk', this.options.key ?? key);
@@ -25,6 +25,8 @@ export interface DrizzleHealthIndicatorOptions {
25
25
  key?: string;
26
26
  ping?: () => Promise<unknown> | unknown;
27
27
  query?: unknown;
28
+ /** Whether this indicator participates in `/ready`. Defaults to `true`. */
29
+ readiness?: boolean;
28
30
  timeoutMs?: number;
29
31
  }
30
32
  /**
@@ -45,8 +47,11 @@ export declare function createDrizzleHealthIndicatorProvider(options?: Omit<Driz
45
47
  export declare class DrizzleHealthIndicator implements HealthIndicator {
46
48
  private readonly options;
47
49
  readonly key: string | undefined;
50
+ readonly readiness: boolean | undefined;
51
+ private pendingProbeSettlement;
48
52
  constructor(options?: DrizzleHealthIndicatorOptions);
49
53
  check(key: string): Promise<HealthIndicatorResult>;
54
+ getPendingHealthCheckSettlement(): Promise<void> | undefined;
50
55
  }
51
56
  export {};
52
57
  //# sourceMappingURL=drizzle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../../src/indicators/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGrD,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAK1E,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD;AAED,UAAU,4BAA4B;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;KAC9C,CAAC;IACF,SAAS,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC;KAC/B,CAAC;CACH;AAED,UAAU,yBAAyB;IACjC,4BAA4B,CAAC,EAAE,MAAM,4BAA4B,CAAC;IAClE,OAAO,CAAC,EAAE,MAAM,kBAAkB,GAAG,OAAO,CAAC;CAC9C;AAED,gEAAgE;AAChE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,cAAc,CAAC,EAAE,yBAAyB,CAAC;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA8DD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,6BAAkC,GAAG,eAAe,CAEzG;AAED;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,OAAO,GAAE,IAAI,CAAC,6BAA6B,EAAE,UAAU,GAAG,gBAAgB,CAAM,GAAG,QAAQ,CAkB/I;AAED,iHAAiH;AACjH,qBAAa,sBAAuB,YAAW,eAAe;IAGhD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEJ,OAAO,GAAE,6BAAkC;IAIlE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAiCzD"}
1
+ {"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../../src/indicators/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAM1E,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD;AAED,UAAU,4BAA4B;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;KAC9C,CAAC;IACF,SAAS,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC;KAC/B,CAAC;CACH;AAED,UAAU,yBAAyB;IACjC,4BAA4B,CAAC,EAAE,MAAM,4BAA4B,CAAC;IAClE,OAAO,CAAC,EAAE,MAAM,kBAAkB,GAAG,OAAO,CAAC;CAC9C;AAED,gEAAgE;AAChE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,cAAc,CAAC,EAAE,yBAAyB,CAAC;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA8DD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,GAAE,6BAAkC,GAAG,eAAe,CAEzG;AAED;;;;;GAKG;AACH,wBAAgB,oCAAoC,CAAC,OAAO,GAAE,IAAI,CAAC,6BAA6B,EAAE,UAAU,GAAG,gBAAgB,CAAM,GAAG,QAAQ,CAkB/I;AAED,iHAAiH;AACjH,qBAAa,sBAAuB,YAAW,eAAe;IAKhD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,OAAO,CAAC,sBAAsB,CAA4B;gBAE7B,OAAO,GAAE,6BAAkC;IAKlE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAoCxD,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS;CAG7D"}
@@ -1,5 +1,5 @@
1
1
  import { optional } from '@fluojs/di';
2
- import { createDownResult, createUpResult, resolveIndicatorKey, throwHealthCheckError, withIndicatorTimeout } from './utils.js';
2
+ import { createDownResult, createUpResult, resolveIndicatorKey, resolveIndicatorTimeoutMs, throwHealthCheckError, waitForIndicatorProbeSettlement, withIndicatorTimeout } from './utils.js';
3
3
  const DRIZZLE_DATABASE = Symbol.for('fluo.drizzle.database');
4
4
  const DRIZZLE_HANDLE_PROVIDER = Symbol.for('fluo.drizzle.handle-provider');
5
5
 
@@ -12,7 +12,7 @@ async function runDrizzlePing(options) {
12
12
  await options.ping();
13
13
  return;
14
14
  }
15
- const database = options.database ?? resolveCurrentDatabase(options.handleProvider);
15
+ const database = resolveCurrentDatabase(options.handleProvider) ?? options.database;
16
16
  if (!database || typeof database.execute !== 'function') {
17
17
  throw new Error('Drizzle indicator requires an execute-capable database handle or a ping callback.');
18
18
  }
@@ -79,20 +79,25 @@ export function createDrizzleHealthIndicatorProvider(options = {}) {
79
79
  /** Health indicator that maps Drizzle lifecycle state and probes connectivity with an execute-capable handle. */
80
80
  export class DrizzleHealthIndicator {
81
81
  key;
82
+ readiness;
83
+ pendingProbeSettlement;
82
84
  constructor(options = {}) {
83
85
  this.options = options;
84
86
  this.key = options.key;
87
+ this.readiness = options.readiness;
85
88
  }
86
89
  async check(key) {
87
90
  const indicatorKey = resolveIndicatorKey('drizzle', this.options.key ?? key);
88
- const timeoutMs = this.options.timeoutMs ?? DEFAULT_DRIZZLE_TIMEOUT_MS;
89
91
  try {
92
+ const timeoutMs = resolveIndicatorTimeoutMs(this.options.timeoutMs, DEFAULT_DRIZZLE_TIMEOUT_MS, indicatorKey);
90
93
  const snapshot = createDrizzleLifecycleSnapshot(this.options.handleProvider);
91
94
  const lifecycleDownResult = snapshot ? createDrizzleLifecycleDownResult(indicatorKey, snapshot) : undefined;
92
95
  if (lifecycleDownResult) {
93
96
  throwHealthCheckError('Drizzle health check failed.', lifecycleDownResult);
94
97
  }
95
- await withIndicatorTimeout(runDrizzlePing(this.options), timeoutMs, indicatorKey);
98
+ const probe = runDrizzlePing(this.options);
99
+ this.pendingProbeSettlement = waitForIndicatorProbeSettlement(probe);
100
+ await withIndicatorTimeout(probe, timeoutMs, indicatorKey);
96
101
  return createUpResult(indicatorKey, snapshot ? {
97
102
  details: snapshot.details,
98
103
  healthStatus: snapshot.health.status,
@@ -105,4 +110,7 @@ export class DrizzleHealthIndicator {
105
110
  throwHealthCheckError('Drizzle health check failed.', createDownResult(indicatorKey, error instanceof Error ? error.message : 'Drizzle health check failed.'));
106
111
  }
107
112
  }
113
+ getPendingHealthCheckSettlement() {
114
+ return this.pendingProbeSettlement;
115
+ }
108
116
  }
@@ -6,6 +6,8 @@ export interface HttpHealthIndicatorOptions {
6
6
  headers?: Record<string, string>;
7
7
  key?: string;
8
8
  method?: string;
9
+ /** Whether this indicator participates in `/ready`. Defaults to `true`. */
10
+ readiness?: boolean;
9
11
  timeoutMs?: number;
10
12
  url: string;
11
13
  }
@@ -27,6 +29,7 @@ export declare function createHttpHealthIndicatorProvider(options: HttpHealthInd
27
29
  export declare class HttpHealthIndicator implements HealthIndicator {
28
30
  private readonly options;
29
31
  readonly key: string | undefined;
32
+ readonly readiness: boolean | undefined;
30
33
  constructor(options: HttpHealthIndicatorOptions);
31
34
  check(key: string): Promise<HealthIndicatorResult>;
32
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/indicators/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE1E,wDAAwD;AACxD,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AAoBD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,eAAe,CAE9F;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,0BAA0B,GAAG,QAAQ,CAO/F;AAED,6EAA6E;AAC7E,qBAAa,mBAAoB,YAAW,eAAe;IAG7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEJ,OAAO,EAAE,0BAA0B;IAI1D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CA6CzD"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/indicators/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE1E,wDAAwD;AACxD,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb;AA8BD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,eAAe,CAE9F;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,0BAA0B,GAAG,QAAQ,CAO/F;AAED,6EAA6E;AAC7E,qBAAa,mBAAoB,YAAW,eAAe;IAI7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;gBAEX,OAAO,EAAE,0BAA0B;IAK1D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAmDzD"}
@@ -1,4 +1,4 @@
1
- import { createDownResult, createUpResult, resolveIndicatorKey, throwHealthCheckError } from './utils.js';
1
+ import { createDownResult, createUpResult, resolveIndicatorKey, resolveIndicatorTimeoutMs, throwHealthCheckError } from './utils.js';
2
2
  import { HealthCheckError } from '../errors.js';
3
3
 
4
4
  /** Options for probing one upstream HTTP dependency. */
@@ -16,6 +16,13 @@ function isExpectedStatus(status, expected) {
16
16
  }
17
17
  return status >= 200 && status < 300;
18
18
  }
19
+ function cancelResponseBody(response) {
20
+ const body = response.body;
21
+ if (!body) {
22
+ return;
23
+ }
24
+ void body.cancel().catch(() => undefined);
25
+ }
19
26
 
20
27
  /**
21
28
  * Create an HTTP-backed health indicator.
@@ -44,45 +51,53 @@ export function createHttpHealthIndicatorProvider(options) {
44
51
  /** Health indicator that probes an upstream HTTP endpoint with `fetch()`. */
45
52
  export class HttpHealthIndicator {
46
53
  key;
54
+ readiness;
47
55
  constructor(options) {
48
56
  this.options = options;
49
57
  this.key = options.key;
58
+ this.readiness = options.readiness;
50
59
  }
51
60
  async check(key) {
52
61
  const indicatorKey = resolveIndicatorKey('http', this.options.key ?? key);
53
- const timeoutMs = this.options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
54
62
  const method = this.options.method ?? 'GET';
55
- const abortController = new AbortController();
56
- const startedAt = Date.now();
57
- const timeout = setTimeout(() => {
58
- abortController.abort(new Error(`HTTP health check timed out after ${String(timeoutMs)}ms.`));
59
- }, timeoutMs);
60
63
  try {
61
- const response = await fetch(this.options.url, {
62
- headers: this.options.headers,
63
- method,
64
- signal: abortController.signal
65
- });
66
- const responseTimeMs = Date.now() - startedAt;
67
- if (!isExpectedStatus(response.status, this.options.expectedStatus)) {
68
- throwHealthCheckError('HTTP health check failed.', createDownResult(indicatorKey, `Unexpected status code ${String(response.status)} from ${this.options.url}.`, {
69
- responseTimeMs,
70
- statusCode: response.status,
71
- url: this.options.url
72
- }));
64
+ const timeoutMs = resolveIndicatorTimeoutMs(this.options.timeoutMs, DEFAULT_HTTP_TIMEOUT_MS, indicatorKey);
65
+ const abortController = new AbortController();
66
+ const startedAt = Date.now();
67
+ const timeout = setTimeout(() => {
68
+ abortController.abort(new Error(`HTTP health check timed out after ${String(timeoutMs)}ms.`));
69
+ }, timeoutMs);
70
+ try {
71
+ const response = await fetch(this.options.url, {
72
+ headers: this.options.headers,
73
+ method,
74
+ signal: abortController.signal
75
+ });
76
+ const responseTimeMs = Date.now() - startedAt;
77
+ try {
78
+ if (!isExpectedStatus(response.status, this.options.expectedStatus)) {
79
+ throwHealthCheckError('HTTP health check failed.', createDownResult(indicatorKey, `Unexpected status code ${String(response.status)} from ${this.options.url}.`, {
80
+ responseTimeMs,
81
+ statusCode: response.status,
82
+ url: this.options.url
83
+ }));
84
+ }
85
+ return createUpResult(indicatorKey, {
86
+ responseTimeMs,
87
+ statusCode: response.status,
88
+ url: this.options.url
89
+ });
90
+ } finally {
91
+ cancelResponseBody(response);
92
+ }
93
+ } finally {
94
+ clearTimeout(timeout);
73
95
  }
74
- return createUpResult(indicatorKey, {
75
- responseTimeMs,
76
- statusCode: response.status,
77
- url: this.options.url
78
- });
79
96
  } catch (error) {
80
97
  if (error instanceof HealthCheckError) {
81
98
  throw error;
82
99
  }
83
100
  throwHealthCheckError('HTTP health check failed.', createDownResult(indicatorKey, error instanceof Error ? error.message : `HTTP health check failed for ${this.options.url}.`));
84
- } finally {
85
- clearTimeout(timeout);
86
101
  }
87
102
  }
88
103
  }
@@ -16,6 +16,8 @@ export interface MemoryHealthIndicatorOptions {
16
16
  heapUsedThresholdRatio?: number;
17
17
  key?: string;
18
18
  memoryUsage?: MemoryUsageSampler;
19
+ /** Whether this indicator participates in `/ready`. Defaults to `true`. */
20
+ readiness?: boolean;
19
21
  rssThresholdBytes?: number;
20
22
  }
21
23
  /**
@@ -36,6 +38,7 @@ export declare function createMemoryHealthIndicatorProvider(options?: MemoryHeal
36
38
  export declare class MemoryHealthIndicator implements HealthIndicator {
37
39
  private readonly options;
38
40
  readonly key: string | undefined;
41
+ readonly readiness: boolean | undefined;
39
42
  constructor(options?: MemoryHealthIndicatorOptions);
40
43
  check(key: string): Promise<HealthIndicatorResult>;
41
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/indicators/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE1E,mDAAmD;AACnD,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,8EAA8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG,MAAM,mBAAmB,CAAC;AAE3D,4DAA4D;AAC5D,MAAM,WAAW,4BAA4B;IAC3C,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAYD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,4BAAiC,GAAG,eAAe,CAEvG;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,OAAO,GAAE,4BAAiC,GAAG,QAAQ,CAOxG;AAED,qEAAqE;AACrE,qBAAa,qBAAsB,YAAW,eAAe;IAG/C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEJ,OAAO,GAAE,4BAAiC;IAIjE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAiCzD"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/indicators/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE1E,mDAAmD;AACnD,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,8EAA8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG,MAAM,mBAAmB,CAAC;AAE3D,4DAA4D;AAC5D,MAAM,WAAW,4BAA4B;IAC3C,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAYD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,4BAAiC,GAAG,eAAe,CAEvG;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,OAAO,GAAE,4BAAiC,GAAG,QAAQ,CAOxG;AAED,qEAAqE;AACrE,qBAAa,qBAAsB,YAAW,eAAe;IAI/C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;gBAEX,OAAO,GAAE,4BAAiC;IAKjE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAiCzD"}
@@ -42,9 +42,11 @@ export function createMemoryHealthIndicatorProvider(options = {}) {
42
42
  /** Health indicator that checks local process heap and RSS usage. */
43
43
  export class MemoryHealthIndicator {
44
44
  key;
45
+ readiness;
45
46
  constructor(options = {}) {
46
47
  this.options = options;
47
48
  this.key = options.key;
49
+ this.readiness = options.readiness;
48
50
  }
49
51
  async check(key) {
50
52
  const indicatorKey = resolveIndicatorKey('memory', this.options.key ?? key);
@@ -1,4 +1,5 @@
1
- import type { Provider } from '@fluojs/di';
1
+ import type { Token } from '@fluojs/core';
2
+ import { type Provider } from '@fluojs/di';
2
3
  import type { HealthIndicator, HealthIndicatorResult } from '../types.js';
3
4
  interface PrismaClientLike {
4
5
  $executeRaw?: (...args: unknown[]) => Promise<unknown>;
@@ -6,33 +7,70 @@ interface PrismaClientLike {
6
7
  $queryRaw?: (...args: unknown[]) => Promise<unknown>;
7
8
  $queryRawUnsafe?: (query: string) => Promise<unknown>;
8
9
  }
10
+ interface PrismaLifecycleSnapshotLike {
11
+ details?: Record<string, unknown>;
12
+ health: {
13
+ reason?: string;
14
+ status: 'healthy' | 'degraded' | 'unhealthy';
15
+ };
16
+ readiness: {
17
+ reason?: string;
18
+ status: 'ready' | 'not-ready';
19
+ };
20
+ }
21
+ interface PrismaServiceLike {
22
+ createPlatformStatusSnapshot?: () => PrismaLifecycleSnapshotLike;
23
+ current?: () => PrismaClientLike | unknown;
24
+ }
9
25
  /** Options for probing Prisma-backed database connectivity. */
10
26
  export interface PrismaHealthIndicatorOptions {
27
+ /** Raw Prisma client to probe when no lifecycle-aware service facade is supplied. */
11
28
  client?: PrismaClientLike;
29
+ /** Explicit raw-client token to resolve when using `createPrismaHealthIndicatorProvider(...)`. */
30
+ clientToken?: Token;
31
+ /** Indicator result key override. Defaults to the key passed to `check(...)`, then `prisma`. */
12
32
  key?: string;
33
+ /** Named Prisma registration to resolve when using `createPrismaHealthIndicatorProvider(...)`. */
34
+ name?: string;
35
+ /** Custom ping callback for manual probes or tests. Lifecycle state is only mapped when `service` is available. */
13
36
  ping?: () => Promise<unknown> | unknown;
37
+ /** Whether this indicator participates in `/ready`. Defaults to `true`. */
38
+ readiness?: boolean;
39
+ /** Lifecycle-aware Prisma service/facade handle, usually resolved from `getPrismaServiceToken(name)`. */
40
+ service?: PrismaServiceLike;
41
+ /** Explicit Prisma service token to resolve when using `createPrismaHealthIndicatorProvider(...)`. */
42
+ serviceToken?: Token;
43
+ /** Maximum time to wait for the ping operation. Defaults to `2_000` ms. */
14
44
  timeoutMs?: number;
15
45
  }
16
46
  /**
17
47
  * Create a Prisma health indicator.
18
48
  *
19
- * @param options Optional Prisma client, ping callback, timeout, and key override.
20
- * @returns A health indicator that executes a lightweight Prisma round trip.
49
+ * @param options Optional lifecycle-aware service facade, Prisma client, ping callback, timeout, and key override.
50
+ * @returns A health indicator that checks Prisma lifecycle state before executing a lightweight round trip.
21
51
  */
22
52
  export declare function createPrismaHealthIndicator(options?: PrismaHealthIndicatorOptions): HealthIndicator;
23
53
  /**
24
- * Create a Terminus indicator provider collection entry that resolves a Prisma client from DI.
54
+ * Create a Terminus indicator provider collection entry that resolves Prisma from DI.
55
+ *
56
+ * The provider prefers `getPrismaServiceToken(options.name)` so `@fluojs/prisma`
57
+ * lifecycle snapshots participate in health/readiness diagnostics. It falls back
58
+ * to the matching raw client token for compatibility with manual provider graphs.
59
+ * Explicit `serviceToken` and `clientToken` values override the name-derived tokens.
25
60
  *
26
- * @param options Optional timeout, key override, or custom ping callback.
61
+ * @param options Optional name hint, explicit tokens, timeout, key override, or custom ping callback.
27
62
  * @returns A factory provider with a unique internal DI token for `TerminusModule` indicatorProviders.
28
63
  */
29
- export declare function createPrismaHealthIndicatorProvider(options?: Omit<PrismaHealthIndicatorOptions, 'client'>): Provider;
30
- /** Health indicator that probes Prisma connectivity with a trivial query. */
64
+ export declare function createPrismaHealthIndicatorProvider(options?: Omit<PrismaHealthIndicatorOptions, 'client' | 'service'>): Provider;
65
+ /** Health indicator that maps Prisma lifecycle status and probes connectivity with a trivial query. */
31
66
  export declare class PrismaHealthIndicator implements HealthIndicator {
32
67
  private readonly options;
33
68
  readonly key: string | undefined;
69
+ readonly readiness: boolean | undefined;
70
+ private pendingProbeSettlement;
34
71
  constructor(options?: PrismaHealthIndicatorOptions);
35
72
  check(key: string): Promise<HealthIndicatorResult>;
73
+ getPendingHealthCheckSettlement(): Promise<void> | undefined;
36
74
  }
37
75
  export {};
38
76
  //# sourceMappingURL=prisma.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/indicators/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAI1E,UAAU,gBAAgB;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvD;AAED,+DAA+D;AAC/D,MAAM,WAAW,4BAA4B;IAC3C,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAuCD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,4BAAiC,GAAG,eAAe,CAEvG;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,OAAO,GAAE,IAAI,CAAC,4BAA4B,EAAE,QAAQ,CAAM,GAAG,QAAQ,CAQxH;AAED,6EAA6E;AAC7E,qBAAa,qBAAsB,YAAW,eAAe;IAG/C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEJ,OAAO,GAAE,4BAAiC;IAIjE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAczD"}
1
+ {"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/indicators/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAM1E,UAAU,gBAAgB;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvD;AAED,UAAU,2BAA2B;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE;QACN,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;KAC9C,CAAC;IACF,SAAS,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,OAAO,GAAG,WAAW,CAAC;KAC/B,CAAC;CACH;AAED,UAAU,iBAAiB;IACzB,4BAA4B,CAAC,EAAE,MAAM,2BAA2B,CAAC;IACjE,OAAO,CAAC,EAAE,MAAM,gBAAgB,GAAG,OAAO,CAAC;CAC5C;AAED,+DAA+D;AAC/D,MAAM,WAAW,4BAA4B;IAC3C,qFAAqF;IACrF,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,kGAAkG;IAClG,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,gGAAgG;IAChG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kGAAkG;IAClG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mHAAmH;IACnH,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACxC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yGAAyG;IACzG,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,sGAAsG;IACtG,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+HD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,4BAAiC,GAAG,eAAe,CAEvG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mCAAmC,CACjD,OAAO,GAAE,IAAI,CAAC,4BAA4B,EAAE,QAAQ,GAAG,SAAS,CAAM,GACrE,QAAQ,CA8BV;AAED,uGAAuG;AACvG,qBAAa,qBAAsB,YAAW,eAAe;IAK/C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,OAAO,CAAC,sBAAsB,CAA4B;gBAE7B,OAAO,GAAE,4BAAiC;IAKjE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA8BxD,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS;CAG7D"}