@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.
- package/README.ko.md +115 -9
- package/README.md +115 -9
- package/dist/health-check.d.ts +13 -1
- package/dist/health-check.d.ts.map +1 -1
- package/dist/health-check.js +56 -22
- package/dist/indicators/disk.d.ts +3 -0
- package/dist/indicators/disk.d.ts.map +1 -1
- package/dist/indicators/disk.js +2 -0
- package/dist/indicators/drizzle.d.ts +5 -0
- package/dist/indicators/drizzle.d.ts.map +1 -1
- package/dist/indicators/drizzle.js +12 -4
- package/dist/indicators/http.d.ts +3 -0
- package/dist/indicators/http.d.ts.map +1 -1
- package/dist/indicators/http.js +41 -26
- package/dist/indicators/memory.d.ts +3 -0
- package/dist/indicators/memory.d.ts.map +1 -1
- package/dist/indicators/memory.js +2 -0
- package/dist/indicators/prisma.d.ts +45 -7
- package/dist/indicators/prisma.d.ts.map +1 -1
- package/dist/indicators/prisma.js +105 -15
- package/dist/indicators/redis.d.ts +9 -0
- package/dist/indicators/redis.d.ts.map +1 -1
- package/dist/indicators/redis.js +15 -3
- package/dist/indicators/utils.d.ts +17 -0
- package/dist/indicators/utils.d.ts.map +1 -1
- package/dist/indicators/utils.js +38 -2
- package/dist/module.d.ts +18 -1
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +26 -4
- package/dist/node-runtime.d.ts +5 -1
- package/dist/node-runtime.d.ts.map +1 -1
- package/dist/node-runtime.js +5 -1
- package/dist/types.d.ts +17 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -11
package/dist/health-check.js
CHANGED
|
@@ -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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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:
|
|
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
|
|
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
|
|
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;
|
|
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"}
|
package/dist/indicators/disk.js
CHANGED
|
@@ -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;
|
|
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.
|
|
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
|
-
|
|
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;
|
|
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"}
|
package/dist/indicators/http.js
CHANGED
|
@@ -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
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
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"}
|