@fluojs/terminus 1.1.0 → 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 +106 -5
- package/README.md +106 -5
- package/dist/health-check.d.ts +7 -1
- package/dist/health-check.d.ts.map +1 -1
- package/dist/health-check.js +33 -7
- 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 +11 -3
- package/dist/indicators/http.d.ts +3 -0
- package/dist/indicators/http.d.ts.map +1 -1
- package/dist/indicators/http.js +2 -0
- 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 +5 -0
- package/dist/indicators/prisma.d.ts.map +1 -1
- package/dist/indicators/prisma.js +10 -2
- package/dist/indicators/redis.d.ts +9 -0
- package/dist/indicators/redis.d.ts.map +1 -1
- package/dist/indicators/redis.js +14 -2
- package/dist/indicators/utils.d.ts +7 -0
- package/dist/indicators/utils.d.ts.map +1 -1
- package/dist/indicators/utils.js +10 -0
- package/dist/module.d.ts +18 -1
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +25 -3
- 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/README.ko.md
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# @fluojs/terminus
|
|
2
|
+
<!-- fluo-terminus-contract: registration=application-owned-TerminusModule.forRoot;health=aggregated-diagnostics;ready-admission=binary;ready-body=ready|starting|unavailable;default-liveness=absent;unhealthy-status=503;route-protection=path-scoped-external-boundary;indicator-readiness=opt-out;readiness-checks=additive -->
|
|
2
3
|
|
|
3
4
|
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
5
|
|
|
5
6
|
fluo 애플리케이션을 위한 헬스 인디케이터(Health Indicator) 툴킷입니다. `@fluojs/terminus`는 런타임의 기본 health/readiness 엔드포인트 위에 의존성 인식 상태 보고 기능을 추가합니다.
|
|
6
7
|
|
|
8
|
+
Node listener helper는 이제 `@fluojs/runtime/node`가 아니라 `@fluojs/platform-nodejs`에 속합니다. 이 package boundary 변경은 Terminus 등록이나 readiness를 바꾸지 않습니다. `TerminusModule.forRoot(...)`는 계속 애플리케이션이 소유하고, `/health`는 진단을 집계하며, `/ready`는 readiness가 성공할 때만 트래픽을 수용합니다.
|
|
9
|
+
|
|
7
10
|
## 목차
|
|
8
11
|
|
|
9
12
|
- [설치](#설치)
|
|
@@ -12,6 +15,7 @@ fluo 애플리케이션을 위한 헬스 인디케이터(Health Indicator) 툴
|
|
|
12
15
|
- [공통 패턴](#공통-패턴)
|
|
13
16
|
- [내장 인디케이터](#내장-인디케이터)
|
|
14
17
|
- [DI 기반 인디케이터](#di-기반-인디케이터)
|
|
18
|
+
- [엔드포인트 미들웨어](#엔드포인트-미들웨어)
|
|
15
19
|
- [실행 가드레일](#실행-가드레일)
|
|
16
20
|
- [실패 시맨틱](#실패-시맨틱)
|
|
17
21
|
- [NestJS 마이그레이션 경계](#nestjs-마이그레이션-경계)
|
|
@@ -73,6 +77,8 @@ class AppModule {}
|
|
|
73
77
|
- `MemoryHealthIndicator` (호환성을 위해 root에서도 export되며 `@fluojs/terminus/node`에서도 제공)
|
|
74
78
|
- `DiskHealthIndicator` (호환성을 위해 root에서도 export되며 `@fluojs/terminus/node`에서도 제공)
|
|
75
79
|
|
|
80
|
+
`@nestjs/terminus`에서 마이그레이션할 때는 소유권 경계를 명시적으로 유지하세요. Custom fluo `HealthIndicator` instance는 `indicators`에 등록하고, indicator가 DI를 통해 dependency를 resolve해야 할 때만 해당 `create*HealthIndicatorProvider()`를 `indicatorProviders`에 사용합니다. Node memory/disk helper는 `@fluojs/terminus/node`에서, Redis helper는 `@fluojs/terminus/redis`에서 import하세요. Prisma, Drizzle, HTTP indicator는 root export입니다. 전용 Redis subpath는 optional peer를 root import 경계 밖에 유지하며, Node helper가 root에서도 export되는 것은 호환성을 위한 것입니다.
|
|
81
|
+
|
|
76
82
|
### DI 기반 인디케이터
|
|
77
83
|
|
|
78
84
|
Redis나 DB 클라이언트와 같이 DI 컨테이너의 의존성이 필요한 인디케이터를 사용할 때는, 모듈 로드 시점에 피어 의존성을 import하지 않도록 문서화된 provider 팩토리를 사용하세요. 이 helper들은 `TerminusModule.forRoot({ indicatorProviders })`에 전달할 indicator provider entry를 만들며 module facade를 대체하지 않습니다.
|
|
@@ -107,6 +113,94 @@ Prisma의 경우 `createPrismaHealthIndicatorProvider()`는 `@fluojs/prisma`가
|
|
|
107
113
|
|
|
108
114
|
Provider factory는 반복 등록할 수 있습니다. 각 인스턴스가 서로 다른 indicator key나 dependency option을 사용한다면 같은 factory가 만든 provider 여러 개를 하나의 `indicatorProviders` 배열에 등록할 수 있으며, Terminus는 나중에 등록된 같은 타입 provider가 앞선 provider를 덮어쓰지 않도록 각 provider 인스턴스를 별도 DI token으로 보관합니다.
|
|
109
115
|
|
|
116
|
+
### 의존성 모듈과 DI 기반 인디케이터 조합하기
|
|
117
|
+
|
|
118
|
+
Terminus는 `indicatorProviders`를 자신의 module scope 안에서 해석합니다. `PrismaModule`, `DrizzleModule`, 또는 named `RedisModule` registration을 바깥 application module에 import하더라도 그 export token이 Terminus에 보이지는 **않습니다**. 일반 module export는 그것을 import한 module에만 보이기 때문입니다. 인디케이터가 실제로 해석되는 scope에서 의존성을 볼 수 있도록, 의존성을 소유한 module을 `imports`로 전달하세요.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { DrizzleModule } from '@fluojs/drizzle';
|
|
122
|
+
import { PrismaModule } from '@fluojs/prisma';
|
|
123
|
+
import { TerminusModule } from '@fluojs/terminus';
|
|
124
|
+
import { createDrizzleHealthIndicatorProvider, createPrismaHealthIndicatorProvider } from '@fluojs/terminus';
|
|
125
|
+
|
|
126
|
+
const prismaModule = PrismaModule.forRoot({ client });
|
|
127
|
+
const drizzleModule = DrizzleModule.forRoot({ database, dispose });
|
|
128
|
+
|
|
129
|
+
@Module({
|
|
130
|
+
imports: [
|
|
131
|
+
prismaModule,
|
|
132
|
+
drizzleModule,
|
|
133
|
+
TerminusModule.forRoot({
|
|
134
|
+
imports: [prismaModule, drizzleModule],
|
|
135
|
+
indicatorProviders: [
|
|
136
|
+
createPrismaHealthIndicatorProvider({ key: 'prisma' }),
|
|
137
|
+
createDrizzleHealthIndicatorProvider({ key: 'drizzle' }),
|
|
138
|
+
],
|
|
139
|
+
}),
|
|
140
|
+
],
|
|
141
|
+
})
|
|
142
|
+
class AppModule {}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
항상 scoped로 등록되어 global module로는 절대 도달할 수 없는 named Redis registration에도 같은 규칙이 적용됩니다.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const cacheRedisModule = RedisModule.forRoot({ host: '127.0.0.1', name: 'cache', port: 6379 });
|
|
149
|
+
|
|
150
|
+
TerminusModule.forRoot({
|
|
151
|
+
imports: [cacheRedisModule],
|
|
152
|
+
indicatorProviders: [
|
|
153
|
+
createRedisHealthIndicatorProvider({ clientName: 'cache', key: 'cache-redis' }),
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
named Redis indicator 의존성은 필수입니다. import된 module이나 global module 어디에서도 해당 token을 공급하지 않으면 bootstrap 중 module graph 검증이 누락된 token을 명시하는 `MODULE_VISIBILITY_ERROR`로 실패합니다. Prisma와 Drizzle indicator 의존성은 해당 token이 bootstrap graph 전체에 없을 때만 선택 사항입니다. 이 경우 owner module을 생략해도 애플리케이션은 bootstrap되지만 해당 indicator는 요청 시점의 `/health`에서 `down`을 보고합니다. Prisma 또는 Drizzle owner module이 애플리케이션의 다른 위치에 존재하지만 Terminus `imports`에서 생략된 경우에는 bootstrap이 `MODULE_VISIBILITY_ERROR`로 실패합니다. optional injection은 module visibility를 우회하지 않습니다. global module이 공급하는 의존성 — 예를 들어 `name` 없이 등록해 기본적으로 global인 `RedisModule.forRoot(...)` — 은 별도의 `imports` 항목 없이도 계속 보입니다.
|
|
159
|
+
|
|
160
|
+
### 엔드포인트 미들웨어
|
|
161
|
+
|
|
162
|
+
class 기반 `endpointMiddleware`로 Terminus가 생성한 엔드포인트에만 접근 정책을 적용하세요. 클래스는 DI를 통해 해석되고 선언 순서대로 `/health`와 `/ready` 모두에서 실행됩니다. 이 옵션을 생략하면 기존의 제한 없는 기본 동작이 유지됩니다. Custom `path` 값은 미들웨어 route match 전에 정규화됩니다.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { ForbiddenException, type MiddlewareContext, type Next } from '@fluojs/http';
|
|
166
|
+
|
|
167
|
+
class HealthProbeAuthMiddleware {
|
|
168
|
+
async handle(context: MiddlewareContext, next: Next): Promise<void> {
|
|
169
|
+
if (context.request.headers['x-health-token'] !== 'secret-token') {
|
|
170
|
+
throw new ForbiddenException('Health endpoints require x-health-token.');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
await next();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
TerminusModule.forRoot({
|
|
178
|
+
endpointMiddleware: [HealthProbeAuthMiddleware],
|
|
179
|
+
path: '/internal/',
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
위 미들웨어는 정규화된 `/internal/health` 및 `/internal/ready` route에서 실행되며, 관련 없는 애플리케이션 route에는 적용되지 않습니다.
|
|
184
|
+
|
|
185
|
+
### Readiness 참여
|
|
186
|
+
|
|
187
|
+
기본적으로 모든 indicator는 `/health`와 `/ready` 양쪽에 참여합니다. 의존성 상태를 `/health`에는 계속 노출하되 그 외에는 준비된 인스턴스를 rotation에서 제외하지 않아야 한다면 `readiness: false`를 설정하세요.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
TerminusModule.forRoot({
|
|
191
|
+
indicators: [
|
|
192
|
+
new HttpHealthIndicator({
|
|
193
|
+
key: 'search',
|
|
194
|
+
readiness: false,
|
|
195
|
+
url: 'https://search.example.com/health',
|
|
196
|
+
}),
|
|
197
|
+
new MemoryHealthIndicator({ key: 'memory', heapUsedThresholdRatio: 0.9 }),
|
|
198
|
+
],
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`search`가 `down`을 보고하면 `/health`는 해당 diagnostic과 함께 `503`을 반환하지만, `/ready`는 계속 `memory`, custom `readinessChecks`, platform readiness를 평가합니다. `readiness`를 생략하거나 `true`로 둔 indicator가 실패하면 이전과 같이 두 endpoint 모두 unavailable 상태가 됩니다.
|
|
203
|
+
|
|
110
204
|
### 실행 가드레일
|
|
111
205
|
|
|
112
206
|
커스텀 인디케이터가 멈추거나 느린 하위 서비스에 의존할 수 있다면 `execution.indicatorTimeoutMs`를 사용하세요. probe가 설정된 시간을 넘기면 Terminus는 무기한 대기하지 않고 해당 인디케이터를 `down`으로 표시합니다.
|
|
@@ -124,14 +218,14 @@ TerminusModule.forRoot({
|
|
|
124
218
|
});
|
|
125
219
|
```
|
|
126
220
|
|
|
127
|
-
`path`로 health endpoint를 custom path 아래에 mount할 수
|
|
221
|
+
`path`로 health endpoint를 custom path 아래에 mount할 수 있습니다. Indicator는 기본적으로 `/ready`를 차단하며, `/health` 진단은 유지하면서 트래픽을 차단하지 않아야 하면 해당 indicator에 `readiness: false`를 설정하세요. `readinessChecks`는 애플리케이션별 추가 조건을 indicator 및 platform readiness check와 합성하며 indicator를 제외하지 않습니다.
|
|
128
222
|
|
|
129
223
|
### 실패 시맨틱
|
|
130
224
|
|
|
131
225
|
인디케이터가 `down` 결과를 반환하거나 `HealthCheckError`를 던지면, `TerminusHealthService`는 이 실패들을 모아 보고서를 작성합니다.
|
|
132
226
|
|
|
133
|
-
-
|
|
134
|
-
-
|
|
227
|
+
- `/health`는 집계된 `HealthCheckReport` 진단을 반환하며 그 report가 healthy일 때만 HTTP `200`을 반환하고, indicator 하나라도 실패하면 HTTP `503`을 반환합니다.
|
|
228
|
+
- `/ready`는 binary traffic-admission 결정을 내립니다. HTTP `200`은 트래픽을 수용하고 HTTP `503`은 인스턴스를 rotation에서 제외합니다. JSON body는 `{ status: 'ready' }`, `{ status: 'starting' }`, `{ status: 'unavailable' }` 중 하나를 보고합니다. `readiness`를 생략했거나 `true`인 indicator는 gate를 차단할 수 있고, `readiness: false`는 `/health` 진단을 유지하면서 `/ready`를 차단하지 않습니다. Custom `readinessChecks`는 조건만 추가합니다. Platform `critical` metadata는 readiness severity bucket이 아니라 `/health` diagnostics에 남습니다.
|
|
135
229
|
- 응답 본문은 `status`, `contributors`, `info`, `error`, `details`를 포함한 구조화된 JSON 객체입니다.
|
|
136
230
|
- 하나의 인디케이터가 여러 keyed entry를 반환할 수도 있으며, 이 경우 `/health`는 모든 entry를 `details`와 `contributors.up` / `contributors.down` 요약에 그대로 반영합니다.
|
|
137
231
|
- 지원하지 않는 status, 빈 결과, 객체가 아닌 인디케이터 결과는 조용히 버려지지 않고 `down` 진단으로 보고됩니다.
|
|
@@ -147,7 +241,7 @@ TerminusModule.forRoot({
|
|
|
147
241
|
|
|
148
242
|
`@nestjs/terminus`에서 마이그레이션할 때는 `TerminusModule.forRoot(...)`를 fluo의 기본 API로 취급하세요. fluo는 `HealthCheckService.check([...])`를 호출하는 controller-level `@HealthCheck()` 메서드를 주요 애플리케이션 계약으로 모델링하지 않습니다. 테스트나 커스텀 애플리케이션 코드에서 `TerminusHealthService.check()`를 직접 호출할 수는 있지만, 프로덕션 엔드포인트 등록은 indicator와 readiness hook을 module option에 두어 runtime `/health`와 `/ready` 경로가 platform diagnostics를 일관되게 포함하도록 해야 합니다.
|
|
149
243
|
|
|
150
|
-
Terminus는 별도의 process-only liveness route도 기본으로 만들지 않습니다. 기본 route model은 집계
|
|
244
|
+
Terminus는 별도의 process-only liveness route도 기본으로 만들지 않습니다. 기본 route model은 집계 진단을 위한 `GET /health`, HTTP `200` 또는 `503`의 binary readiness를 위한 `GET /ready`입니다. 배포 환경에서 좁은 의미의 process liveness probe가 필요하다면, Terminus가 NestJS-style 추가 route를 만들어 준다고 가정하지 말고 애플리케이션 또는 배포 계층에서 해당 probe를 정의하세요. 이 runtime-owned route는 NestJS controller의 `@HealthCheck()` 또는 `@UseGuards()` metadata를 명시적으로 거부하므로, path-scoped application 또는 adapter middleware, network policy, deployment-owned probe boundary에서 보호해야 합니다.
|
|
151
245
|
|
|
152
246
|
Runtime-specific indicator는 subpath별로 분리되어 있습니다. Node.js memory 및 disk check에는 `@fluojs/terminus/node`를 사용하고, Redis check에는 `@fluojs/terminus/redis`를 사용하세요. Prisma와 Drizzle provider helper는 token-only DI seam을 해석하므로 해당 선택적 peer가 없어도 root package import는 안전하게 유지되며, Node disk filesystem access도 lazy하게 유지되어 애플리케이션이 runtime-specific probe에 명시적으로 opt in합니다.
|
|
153
247
|
|
|
@@ -157,7 +251,7 @@ Runtime-specific indicator는 subpath별로 분리되어 있습니다. Node.js m
|
|
|
157
251
|
|
|
158
252
|
- `static forRoot(options: TerminusModuleOptions): ModuleType`
|
|
159
253
|
- 인디케이터 및 provider 등록을 위한 메인 엔트리 포인트입니다.
|
|
160
|
-
- Option에는 `indicators`, `indicatorProviders`, `readinessChecks`, `execution.indicatorTimeoutMs`, `path`가 포함됩니다.
|
|
254
|
+
- Option에는 `imports`, `indicators`, `indicatorProviders`, `readinessChecks`, `execution.indicatorTimeoutMs`, `path`가 포함됩니다.
|
|
161
255
|
|
|
162
256
|
### `TerminusHealthService`
|
|
163
257
|
|
|
@@ -165,6 +259,13 @@ Runtime-specific indicator는 subpath별로 분리되어 있습니다. Node.js m
|
|
|
165
259
|
- 현재 등록된 인디케이터를 실행해 집계된 보고서를 반환합니다.
|
|
166
260
|
- `isHealthy(): Promise<boolean>`
|
|
167
261
|
- 현재 집계 결과가 완전히 healthy 상태인지 반환합니다.
|
|
262
|
+
- `isReady(): Promise<boolean>`
|
|
263
|
+
- readiness에 참여하는 모든 indicator가 현재 `up`을 보고하는지 반환합니다.
|
|
264
|
+
|
|
265
|
+
### `HealthIndicator`
|
|
266
|
+
|
|
267
|
+
- `readiness?: boolean`
|
|
268
|
+
- indicator의 `/ready` 참여 여부를 제어하며 기본값은 `true`입니다. 내장 indicator의 모든 option 객체가 이 설정을 지원합니다.
|
|
168
269
|
|
|
169
270
|
### 직접 helper와 token
|
|
170
271
|
|
package/README.md
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# @fluojs/terminus
|
|
2
|
+
<!-- fluo-terminus-contract: registration=application-owned-TerminusModule.forRoot;health=aggregated-diagnostics;ready-admission=binary;ready-body=ready|starting|unavailable;default-liveness=absent;unhealthy-status=503;route-protection=path-scoped-external-boundary;indicator-readiness=opt-out;readiness-checks=additive -->
|
|
2
3
|
|
|
3
4
|
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
4
5
|
|
|
5
6
|
Health indicator toolkit for fluo applications. `@fluojs/terminus` layers on top of runtime health/readiness endpoints to provide dependency-aware status reporting.
|
|
6
7
|
|
|
8
|
+
Node listener helpers now belong to `@fluojs/platform-nodejs`, not `@fluojs/runtime/node`. This package boundary change does not alter Terminus registration or readiness: `TerminusModule.forRoot(...)` remains application-owned, `/health` aggregates diagnostics, and `/ready` admits traffic only when readiness succeeds.
|
|
9
|
+
|
|
7
10
|
## Table of Contents
|
|
8
11
|
|
|
9
12
|
- [Installation](#installation)
|
|
@@ -12,6 +15,7 @@ Health indicator toolkit for fluo applications. `@fluojs/terminus` layers on top
|
|
|
12
15
|
- [Common Patterns](#common-patterns)
|
|
13
16
|
- [Built-in Indicators](#built-in-indicators)
|
|
14
17
|
- [DI-Backed Indicators](#di-backed-indicators)
|
|
18
|
+
- [Endpoint Middleware](#endpoint-middleware)
|
|
15
19
|
- [Execution Guardrails](#execution-guardrails)
|
|
16
20
|
- [Failure Semantics](#failure-semantics)
|
|
17
21
|
- [NestJS Migration Boundaries](#nestjs-migration-boundaries)
|
|
@@ -73,6 +77,8 @@ The package provides several indicators out of the box:
|
|
|
73
77
|
- `MemoryHealthIndicator` (root-exported for compatibility and also available from `@fluojs/terminus/node`)
|
|
74
78
|
- `DiskHealthIndicator` (root-exported for compatibility and also available from `@fluojs/terminus/node`)
|
|
75
79
|
|
|
80
|
+
When migrating from `@nestjs/terminus`, keep ownership boundaries explicit: register custom fluo `HealthIndicator` instances in `indicators`, and use the matching `create*HealthIndicatorProvider()` only when the indicator must resolve its dependency from DI through `indicatorProviders`. Import Node memory/disk helpers from `@fluojs/terminus/node` and Redis helpers from `@fluojs/terminus/redis`; Prisma, Drizzle, and HTTP indicators are root exports. The dedicated Redis subpath keeps its optional peer out of the root import boundary, while Node helpers remain root-exported only for compatibility.
|
|
81
|
+
|
|
76
82
|
### DI-Backed Indicators
|
|
77
83
|
|
|
78
84
|
To use indicators that require dependencies from the DI container (like Redis or Database clients) without importing peer dependencies at module load time, use the documented provider factories. These helpers create indicator provider entries for `TerminusModule.forRoot({ indicatorProviders })`; they do not replace the module facade.
|
|
@@ -107,6 +113,94 @@ For Prisma, `createPrismaHealthIndicatorProvider()` prefers the lifecycle-aware
|
|
|
107
113
|
|
|
108
114
|
Provider factories are repeatable. You may register multiple providers created by the same factory in one `indicatorProviders` array when each instance uses a distinct indicator key or dependency option; Terminus keeps every provider instance under its own DI token instead of letting later same-type providers overwrite earlier ones.
|
|
109
115
|
|
|
116
|
+
### Composing DI-Backed Indicators With Dependency Modules
|
|
117
|
+
|
|
118
|
+
Terminus resolves `indicatorProviders` inside its own module scope. Importing `PrismaModule`, `DrizzleModule`, or a named `RedisModule` registration into the surrounding application module does **not** make its exported tokens visible to Terminus, because ordinary module exports are only visible to the modules that import them. Pass the dependency-owning modules through `imports` so Terminus resolves them in the scope where the indicators live.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { DrizzleModule } from '@fluojs/drizzle';
|
|
122
|
+
import { PrismaModule } from '@fluojs/prisma';
|
|
123
|
+
import { TerminusModule } from '@fluojs/terminus';
|
|
124
|
+
import { createDrizzleHealthIndicatorProvider, createPrismaHealthIndicatorProvider } from '@fluojs/terminus';
|
|
125
|
+
|
|
126
|
+
const prismaModule = PrismaModule.forRoot({ client });
|
|
127
|
+
const drizzleModule = DrizzleModule.forRoot({ database, dispose });
|
|
128
|
+
|
|
129
|
+
@Module({
|
|
130
|
+
imports: [
|
|
131
|
+
prismaModule,
|
|
132
|
+
drizzleModule,
|
|
133
|
+
TerminusModule.forRoot({
|
|
134
|
+
imports: [prismaModule, drizzleModule],
|
|
135
|
+
indicatorProviders: [
|
|
136
|
+
createPrismaHealthIndicatorProvider({ key: 'prisma' }),
|
|
137
|
+
createDrizzleHealthIndicatorProvider({ key: 'drizzle' }),
|
|
138
|
+
],
|
|
139
|
+
}),
|
|
140
|
+
],
|
|
141
|
+
})
|
|
142
|
+
class AppModule {}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The same rule applies to named Redis registrations, which are always scoped and can never be reached through a global module:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const cacheRedisModule = RedisModule.forRoot({ host: '127.0.0.1', name: 'cache', port: 6379 });
|
|
149
|
+
|
|
150
|
+
TerminusModule.forRoot({
|
|
151
|
+
imports: [cacheRedisModule],
|
|
152
|
+
indicatorProviders: [
|
|
153
|
+
createRedisHealthIndicatorProvider({ clientName: 'cache', key: 'cache-redis' }),
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
A named Redis indicator dependency is required. If no imported or global module supplies its token, module-graph validation fails during bootstrap with a `MODULE_VISIBILITY_ERROR` naming the missing token. Prisma and Drizzle indicator dependencies are optional only when their tokens are absent from the bootstrap graph: omitting their owner modules then allows the application to bootstrap and the corresponding indicator reports `down` in `/health` at request time. If a Prisma or Drizzle owner module exists elsewhere in the application but is omitted from Terminus `imports`, bootstrap instead fails with `MODULE_VISIBILITY_ERROR`; optional injection never bypasses module visibility. Dependencies published by a global module — for example `RedisModule.forRoot(...)` without a `name`, which is global by default — remain visible without an explicit `imports` entry.
|
|
159
|
+
|
|
160
|
+
### Endpoint Middleware
|
|
161
|
+
|
|
162
|
+
Use class-based `endpointMiddleware` to protect only Terminus-generated endpoints. Classes resolve through DI and run in declaration order for both `/health` and `/ready`; omitting the option preserves the default unprotected behavior. Custom `path` values are normalized before the middleware route match.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { ForbiddenException, type MiddlewareContext, type Next } from '@fluojs/http';
|
|
166
|
+
|
|
167
|
+
class HealthProbeAuthMiddleware {
|
|
168
|
+
async handle(context: MiddlewareContext, next: Next): Promise<void> {
|
|
169
|
+
if (context.request.headers['x-health-token'] !== 'secret-token') {
|
|
170
|
+
throw new ForbiddenException('Health endpoints require x-health-token.');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
await next();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
TerminusModule.forRoot({
|
|
178
|
+
endpointMiddleware: [HealthProbeAuthMiddleware],
|
|
179
|
+
path: '/internal/',
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The middleware above runs for normalized `/internal/health` and `/internal/ready` routes, not for unrelated application routes.
|
|
184
|
+
|
|
185
|
+
### Readiness Participation
|
|
186
|
+
|
|
187
|
+
Every indicator participates in both `/health` and `/ready` by default. Set `readiness: false` when a dependency must remain visible in `/health` but must not remove an otherwise ready instance from rotation.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
TerminusModule.forRoot({
|
|
191
|
+
indicators: [
|
|
192
|
+
new HttpHealthIndicator({
|
|
193
|
+
key: 'search',
|
|
194
|
+
readiness: false,
|
|
195
|
+
url: 'https://search.example.com/health',
|
|
196
|
+
}),
|
|
197
|
+
new MemoryHealthIndicator({ key: 'memory', heapUsedThresholdRatio: 0.9 }),
|
|
198
|
+
],
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
If `search` reports `down`, `/health` returns `503` with its diagnostic while `/ready` continues to evaluate `memory`, custom `readinessChecks`, and platform readiness. A failing indicator whose `readiness` is omitted or `true` continues to make both endpoints unavailable.
|
|
203
|
+
|
|
110
204
|
### Execution Guardrails
|
|
111
205
|
|
|
112
206
|
Use `execution.indicatorTimeoutMs` when custom indicators might hang or depend on slow downstreams. When a probe exceeds the configured timeout, Terminus marks that indicator as `down` instead of waiting forever.
|
|
@@ -124,14 +218,14 @@ TerminusModule.forRoot({
|
|
|
124
218
|
});
|
|
125
219
|
```
|
|
126
220
|
|
|
127
|
-
Use `path` to mount the health endpoints under a custom path
|
|
221
|
+
Use `path` to mount the health endpoints under a custom path. Indicators gate `/ready` by default; set an indicator's `readiness: false` to retain its `/health` diagnostics without blocking traffic. `readinessChecks` composes additional application-specific conditions with indicator and platform readiness checks; it does not exclude indicators.
|
|
128
222
|
|
|
129
223
|
### Failure Semantics
|
|
130
224
|
|
|
131
225
|
When an indicator returns a `down` result or throws a `HealthCheckError`, the `TerminusHealthService` aggregates the failure into a report:
|
|
132
226
|
|
|
133
|
-
- `/health` returns HTTP `
|
|
134
|
-
- `/ready`
|
|
227
|
+
- `/health` returns the aggregated `HealthCheckReport` diagnostics and returns HTTP `200` only when that report is healthy; any indicator failure returns HTTP `503`.
|
|
228
|
+
- `/ready` makes a binary traffic-admission decision: HTTP `200` admits traffic and HTTP `503` removes the instance from rotation. Its JSON body reports `{ status: 'ready' }`, `{ status: 'starting' }`, or `{ status: 'unavailable' }`. An indicator whose `readiness` is omitted or `true` can block the gate; `readiness: false` retains its `/health` diagnostics without blocking `/ready`. Custom `readinessChecks` only add conditions. Platform `critical` metadata stays in `/health` diagnostics, not readiness severity buckets.
|
|
135
229
|
- The response body contains a structured JSON object with `status`, `contributors`, `info`, `error`, and `details`.
|
|
136
230
|
- Indicators may emit multiple keyed entries in a single check result; `/health` preserves every keyed entry in `details` and in the `contributors.up` / `contributors.down` summaries.
|
|
137
231
|
- Unsupported, empty, or non-object indicator results are reported as `down` diagnostics instead of being silently discarded.
|
|
@@ -147,7 +241,7 @@ When an indicator returns a `down` result or throws a `HealthCheckError`, the `T
|
|
|
147
241
|
|
|
148
242
|
When migrating from `@nestjs/terminus`, treat `TerminusModule.forRoot(...)` as the primary fluo API. fluo does not model controller-level `@HealthCheck()` methods that call `HealthCheckService.check([...])` as the main application contract. You can still call `TerminusHealthService.check()` directly from tests or custom application code, but production endpoint registration should keep indicators and readiness hooks in module options so the runtime `/health` and `/ready` routes include platform diagnostics consistently.
|
|
149
243
|
|
|
150
|
-
Terminus also does not create a separate process-only liveness route by default. The default route model remains `GET /health` for aggregated
|
|
244
|
+
Terminus also does not create a separate process-only liveness route by default. The default route model remains `GET /health` for aggregated diagnostics and `GET /ready` for binary readiness with HTTP `200` or `503`. If your deployment requires a narrow process liveness probe, define that probe at the application or deployment layer instead of assuming Terminus will add a NestJS-style extra route. These runtime-owned routes explicitly reject a NestJS controller's `@HealthCheck()` or `@UseGuards()` metadata; protect them with path-scoped application or adapter middleware, network policy, or a deployment-owned probe boundary.
|
|
151
245
|
|
|
152
246
|
Runtime-specific indicators are split by subpath. Use `@fluojs/terminus/node` for Node.js memory and disk checks, and use `@fluojs/terminus/redis` for Redis checks. Prisma and Drizzle provider helpers resolve token-only DI seams so the root package stays import-safe when those optional peers are absent, and Node disk filesystem access stays lazy so applications opt into runtime-specific probes explicitly.
|
|
153
247
|
|
|
@@ -157,7 +251,7 @@ Runtime-specific indicators are split by subpath. Use `@fluojs/terminus/node` fo
|
|
|
157
251
|
|
|
158
252
|
- `static forRoot(options: TerminusModuleOptions): ModuleType`
|
|
159
253
|
- Main entry point for registering indicators and providers.
|
|
160
|
-
- Options include `indicators`, `indicatorProviders`, `readinessChecks`, `execution.indicatorTimeoutMs`, and `path`.
|
|
254
|
+
- Options include `imports`, `indicators`, `indicatorProviders`, `readinessChecks`, `execution.indicatorTimeoutMs`, and `path`.
|
|
161
255
|
|
|
162
256
|
### `TerminusHealthService`
|
|
163
257
|
|
|
@@ -165,6 +259,13 @@ Runtime-specific indicators are split by subpath. Use `@fluojs/terminus/node` fo
|
|
|
165
259
|
- Runs the currently registered indicators and returns the aggregated report.
|
|
166
260
|
- `isHealthy(): Promise<boolean>`
|
|
167
261
|
- Returns whether the current aggregated report is fully healthy.
|
|
262
|
+
- `isReady(): Promise<boolean>`
|
|
263
|
+
- Returns whether every indicator participating in readiness currently reports `up`.
|
|
264
|
+
|
|
265
|
+
### `HealthIndicator`
|
|
266
|
+
|
|
267
|
+
- `readiness?: boolean`
|
|
268
|
+
- Controls whether an indicator participates in `/ready`; it defaults to `true`. All bundled indicator option objects accept this setting.
|
|
168
269
|
|
|
169
270
|
### Direct helpers and tokens
|
|
170
271
|
|
package/dist/health-check.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare function runHealthCheck(indicators: readonly HealthIndicator[], e
|
|
|
18
18
|
* @param report Health report returned by `runHealthCheck(...)` or `TerminusHealthService.check()`.
|
|
19
19
|
* @param message Error message used when one or more indicators are down.
|
|
20
20
|
* @returns The same health report when every indicator is healthy.
|
|
21
|
-
* @throws {HealthCheckError} When
|
|
21
|
+
* @throws {HealthCheckError} When one or more indicators are down.
|
|
22
22
|
*/
|
|
23
23
|
export declare function assertHealthCheck(report: HealthCheckReport, message?: string): HealthCheckReport;
|
|
24
24
|
/** Service facade that resolves and runs the health indicators registered in Terminus. */
|
|
@@ -39,5 +39,11 @@ export declare class TerminusHealthService {
|
|
|
39
39
|
* @returns `true` when the aggregated report status is `ok`.
|
|
40
40
|
*/
|
|
41
41
|
isHealthy(): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Return whether every indicator participating in readiness currently reports `up`.
|
|
44
|
+
*
|
|
45
|
+
* @returns `true` when all indicators whose `readiness` setting is not `false` report `up`.
|
|
46
|
+
*/
|
|
47
|
+
isReady(): Promise<boolean>;
|
|
42
48
|
}
|
|
43
49
|
//# sourceMappingURL=health-check.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health-check.d.ts","sourceRoot":"","sources":["../src/health-check.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,2BAA2B,EAC3B,iBAAiB,EACjB,eAAe,EAGhB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"health-check.d.ts","sourceRoot":"","sources":["../src/health-check.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,2BAA2B,EAC3B,iBAAiB,EACjB,eAAe,EAGhB,MAAM,YAAY,CAAC;AAuXpB;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,gBAAgB,GAAE,2BAAgC,GACjD,OAAO,CAAC,iBAAiB,CAAC,CAE5B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,SAAyB,GAAG,iBAAiB,CAMhH;AAED,0FAA0F;AAC1F,qBAAa,qBAAqB;IAI9B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAJnC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyC;gBAG7D,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,gBAAgB,GAAE,2BAAgC;IAGrE;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAIzC;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;CAOlC"}
|
package/dist/health-check.js
CHANGED
|
@@ -23,17 +23,33 @@ function startSerializedIndicatorCheck(indicator, key, runningIndicatorChecks) {
|
|
|
23
23
|
}
|
|
24
24
|
const check = Promise.resolve().then(() => indicator.check(key));
|
|
25
25
|
runningIndicatorChecks.set(indicator, check);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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;
|
|
29
39
|
}
|
|
30
|
-
}, () => {
|
|
31
40
|
if (runningIndicatorChecks.get(indicator) === check) {
|
|
32
41
|
runningIndicatorChecks.delete(indicator);
|
|
33
42
|
}
|
|
34
|
-
}
|
|
43
|
+
};
|
|
44
|
+
check.then(releaseIndicatorOwnership, releaseIndicatorOwnership);
|
|
35
45
|
return check;
|
|
36
46
|
}
|
|
47
|
+
function normalizeHealthCheckErrorCauses(key, causes) {
|
|
48
|
+
return normalizeIndicatorResult(key, causes).map(([entryKey, state]) => [entryKey, {
|
|
49
|
+
...state,
|
|
50
|
+
status: 'down'
|
|
51
|
+
}]);
|
|
52
|
+
}
|
|
37
53
|
async function withTimeout(promise, timeoutMs) {
|
|
38
54
|
let timer;
|
|
39
55
|
try {
|
|
@@ -179,7 +195,7 @@ async function runIndicator(indicator, index, executionOptions, runningIndicator
|
|
|
179
195
|
} catch (error) {
|
|
180
196
|
if (error instanceof HealthCheckError) {
|
|
181
197
|
return {
|
|
182
|
-
entries:
|
|
198
|
+
entries: normalizeHealthCheckErrorCauses(key, error.causes),
|
|
183
199
|
indicatorKey: key
|
|
184
200
|
};
|
|
185
201
|
}
|
|
@@ -251,7 +267,7 @@ export async function runHealthCheck(indicators, executionOptions = {}) {
|
|
|
251
267
|
* @param report Health report returned by `runHealthCheck(...)` or `TerminusHealthService.check()`.
|
|
252
268
|
* @param message Error message used when one or more indicators are down.
|
|
253
269
|
* @returns The same health report when every indicator is healthy.
|
|
254
|
-
* @throws {HealthCheckError} When
|
|
270
|
+
* @throws {HealthCheckError} When one or more indicators are down.
|
|
255
271
|
*/
|
|
256
272
|
export function assertHealthCheck(report, message = 'Health check failed.') {
|
|
257
273
|
if (report.status === 'error') {
|
|
@@ -285,4 +301,14 @@ export class TerminusHealthService {
|
|
|
285
301
|
async isHealthy() {
|
|
286
302
|
return (await this.check()).status === 'ok';
|
|
287
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
|
+
}
|
|
288
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, resolveIndicatorTimeoutMs, 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,9 +79,12 @@ 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);
|
|
@@ -92,7 +95,9 @@ export class DrizzleHealthIndicator {
|
|
|
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;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;
|
|
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
|
@@ -51,9 +51,11 @@ export function createHttpHealthIndicatorProvider(options) {
|
|
|
51
51
|
/** Health indicator that probes an upstream HTTP endpoint with `fetch()`. */
|
|
52
52
|
export class HttpHealthIndicator {
|
|
53
53
|
key;
|
|
54
|
+
readiness;
|
|
54
55
|
constructor(options) {
|
|
55
56
|
this.options = options;
|
|
56
57
|
this.key = options.key;
|
|
58
|
+
this.readiness = options.readiness;
|
|
57
59
|
}
|
|
58
60
|
async check(key) {
|
|
59
61
|
const indicatorKey = resolveIndicatorKey('http', this.options.key ?? key);
|
|
@@ -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);
|
|
@@ -34,6 +34,8 @@ export interface PrismaHealthIndicatorOptions {
|
|
|
34
34
|
name?: string;
|
|
35
35
|
/** Custom ping callback for manual probes or tests. Lifecycle state is only mapped when `service` is available. */
|
|
36
36
|
ping?: () => Promise<unknown> | unknown;
|
|
37
|
+
/** Whether this indicator participates in `/ready`. Defaults to `true`. */
|
|
38
|
+
readiness?: boolean;
|
|
37
39
|
/** Lifecycle-aware Prisma service/facade handle, usually resolved from `getPrismaServiceToken(name)`. */
|
|
38
40
|
service?: PrismaServiceLike;
|
|
39
41
|
/** Explicit Prisma service token to resolve when using `createPrismaHealthIndicatorProvider(...)`. */
|
|
@@ -64,8 +66,11 @@ export declare function createPrismaHealthIndicatorProvider(options?: Omit<Prism
|
|
|
64
66
|
export declare class PrismaHealthIndicator implements HealthIndicator {
|
|
65
67
|
private readonly options;
|
|
66
68
|
readonly key: string | undefined;
|
|
69
|
+
readonly readiness: boolean | undefined;
|
|
70
|
+
private pendingProbeSettlement;
|
|
67
71
|
constructor(options?: PrismaHealthIndicatorOptions);
|
|
68
72
|
check(key: string): Promise<HealthIndicatorResult>;
|
|
73
|
+
getPendingHealthCheckSettlement(): Promise<void> | undefined;
|
|
69
74
|
}
|
|
70
75
|
export {};
|
|
71
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,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAY,KAAK,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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { optional } from '@fluojs/di';
|
|
2
|
-
import { createDownResult, createUpResult, resolveIndicatorKey, resolveIndicatorTimeoutMs, throwHealthCheckError, withIndicatorTimeout } from './utils.js';
|
|
2
|
+
import { createDownResult, createUpResult, resolveIndicatorKey, resolveIndicatorTimeoutMs, throwHealthCheckError, waitForIndicatorProbeSettlement, withIndicatorTimeout } from './utils.js';
|
|
3
3
|
const PRISMA_CLIENT = Symbol.for('fluo.prisma.client');
|
|
4
4
|
const PRISMA_SERVICE = Symbol.for('fluo.prisma.service');
|
|
5
5
|
|
|
@@ -137,9 +137,12 @@ export function createPrismaHealthIndicatorProvider(options = {}) {
|
|
|
137
137
|
/** Health indicator that maps Prisma lifecycle status and probes connectivity with a trivial query. */
|
|
138
138
|
export class PrismaHealthIndicator {
|
|
139
139
|
key;
|
|
140
|
+
readiness;
|
|
141
|
+
pendingProbeSettlement;
|
|
140
142
|
constructor(options = {}) {
|
|
141
143
|
this.options = options;
|
|
142
144
|
this.key = options.key;
|
|
145
|
+
this.readiness = options.readiness;
|
|
143
146
|
}
|
|
144
147
|
async check(key) {
|
|
145
148
|
const indicatorKey = resolveIndicatorKey('prisma', this.options.key ?? key);
|
|
@@ -150,7 +153,9 @@ export class PrismaHealthIndicator {
|
|
|
150
153
|
if (lifecycleDownResult) {
|
|
151
154
|
throwHealthCheckError('Prisma health check failed.', lifecycleDownResult);
|
|
152
155
|
}
|
|
153
|
-
|
|
156
|
+
const probe = runPrismaPing(this.options);
|
|
157
|
+
this.pendingProbeSettlement = waitForIndicatorProbeSettlement(probe);
|
|
158
|
+
await withIndicatorTimeout(probe, timeoutMs, indicatorKey);
|
|
154
159
|
return createUpResult(indicatorKey, createPrismaLifecycleUpDetails(snapshot));
|
|
155
160
|
} catch (error) {
|
|
156
161
|
if (error instanceof Error && error.name === 'HealthCheckError') {
|
|
@@ -159,4 +164,7 @@ export class PrismaHealthIndicator {
|
|
|
159
164
|
throwHealthCheckError('Prisma health check failed.', createDownResult(indicatorKey, error instanceof Error ? error.message : 'Prisma health check failed.'));
|
|
160
165
|
}
|
|
161
166
|
}
|
|
167
|
+
getPendingHealthCheckSettlement() {
|
|
168
|
+
return this.pendingProbeSettlement;
|
|
169
|
+
}
|
|
162
170
|
}
|
|
@@ -22,6 +22,8 @@ export interface RedisHealthIndicatorOptions {
|
|
|
22
22
|
key?: string;
|
|
23
23
|
/** Custom ping callback for manual probes or tests. Lifecycle state is only mapped when `client.status` is available. */
|
|
24
24
|
ping?: () => Promise<unknown> | unknown;
|
|
25
|
+
/** Whether this indicator participates in `/ready`. Defaults to `true`. */
|
|
26
|
+
readiness?: boolean;
|
|
25
27
|
/** Maximum time to wait for the ping operation. Defaults to `2_000` ms. */
|
|
26
28
|
timeoutMs?: number;
|
|
27
29
|
}
|
|
@@ -39,6 +41,10 @@ export declare function createRedisHealthIndicator(options?: RedisHealthIndicato
|
|
|
39
41
|
* default-vs-named client lifecycle boundary from `@fluojs/redis` while keeping the
|
|
40
42
|
* Redis-specific peer dependency isolated to `@fluojs/terminus/redis`.
|
|
41
43
|
*
|
|
44
|
+
* The Redis client token is a required dependency of the indicator, so it must be visible in
|
|
45
|
+
* the Terminus module scope. Pass the owning module through `TerminusModule.forRoot({ imports })`;
|
|
46
|
+
* a missing registration fails at bootstrap rather than degrading `/health` and `/ready` afterwards.
|
|
47
|
+
*
|
|
42
48
|
* @param options Optional named-client hint, timeout, key override, or custom ping callback.
|
|
43
49
|
* @returns A factory provider that exposes `RedisHealthIndicator` from the DI container.
|
|
44
50
|
*/
|
|
@@ -47,8 +53,11 @@ export declare function createRedisHealthIndicatorProvider(options?: Omit<RedisH
|
|
|
47
53
|
export declare class RedisHealthIndicator implements HealthIndicator {
|
|
48
54
|
private readonly options;
|
|
49
55
|
readonly key: string | undefined;
|
|
56
|
+
readonly readiness: boolean | undefined;
|
|
57
|
+
private pendingProbeSettlement;
|
|
50
58
|
constructor(options?: RedisHealthIndicatorOptions);
|
|
51
59
|
check(key: string): Promise<HealthIndicatorResult>;
|
|
60
|
+
getPendingHealthCheckSettlement(): Promise<void> | undefined;
|
|
52
61
|
}
|
|
53
62
|
export {};
|
|
54
63
|
//# sourceMappingURL=redis.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/indicators/redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAA+E,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/indicators/redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAA+E,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE1I,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAG1E,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IAC1C,oGAAoG;IACpG,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,gGAAgG;IAChG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yHAAyH;IACzH,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACxC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,2BAAgC,GAAG,eAAe,CAErG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,GAAE,IAAI,CAAC,2BAA2B,EAAE,QAAQ,CAAM,GAAG,QAAQ,CAQtH;AAED,4GAA4G;AAC5G,qBAAa,oBAAqB,YAAW,eAAe;IAK9C,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,2BAAgC;IAKhE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA2BxD,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS;CAG7D"}
|
package/dist/indicators/redis.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRedisPlatformStatusSnapshot, getRedisClientToken, getRedisComponentId } from '@fluojs/redis';
|
|
2
|
-
import { createDownResult, createUpResult, resolveIndicatorKey, resolveIndicatorTimeoutMs, throwHealthCheckError, withIndicatorTimeout } from './utils.js';
|
|
2
|
+
import { createDownResult, createUpResult, resolveIndicatorKey, resolveIndicatorTimeoutMs, throwHealthCheckError, waitForIndicatorProbeSettlement, withIndicatorTimeout } from './utils.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Options for probing Redis connectivity.
|
|
@@ -74,6 +74,10 @@ export function createRedisHealthIndicator(options = {}) {
|
|
|
74
74
|
* default-vs-named client lifecycle boundary from `@fluojs/redis` while keeping the
|
|
75
75
|
* Redis-specific peer dependency isolated to `@fluojs/terminus/redis`.
|
|
76
76
|
*
|
|
77
|
+
* The Redis client token is a required dependency of the indicator, so it must be visible in
|
|
78
|
+
* the Terminus module scope. Pass the owning module through `TerminusModule.forRoot({ imports })`;
|
|
79
|
+
* a missing registration fails at bootstrap rather than degrading `/health` and `/ready` afterwards.
|
|
80
|
+
*
|
|
77
81
|
* @param options Optional named-client hint, timeout, key override, or custom ping callback.
|
|
78
82
|
* @returns A factory provider that exposes `RedisHealthIndicator` from the DI container.
|
|
79
83
|
*/
|
|
@@ -92,9 +96,12 @@ export function createRedisHealthIndicatorProvider(options = {}) {
|
|
|
92
96
|
/** Health indicator that maps Redis lifecycle status and checks reachability with a ping-like operation. */
|
|
93
97
|
export class RedisHealthIndicator {
|
|
94
98
|
key;
|
|
99
|
+
readiness;
|
|
100
|
+
pendingProbeSettlement;
|
|
95
101
|
constructor(options = {}) {
|
|
96
102
|
this.options = options;
|
|
97
103
|
this.key = options.key;
|
|
104
|
+
this.readiness = options.readiness;
|
|
98
105
|
}
|
|
99
106
|
async check(key) {
|
|
100
107
|
const indicatorKey = resolveIndicatorKey('redis', this.options.key ?? key);
|
|
@@ -104,7 +111,9 @@ export class RedisHealthIndicator {
|
|
|
104
111
|
if (lifecycleDownResult) {
|
|
105
112
|
throwHealthCheckError('Redis health check failed.', lifecycleDownResult);
|
|
106
113
|
}
|
|
107
|
-
|
|
114
|
+
const probe = runRedisPing(this.options);
|
|
115
|
+
this.pendingProbeSettlement = waitForIndicatorProbeSettlement(probe);
|
|
116
|
+
await withIndicatorTimeout(probe, timeoutMs, indicatorKey);
|
|
108
117
|
return createUpResult(indicatorKey, createRedisLifecycleUpDetails(this.options));
|
|
109
118
|
} catch (error) {
|
|
110
119
|
if (error instanceof Error && error.name === 'HealthCheckError') {
|
|
@@ -113,4 +122,7 @@ export class RedisHealthIndicator {
|
|
|
113
122
|
throwHealthCheckError('Redis health check failed.', createDownResult(indicatorKey, error instanceof Error ? error.message : 'Redis health check failed.'));
|
|
114
123
|
}
|
|
115
124
|
}
|
|
125
|
+
getPendingHealthCheckSettlement() {
|
|
126
|
+
return this.pendingProbeSettlement;
|
|
127
|
+
}
|
|
116
128
|
}
|
|
@@ -39,6 +39,13 @@ export declare function createDownResult(key: string, message: string, details?:
|
|
|
39
39
|
* @returns The original promise result when it finishes in time.
|
|
40
40
|
*/
|
|
41
41
|
export declare function withIndicatorTimeout<T>(promise: Promise<T>, timeoutMs: number, indicatorName: string): Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Convert a probe promise into a settlement signal that never rejects.
|
|
44
|
+
*
|
|
45
|
+
* @param promise Probe promise whose completion releases its execution ownership.
|
|
46
|
+
* @returns A promise that resolves after the probe either fulfills or rejects.
|
|
47
|
+
*/
|
|
48
|
+
export declare function waitForIndicatorProbeSettlement(promise: Promise<unknown>): Promise<void>;
|
|
42
49
|
/**
|
|
43
50
|
* Resolve the effective key used in reports for one indicator execution.
|
|
44
51
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/indicators/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,6EAA6E;AAC7E,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,MAAM,GACpB,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,qBAAqB,CAOxG;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,qBAAqB,CAQvB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,CAAC,CAAC,CAyBZ;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,GAAG,KAAK,CAE3F"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/indicators/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,6EAA6E;AAC7E,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,MAAM,GACpB,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,qBAAqB,CAOxG;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,qBAAqB,CAQvB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,CAAC,CAAC,CAyBZ;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,GAAG,KAAK,CAE3F"}
|
package/dist/indicators/utils.js
CHANGED
|
@@ -85,6 +85,16 @@ export function withIndicatorTimeout(promise, timeoutMs, indicatorName) {
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Convert a probe promise into a settlement signal that never rejects.
|
|
90
|
+
*
|
|
91
|
+
* @param promise Probe promise whose completion releases its execution ownership.
|
|
92
|
+
* @returns A promise that resolves after the probe either fulfills or rejects.
|
|
93
|
+
*/
|
|
94
|
+
export function waitForIndicatorProbeSettlement(promise) {
|
|
95
|
+
return promise.then(() => undefined, () => undefined);
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
/**
|
|
89
99
|
* Resolve the effective key used in reports for one indicator execution.
|
|
90
100
|
*
|
package/dist/module.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ export declare class TerminusModule {
|
|
|
5
5
|
/**
|
|
6
6
|
* Register Terminus health indicators and readiness hooks.
|
|
7
7
|
*
|
|
8
|
+
* DI-backed indicator providers resolve inside the Terminus module scope. A named Redis token
|
|
9
|
+
* is required, so its owner module must be listed in `imports`; otherwise bootstrap fails with
|
|
10
|
+
* `MODULE_VISIBILITY_ERROR`. Prisma and Drizzle owner tokens are optional only when absent from
|
|
11
|
+
* the bootstrap graph: omitting their modules then lets the application bootstrap and their
|
|
12
|
+
* indicators report `down` on health checks. Existing sibling owner tokens still require
|
|
13
|
+
* `imports`; optional injection never bypasses module visibility.
|
|
14
|
+
*
|
|
8
15
|
* @example
|
|
9
16
|
* ```ts
|
|
10
17
|
* import { MemoryHealthIndicator } from '@fluojs/terminus/node';
|
|
@@ -14,7 +21,17 @@ export declare class TerminusModule {
|
|
|
14
21
|
* });
|
|
15
22
|
* ```
|
|
16
23
|
*
|
|
17
|
-
* @
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const prismaModule = PrismaModule.forRoot({ client });
|
|
27
|
+
*
|
|
28
|
+
* TerminusModule.forRoot({
|
|
29
|
+
* imports: [prismaModule],
|
|
30
|
+
* indicatorProviders: [createPrismaHealthIndicatorProvider({ key: 'prisma' })],
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @param options Terminus health indicator, dependency import, and readiness configuration.
|
|
18
35
|
* @returns A runtime module exposing health endpoints and `TerminusHealthService`.
|
|
19
36
|
*/
|
|
20
37
|
static forRoot(options?: TerminusModuleOptions): ModuleType;
|
package/dist/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,UAAU,EAKhB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAA4D,qBAAqB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,UAAU,EAKhB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAA4D,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAkSlH,uFAAuF;AACvF,qBAAa,cAAc;IACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,qBAA0B,GAAG,UAAU;CAGhE"}
|
package/dist/module.js
CHANGED
|
@@ -23,6 +23,7 @@ function createTerminusProviders(options = {}) {
|
|
|
23
23
|
execution: {
|
|
24
24
|
...(options.execution ?? {})
|
|
25
25
|
},
|
|
26
|
+
imports: [...(options.imports ?? [])],
|
|
26
27
|
indicators: copyIndicators(options.indicators),
|
|
27
28
|
indicatorProviders: copyProviders(options.indicatorProviders),
|
|
28
29
|
readinessChecks: [...(options.readinessChecks ?? [])]
|
|
@@ -149,7 +150,9 @@ function withPlatformDiagnostics(report, health, readiness) {
|
|
|
149
150
|
}
|
|
150
151
|
function createTerminusRuntimeModule(options = {}) {
|
|
151
152
|
const readinessChecks = [...(options.readinessChecks ?? [])];
|
|
153
|
+
const terminusImports = [...(options.imports ?? [])];
|
|
152
154
|
const healthModule = HealthModule.forRoot({
|
|
155
|
+
endpointMiddleware: options.endpointMiddleware,
|
|
153
156
|
healthCheck: async ctx => {
|
|
154
157
|
const healthService = await ctx.container.resolve(TerminusHealthService);
|
|
155
158
|
const platformShell = await ctx.container.resolve(PLATFORM_SHELL);
|
|
@@ -169,9 +172,11 @@ function createTerminusRuntimeModule(options = {}) {
|
|
|
169
172
|
class TerminusRuntimeModule {}
|
|
170
173
|
return defineModule(TerminusRuntimeModule, {
|
|
171
174
|
exports: [TERMINUS_HEALTH_INDICATORS, TERMINUS_INDICATOR_PROVIDER_TOKENS, TerminusHealthService],
|
|
172
|
-
imports: [healthModule],
|
|
175
|
+
imports: [healthModule, ...terminusImports],
|
|
173
176
|
providers: [...createTerminusProviders({
|
|
174
177
|
execution: options.execution,
|
|
178
|
+
endpointMiddleware: options.endpointMiddleware,
|
|
179
|
+
imports: terminusImports,
|
|
175
180
|
indicatorProviders: options.indicatorProviders,
|
|
176
181
|
indicators: options.indicators,
|
|
177
182
|
path: options.path,
|
|
@@ -185,7 +190,7 @@ function createTerminusRuntimeModule(options = {}) {
|
|
|
185
190
|
onApplicationBootstrap() {
|
|
186
191
|
healthModule.addReadinessCheck(async ctx => {
|
|
187
192
|
const platformShell = await ctx.container.resolve(PLATFORM_SHELL);
|
|
188
|
-
const [indicatorHealthy, readiness] = await Promise.all([healthService.
|
|
193
|
+
const [indicatorHealthy, readiness] = await Promise.all([healthService.isReady(), platformShell.ready()]);
|
|
189
194
|
return indicatorHealthy && readiness.status === 'ready';
|
|
190
195
|
});
|
|
191
196
|
}
|
|
@@ -200,6 +205,13 @@ export class TerminusModule {
|
|
|
200
205
|
/**
|
|
201
206
|
* Register Terminus health indicators and readiness hooks.
|
|
202
207
|
*
|
|
208
|
+
* DI-backed indicator providers resolve inside the Terminus module scope. A named Redis token
|
|
209
|
+
* is required, so its owner module must be listed in `imports`; otherwise bootstrap fails with
|
|
210
|
+
* `MODULE_VISIBILITY_ERROR`. Prisma and Drizzle owner tokens are optional only when absent from
|
|
211
|
+
* the bootstrap graph: omitting their modules then lets the application bootstrap and their
|
|
212
|
+
* indicators report `down` on health checks. Existing sibling owner tokens still require
|
|
213
|
+
* `imports`; optional injection never bypasses module visibility.
|
|
214
|
+
*
|
|
203
215
|
* @example
|
|
204
216
|
* ```ts
|
|
205
217
|
* import { MemoryHealthIndicator } from '@fluojs/terminus/node';
|
|
@@ -209,7 +221,17 @@ export class TerminusModule {
|
|
|
209
221
|
* });
|
|
210
222
|
* ```
|
|
211
223
|
*
|
|
212
|
-
* @
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* const prismaModule = PrismaModule.forRoot({ client });
|
|
227
|
+
*
|
|
228
|
+
* TerminusModule.forRoot({
|
|
229
|
+
* imports: [prismaModule],
|
|
230
|
+
* indicatorProviders: [createPrismaHealthIndicatorProvider({ key: 'prisma' })],
|
|
231
|
+
* });
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* @param options Terminus health indicator, dependency import, and readiness configuration.
|
|
213
235
|
* @returns A runtime module exposing health endpoints and `TerminusHealthService`.
|
|
214
236
|
*/
|
|
215
237
|
static forRoot(options = {}) {
|
package/dist/node-runtime.d.ts
CHANGED
|
@@ -5,7 +5,11 @@ interface NodeMemoryUsageSnapshot {
|
|
|
5
5
|
heapUsed: number;
|
|
6
6
|
rss: number;
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Read Node.js process memory usage through the Terminus Node runtime seam.
|
|
10
|
+
*
|
|
11
|
+
* @returns A snapshot of the current Node.js process memory usage.
|
|
12
|
+
*/
|
|
9
13
|
export declare function readNodeMemoryUsage(): NodeMemoryUsageSnapshot;
|
|
10
14
|
export {};
|
|
11
15
|
//# sourceMappingURL=node-runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-runtime.d.ts","sourceRoot":"","sources":["../src/node-runtime.ts"],"names":[],"mappings":"AAAA,UAAU,uBAAuB;IAC/B,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;AAkBD
|
|
1
|
+
{"version":3,"file":"node-runtime.d.ts","sourceRoot":"","sources":["../src/node-runtime.ts"],"names":[],"mappings":"AAAA,UAAU,uBAAuB;IAC/B,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;AAkBD;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,uBAAuB,CAQ7D"}
|
package/dist/node-runtime.js
CHANGED
|
@@ -3,7 +3,11 @@ function resolveNodeProcess() {
|
|
|
3
3
|
return runtimeGlobal.process;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Read Node.js process memory usage through the Terminus Node runtime seam.
|
|
8
|
+
*
|
|
9
|
+
* @returns A snapshot of the current Node.js process memory usage.
|
|
10
|
+
*/
|
|
7
11
|
export function readNodeMemoryUsage() {
|
|
8
12
|
const memoryUsage = resolveNodeProcess()?.memoryUsage;
|
|
9
13
|
if (typeof memoryUsage !== 'function') {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { Constructor } from '@fluojs/core';
|
|
1
2
|
import type { Provider } from '@fluojs/di';
|
|
2
|
-
import type {
|
|
3
|
+
import type { Middleware } from '@fluojs/http';
|
|
4
|
+
import type { ModuleType, PlatformHealthReport, PlatformReadinessReport, ReadinessCheck } from '@fluojs/runtime';
|
|
3
5
|
/** Status values returned by one health indicator execution. */
|
|
4
6
|
export type HealthIndicatorStatus = 'up' | 'down';
|
|
5
7
|
/** One indicator state payload stored under its resolved key. */
|
|
@@ -14,6 +16,8 @@ export type HealthIndicatorResult = {
|
|
|
14
16
|
export interface HealthIndicator {
|
|
15
17
|
check(key: string): Promise<HealthIndicatorResult>;
|
|
16
18
|
key?: string;
|
|
19
|
+
/** Whether this indicator participates in `/ready`. Defaults to `true`. */
|
|
20
|
+
readiness?: boolean;
|
|
17
21
|
}
|
|
18
22
|
/** Structured health report returned by Terminus aggregation helpers. */
|
|
19
23
|
export interface HealthCheckReport {
|
|
@@ -44,6 +48,18 @@ export interface HealthCheckExecutionOptions {
|
|
|
44
48
|
*/
|
|
45
49
|
export interface TerminusModuleOptions {
|
|
46
50
|
execution?: HealthCheckExecutionOptions;
|
|
51
|
+
/** Class-based middleware applied only to the generated `/health` and `/ready` endpoints. */
|
|
52
|
+
endpointMiddleware?: readonly Constructor<Middleware>[];
|
|
53
|
+
/**
|
|
54
|
+
* Modules whose exported tokens must be visible to `indicatorProviders`.
|
|
55
|
+
*
|
|
56
|
+
* Terminus registers `indicatorProviders` inside its own module scope, so a
|
|
57
|
+
* dependency-owning module such as `PrismaModule`, `DrizzleModule`, or a named
|
|
58
|
+
* `RedisModule` registration must be imported here for its exported tokens to
|
|
59
|
+
* resolve. Importing the module into the parent application module alone does
|
|
60
|
+
* not make its exports visible to Terminus.
|
|
61
|
+
*/
|
|
62
|
+
imports?: readonly ModuleType[];
|
|
47
63
|
indicators?: readonly HealthIndicator[];
|
|
48
64
|
indicatorProviders?: readonly Provider[];
|
|
49
65
|
path?: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjH,gEAAgE;AAChE,MAAM,MAAM,qBAAqB,GAAG,IAAI,GAAG,MAAM,CAAC;AAElD,iEAAiE;AACjE,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,qBAAqB,CAAC;CAC/B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE5B,qDAAqD;AACrD,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAAC;CACrC,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE;QACZ,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,EAAE,EAAE,MAAM,EAAE,CAAC;KACd,CAAC;IACF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC9C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE;QACT,MAAM,EAAE,oBAAoB,CAAC;QAC7B,SAAS,EAAE,uBAAuB,CAAC;KACpC,CAAC;IACF,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;CACxB;AAED,mFAAmF;AACnF,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,2BAA2B,CAAC;IACxC,6FAA6F;IAC7F,kBAAkB,CAAC,EAAE,SAAS,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;IACxD;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACxC,kBAAkB,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CAC7C"}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"liveness",
|
|
10
10
|
"health-check"
|
|
11
11
|
],
|
|
12
|
-
"version": "
|
|
12
|
+
"version": "2.0.0",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"directory": "packages/terminus"
|
|
19
19
|
},
|
|
20
20
|
"engines": {
|
|
21
|
-
"node": ">=
|
|
21
|
+
"node": ">=24.0.0 <27"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
@@ -44,15 +44,15 @@
|
|
|
44
44
|
"dist"
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@fluojs/core": "^
|
|
48
|
-
"@fluojs/di": "^
|
|
49
|
-
"@fluojs/http": "^
|
|
50
|
-
"@fluojs/runtime": "^
|
|
47
|
+
"@fluojs/core": "^2.0.0",
|
|
48
|
+
"@fluojs/di": "^3.0.0",
|
|
49
|
+
"@fluojs/http": "^3.0.0",
|
|
50
|
+
"@fluojs/runtime": "^3.0.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@fluojs/drizzle": "^
|
|
54
|
-
"@fluojs/prisma": "^
|
|
55
|
-
"@fluojs/redis": "^
|
|
53
|
+
"@fluojs/drizzle": "^2.0.0",
|
|
54
|
+
"@fluojs/prisma": "^2.0.0",
|
|
55
|
+
"@fluojs/redis": "^2.0.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependenciesMeta": {
|
|
58
58
|
"@fluojs/drizzle": {
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"vitest": "^
|
|
70
|
-
"@fluojs/testing": "^
|
|
69
|
+
"vitest": "^4.1.11",
|
|
70
|
+
"@fluojs/testing": "^3.0.0"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
73
|
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|