@fluojs/runtime 1.0.0-beta.9 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +38 -15
- package/README.md +38 -15
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +113 -21
- package/dist/health/diagnostics.d.ts.map +1 -1
- package/dist/health/diagnostics.js +4 -4
- package/dist/health/health.d.ts +17 -4
- package/dist/health/health.d.ts.map +1 -1
- package/dist/health/health.js +34 -11
- package/dist/http-adapter-shared.d.ts.map +1 -1
- package/dist/http-adapter-shared.js +31 -4
- package/dist/internal/core-metadata.d.ts +84 -0
- package/dist/internal/core-metadata.d.ts.map +1 -0
- package/dist/internal/core-metadata.js +83 -0
- package/dist/internal/http-runtime.d.ts +5 -0
- package/dist/internal/http-runtime.d.ts.map +1 -0
- package/dist/internal/http-runtime.js +4 -0
- package/dist/internal.d.ts +2 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/logging/logger.d.ts +30 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +63 -5
- package/dist/module-graph.d.ts +1 -1
- package/dist/module-graph.d.ts.map +1 -1
- package/dist/module-graph.js +5 -5
- package/dist/node/internal-node-request.d.ts.map +1 -1
- package/dist/node/internal-node-request.js +1 -2
- package/dist/node/internal-node.d.ts.map +1 -1
- package/dist/node/internal-node.js +15 -0
- package/dist/tokens.d.ts +12 -0
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +11 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web.js +3 -3
- package/package.json +6 -6
package/README.ko.md
CHANGED
|
@@ -26,8 +26,8 @@ npm install @fluojs/runtime
|
|
|
26
26
|
다음과 같은 경우에 이 패키지를 사용합니다:
|
|
27
27
|
- **fluo 애플리케이션 부트스트랩**: 모듈을 실행 중인 HTTP 서버나 마이크로서비스로 변환할 때.
|
|
28
28
|
- **DI 및 라이프사이클 오케스트레이션**: 모듈 그래프 컴파일, 프로바이더 연결 및 애플리케이션 훅(`onModuleInit`, `onApplicationBootstrap`)을 관리할 때.
|
|
29
|
-
- **독립형 컨텍스트 생성**: HTTP 서버는 필요 없지만 DI가 필요한 CLI
|
|
30
|
-
- **진단 및 검사**: CLI
|
|
29
|
+
- **독립형 컨텍스트 생성**: HTTP 서버는 필요 없지만 DI가 필요한 CLI task, script 또는 worker를 실행할 때.
|
|
30
|
+
- **진단 및 검사**: CLI 내보내기를 위한 기계 읽기 가능한 플랫폼 snapshot과 diagnostic issue를 생산하되, 그래프 보기와 Mermaid 표현은 Studio에 맡길 때.
|
|
31
31
|
|
|
32
32
|
## 퀵 스타트
|
|
33
33
|
|
|
@@ -115,7 +115,7 @@ class DatabaseModule {}
|
|
|
115
115
|
|
|
116
116
|
@Module({
|
|
117
117
|
imports: [DatabaseModule],
|
|
118
|
-
|
|
118
|
+
providers: [UsersService], // UsersService가 DatabaseService를 주입받을 수 있음
|
|
119
119
|
})
|
|
120
120
|
class UsersModule {}
|
|
121
121
|
```
|
|
@@ -126,10 +126,14 @@ class UsersModule {}
|
|
|
126
126
|
- `@fluojs/runtime/node`에서는 Node 요청 바디 파싱 전에 primary `content-type` media type을 normalize한 뒤 JSON 및 멀티파트 여부를 판단하므로, 대소문자가 섞인 JSON/멀티파트 헤더도 문서화된 파서 동작을 그대로 유지합니다.
|
|
127
127
|
- Node 기반 및 Web 표준 요청 wrapper는 바디 파싱 전에 저비용 요청 metadata를 snapshot으로 고정한 뒤 dispatch 경계에서 `body`/`rawBody`를 한 번 materialize하므로 userland는 계속 동기 parsed 값을 관찰합니다.
|
|
128
128
|
- Node 기반 쿠키/쿼리 값과 Web 표준 헤더는 요청 wrapper가 생성되는 시점에 snapshot으로 고정된 뒤 요청별로 lazy하게 normalize되고 memoize됩니다. 이후 upstream 객체가 변경되어도 `FrameworkRequest` view는 바뀌지 않습니다.
|
|
129
|
+
- Node 기반 request context ID는 `x-request-id`를 우선 사용하고, `x-request-id`가 없으면 `x-correlation-id`로 fallback합니다. 따라서 error response와 request-aware integration이 upstream correlation identifier를 유지합니다.
|
|
129
130
|
- `ApplicationContext.get()`과 `Application.get()`은 bootstrap 시점에 알려진 직접 root singleton class/factory provider 조회만 memoize하며, alias, request, transient, 종료 이후, multi-provider, `container.override()` 해석 의미는 그대로 유지합니다.
|
|
130
131
|
- `multi: true` provider token은 context cache에 memoize되지 않습니다. 각 `get()` 호출은 DI로 위임되어 컨테이너가 새로운 contribution 배열을 조립하며, 각 contribution 자체는 해당 provider scope에 따라 재사용됩니다.
|
|
131
132
|
- `duplicateProviderPolicy`가 `warn` 또는 `ignore`일 때 context cache 적격성과 lifecycle hook 실행은 bootstrap이 선택한 effective winning provider를 기준으로 결정됩니다. stale losing provider는 cache entry나 lifecycle hook을 만들지 않습니다.
|
|
132
133
|
- 애플리케이션 또는 컨텍스트 bootstrap이 런타임 리소스나 lifecycle instance 생성 이후 실패하면 fluo는 readiness를 초기화하고, 등록된 runtime cleanup callback을 실행하며, 그 시점까지 해석된 instance의 shutdown hook을 `bootstrap-failed`로 호출하고, 컨테이너를 dispose하고, cleanup 실패를 로그로 남긴 뒤 원래 bootstrap error를 다시 던집니다.
|
|
134
|
+
- `Application.listen()`과 microservice `listen()`은 shutdown과 직렬화됩니다. 겹치는 startup 호출은 같은 in-flight startup을 공유하고, shutdown은 진행 중인 startup이 끝날 때까지 기다리며, shutdown과 경합한 startup은 close 시작 이후 shell을 다시 `ready`로 전이할 수 없습니다.
|
|
135
|
+
- 종료 시그널 등록 실패는 사용자가 관찰할 수 있습니다. `runNodeApplication(...)`, `bootstrapNodeApplication(...)`, adapter 소유 runtime helper는 이미 시작된 애플리케이션을 `bootstrap-failed`로 닫고, close 실패가 있으면 별도로 로그로 남기며, 원래 registration error로 reject합니다.
|
|
136
|
+
- 종료 시그널 등록 해제 실패는 애플리케이션 close를 건너뛰지 않습니다. `app.close()`는 항상 adapter shutdown, lifecycle hook, runtime cleanup callback, container dispose까지 계속 진행합니다. close 자체가 성공하면 unregistration error로 reject하고, close도 실패하면 두 실패를 모두 담은 aggregate로 reject합니다.
|
|
133
137
|
- 연결된 microservice는 부모 `Application`이 소유하는 child입니다. `startAllMicroservices()`는 순차적으로 시작하며 이후 child 시작이 실패하면 이미 시작된 child를 `bootstrap-failed`로 rollback하고, `Application.close(signal)`은 부모 lifecycle hook, adapter 종료, container dispose보다 먼저 연결된 child를 닫습니다.
|
|
134
138
|
- `FluoFactory.createMicroservice()`는 cleanup이 실패해도 원래 bootstrap/runtime 해석 오류를 보존하고 cleanup 실패는 별도로 로그로 남깁니다.
|
|
135
139
|
- Bootstrap은 독립적인 singleton lifecycle provider를 병렬로 해석한 뒤 lifecycle hook은 결정적인 provider 순서대로 실행합니다.
|
|
@@ -137,29 +141,37 @@ class UsersModule {}
|
|
|
137
141
|
- `createNodeHttpAdapter(...)`, `bootstrapNodeApplication(...)`, `runNodeApplication(...)`는 `maxBodySize`를 0 이상의 정수 바이트 수로만 받으며, 값이 잘못되면 어댑터 생성/부트스트랩 단계에서 즉시 실패합니다.
|
|
138
142
|
- 응답 스트림 백프레셔 헬퍼는 `drain`, `close`, `error` 중 어느 경우에도 `waitForDrain()`을 완료시켜 끊어진 연결에서 스트리밍 작성기가 멈추지 않도록 합니다.
|
|
139
143
|
- 런타임 health 모듈은 bootstrap이 ready로 표시하기 전까지 `/ready`를 HTTP 503과 `starting`으로 보고하며, 애플리케이션/컨텍스트 종료가 시작되는 즉시, 종료 시도가 실패하더라도 다시 `starting`으로 내려갑니다.
|
|
144
|
+
- 런타임 health module readiness check는 현재 `RequestContext`를 받으므로, public integration이 internal runtime token을 import하지 않고도 runtime-exposed status provider를 해석할 수 있습니다.
|
|
140
145
|
- 시그널 기반 종료 헬퍼는 bounded drain semantics를 유지하면서 timeout/실패 상황을 로그와 `process.exitCode`로 보고하지만, 최종 프로세스 종료 소유권은 주변 호스트 런타임에 남겨 둡니다.
|
|
141
|
-
- 플랫폼 snapshot 생산은 런타임에 남아 있고, 그래프
|
|
146
|
+
- 플랫폼 snapshot 및 diagnostic issue 생산은 런타임에 남아 있고, 그래프 보기, filtering 표현, Mermaid 렌더링은 CLI 및 자동화 호출자가 소비하는 Studio 소유 계약입니다.
|
|
142
147
|
- 모듈 그래프 컴파일 결과 캐시는 `moduleGraphCache: true`를 통한 opt-in입니다. 캐시 항목은 root module identity, runtime provider, validation token, core metadata version, compile algorithm version으로 식별되며, 성공한 컴파일만 저장하고 호출자 mutation이 이후 bootstrap을 오염시키지 않도록 격리된 그래프 복사본을 반환합니다.
|
|
143
148
|
|
|
144
149
|
## 공개 API 개요
|
|
145
150
|
|
|
146
151
|
- `fluoFactory`: 패키지 예제에서 사용하는 런타임 부트스트랩 파사드의 lower-camel-case 별칭입니다.
|
|
147
|
-
- `FluoFactory`:
|
|
152
|
+
- `FluoFactory`: 명시적 static 접근을 제공하는 클래스 기반 런타임 부트스트랩 파사드입니다.
|
|
148
153
|
- `Application`: `ApplicationContext`를 확장하며 `listen()`, `dispatch()`, `state`를 포함합니다.
|
|
149
|
-
- `ApplicationContext`: `get<T>(token)`, `close()` 기능을 제공하며 `container
|
|
154
|
+
- `ApplicationContext`: `get<T>(token)`, `close()` 기능을 제공하며 `container`, `modules`, bootstrap diagnostics에 접근할 수 있습니다.
|
|
150
155
|
- `LifecycleHooks`: `OnModuleInit`, `OnApplicationBootstrap`, `OnModuleDestroy`, `OnApplicationShutdown`를 묶는 편의 union 타입입니다.
|
|
151
|
-
- `
|
|
156
|
+
- `HealthModule.forRoot(options)`: bootstrap 및 shutdown 라이프사이클 전이에 맞춰 readiness marker를 관리하는 런타임 소유 `/health`, `/ready` 모듈 파사드입니다.
|
|
157
|
+
- `createHealthModule(options)`: 같은 런타임 health module 계약을 위한 deprecated compatibility helper입니다. 애플리케이션-facing module import에서는 `HealthModule.forRoot(...)`를 우선 사용하세요.
|
|
158
|
+
- `ReadinessCheck`: runtime health module이 사용하는 function type입니다. Check는 `/ready` request context를 받고 boolean 또는 promise를 반환합니다.
|
|
152
159
|
- `defineModule(cls, metadata)`: 프로그래밍 방식의 모듈 정의 헬퍼입니다.
|
|
153
160
|
- `bootstrapApplication(options)`: 저수준 비동기 부트스트랩 함수입니다.
|
|
161
|
+
- `bootstrapModule(...)`: 저수준 module graph bootstrap helper입니다.
|
|
162
|
+
- `createBootstrapTimingDiagnostics(...)`, `createRuntimeDiagnosticsGraph(...)`: CLI/support tooling을 위한 runtime 소유 diagnostics snapshot helper입니다. 이 helper들은 기계 읽기 가능한 데이터를 생산하며, Studio가 viewer parsing, graph presentation, Mermaid rendering을 소유합니다.
|
|
163
|
+
- `createRequestAbortContext(...)`, `trackActiveRequestTransaction(...)`, `untrackActiveRequestTransaction(...)`: runtime-aware integration이 사용하는 request abort 및 active transaction helper입니다.
|
|
154
164
|
|
|
155
165
|
## 플랫폼 전용 서브경로
|
|
156
166
|
|
|
157
167
|
| 서브경로 | 용도 |
|
|
158
168
|
| :--- | :--- |
|
|
159
169
|
| `@fluojs/runtime/node` | 로거 팩토리, Node 어댑터/부트스트랩 헬퍼, 종료 시그널 등록을 위한 지원되는 Node.js 전용 진입점입니다. |
|
|
160
|
-
| `@fluojs/runtime/web` | Bun, Deno, Cloudflare Workers를 위한 공유
|
|
161
|
-
| `@fluojs/runtime/internal` |
|
|
162
|
-
| `@fluojs/runtime/internal-node` |
|
|
170
|
+
| `@fluojs/runtime/web` | Bun, Deno, Cloudflare Workers를 위한 공유 Web 표준 요청/응답 유틸리티입니다. `createWebRequestResponseFactory`, `dispatchWebRequest`, `createWebFrameworkRequest`, `parseMultipart`를 포함합니다. |
|
|
171
|
+
| `@fluojs/runtime/internal` | package integration을 위한 token-only internal seam입니다. |
|
|
172
|
+
| `@fluojs/runtime/internal-node` | adapter/runtime plumbing을 위한 Node 전용 internal seam이며, 애플리케이션 코드에서는 `@fluojs/runtime/node`를 우선 사용하세요. |
|
|
173
|
+
| `@fluojs/runtime/internal/http-adapter` | platform package를 위한 internal HTTP adapter seam입니다. |
|
|
174
|
+
| `@fluojs/runtime/internal/request-response-factory` | platform package를 위한 internal request/response factory seam입니다. |
|
|
163
175
|
|
|
164
176
|
### Node 전용 서브경로 (`@fluojs/runtime/node`)
|
|
165
177
|
|
|
@@ -182,14 +194,25 @@ const adapter = createNodeHttpAdapter({
|
|
|
182
194
|
});
|
|
183
195
|
```
|
|
184
196
|
|
|
185
|
-
공개 Node 런타임 surface에서 `maxBodySize`는
|
|
197
|
+
공개 Node 런타임 surface에서 `maxBodySize`, `retryDelayMs`, `retryLimit`, `shutdownTimeoutMs`는 숫자로 표현된 0 이상의 정수만 허용합니다. `'1mb'`, 소수 retry 횟수, 음수 shutdown timeout 같은 값은 나중에 암묵 변환되지 않고 어댑터 생성 시점에 즉시 거부됩니다. Node request context ID는 `x-request-id`를 우선 사용하며, 없으면 `x-correlation-id`를 runtime error response와 request-aware integration의 request ID fallback으로 사용합니다.
|
|
186
198
|
|
|
187
|
-
- `createConsoleApplicationLogger()`: `process.stdout`/`process.stderr`를 사용하는 컬러 콘솔
|
|
199
|
+
- `createConsoleApplicationLogger()`: `process.stdout`/`process.stderr`를 사용하는 컬러 콘솔 로거입니다. 기본값은 pretty 형식입니다. 더 간결한 `[fluo] LEVEL [context] message` 줄을 원하면 `{ mode: 'minimal' }`, 런타임 로거 출력을 숨기려면 `{ mode: 'silent' }`, 낮은 심각도 메시지를 걸러내려면 `{ level: 'warn' }` 같은 threshold, 결정적인 비컬러 출력을 원하면 `{ color: false }`를 전달하세요.
|
|
188
200
|
- `createJsonApplicationLogger()`: `process.stdout`/`process.stderr`를 사용하는 구조화된 JSON 로거.
|
|
189
|
-
- `createNodeHttpAdapter()`: 어댑터 우선 런타임 구성을 위한 raw Node `http`/`https` 어댑터 팩토리입니다. primary Node 요청 `content-type`을 JSON/멀티파트 판별 전에 normalize하며, `maxBodySize`는
|
|
190
|
-
- `bootstrapNodeApplication()` / `runNodeApplication()`:
|
|
201
|
+
- `createNodeHttpAdapter()`: 어댑터 우선 런타임 구성을 위한 raw Node `http`/`https` 어댑터 팩토리입니다. primary Node 요청 `content-type`을 JSON/멀티파트 판별 전에 normalize하며, `maxBodySize`, `retryDelayMs`, `retryLimit`, `shutdownTimeoutMs`는 0 이상의 정수만 받습니다.
|
|
202
|
+
- `bootstrapNodeApplication()` / `runNodeApplication()`: 직접 Node runtime flow에서 사용하는 Node 전용 부트스트랩 헬퍼.
|
|
191
203
|
- `createNodeShutdownSignalRegistration()`, `defaultNodeShutdownSignals()`, `registerShutdownSignals()`: 호스트가 명시적으로 시그널 wiring을 제어할 때 쓰는 종료 등록 헬퍼.
|
|
192
204
|
|
|
205
|
+
런타임 애플리케이션 로깅은 CLI lifecycle reporting과 별개입니다. 애플리케이션/런타임 자체가 내는 로그를 바꾸고 싶을 때 `ApplicationLogger`를 설정하세요:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { createConsoleApplicationLogger, createJsonApplicationLogger } from '@fluojs/runtime/node';
|
|
209
|
+
|
|
210
|
+
const minimalLogger = createConsoleApplicationLogger({ mode: 'minimal', level: 'warn' });
|
|
211
|
+
const jsonLogger = createJsonApplicationLogger();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
개발 명령의 raw child-process 출력이 필요하면 대신 `fluo dev --verbose` 같은 CLI reporter flag를 사용하세요.
|
|
215
|
+
|
|
193
216
|
더 저수준의 Node compression internals는 공개 `@fluojs/runtime/node` 계약이 아니라 `@fluojs/runtime/internal-node` seam 뒤에 둡니다.
|
|
194
217
|
|
|
195
218
|
## 관련 패키지
|
|
@@ -198,7 +221,7 @@ const adapter = createNodeHttpAdapter({
|
|
|
198
221
|
- [@fluojs/di](../di): 의존성 주입(DI) 컨테이너 구현체.
|
|
199
222
|
- [@fluojs/http](../http): HTTP 라우팅, 컨트롤러 및 디스패처.
|
|
200
223
|
- [@fluojs/platform-nodejs](../platform-nodejs): 공식 Node.js HTTP 어댑터.
|
|
201
|
-
- [@fluojs/studio](../studio): 런타임이 생산한 snapshot
|
|
224
|
+
- [@fluojs/studio](../studio): 런타임이 생산한 snapshot과 diagnostic issue를 위한 viewer, filtering, rendering helper.
|
|
202
225
|
|
|
203
226
|
## 예제 소스
|
|
204
227
|
|
package/README.md
CHANGED
|
@@ -26,8 +26,8 @@ npm install @fluojs/runtime
|
|
|
26
26
|
Use this package when you need to:
|
|
27
27
|
- **Bootstrap a fluo application**: Convert your modules into a running HTTP server or microservice.
|
|
28
28
|
- **Orchestrate DI and Lifecycle**: Manage module-graph compilation, provider wiring, and application hooks (`onModuleInit`, `onApplicationBootstrap`).
|
|
29
|
-
- **Create Standalone Contexts**: Run CLI tasks,
|
|
30
|
-
- **Diagnostic Inspection**: Produce machine-readable platform snapshots for CLI export
|
|
29
|
+
- **Create Standalone Contexts**: Run CLI tasks, scripts, or workers that need DI but not an HTTP server.
|
|
30
|
+
- **Diagnostic Inspection**: Produce machine-readable platform snapshots and diagnostic issues for CLI export while leaving graph viewing and Mermaid presentation to Studio.
|
|
31
31
|
|
|
32
32
|
## Quick Start
|
|
33
33
|
|
|
@@ -115,7 +115,7 @@ class DatabaseModule {}
|
|
|
115
115
|
|
|
116
116
|
@Module({
|
|
117
117
|
imports: [DatabaseModule],
|
|
118
|
-
|
|
118
|
+
providers: [UsersService], // UsersService can inject DatabaseService
|
|
119
119
|
})
|
|
120
120
|
class UsersModule {}
|
|
121
121
|
```
|
|
@@ -126,10 +126,14 @@ class UsersModule {}
|
|
|
126
126
|
- On `@fluojs/runtime/node`, Node request body parsing normalizes the primary `content-type` media type before JSON and multipart detection, so mixed-case JSON and multipart headers preserve the documented parser behavior.
|
|
127
127
|
- Node-backed and Web-standard request wrappers snapshot cheap request metadata before body parsing, then materialize `body`/`rawBody` once at the dispatch boundary so userland continues to observe synchronous parsed values.
|
|
128
128
|
- Node-backed cookies/query values and Web-standard headers are snapshotted when the request wrapper is created, then lazily normalized and memoized per request; later upstream object mutations do not change the `FrameworkRequest` view.
|
|
129
|
+
- Node-backed request context IDs prefer `x-request-id` and fall back to `x-correlation-id` when `x-request-id` is absent, so error responses and request-aware integrations keep the upstream correlation identifier.
|
|
129
130
|
- `ApplicationContext.get()` and `Application.get()` memoize only direct root singleton class/factory provider lookups known at bootstrap, while preserving alias, request, transient, post-close, multi-provider, and `container.override()` resolution semantics.
|
|
130
131
|
- `multi: true` provider tokens are not context-cache memoized: each `get()` call delegates to DI so the container can assemble a fresh contribution array while still reusing each contribution according to its own provider scope.
|
|
131
132
|
- When `duplicateProviderPolicy` is `warn` or `ignore`, context-cache eligibility and lifecycle hook execution are based on the effective winning provider selected by bootstrap; stale losing providers do not seed cache entries or lifecycle hooks.
|
|
132
133
|
- If application or context bootstrap fails after runtime resources or lifecycle instances have been created, fluo resets readiness, runs registered runtime cleanup callbacks, invokes shutdown hooks for instances resolved so far with `bootstrap-failed`, disposes the container, logs cleanup failures, and rethrows the original bootstrap error.
|
|
134
|
+
- `Application.listen()` and microservice `listen()` are serialized with shutdown: overlapping startup calls share the same in-flight startup, shutdown waits for in-flight startup to settle, and a startup that races with shutdown cannot transition the shell back to `ready` after close begins.
|
|
135
|
+
- Shutdown signal registration failures are user-observable: `runNodeApplication(...)`, `bootstrapNodeApplication(...)`, and adapter-owned runtime helpers close the already-started application with `bootstrap-failed`, log any close failure separately, and reject with the original registration error.
|
|
136
|
+
- Shutdown signal unregistration failures do not skip application close: `app.close()` always continues through adapter shutdown, lifecycle hooks, runtime cleanup callbacks, and container disposal; if close otherwise succeeds it rejects with the unregistration error, and if close also fails it rejects with an aggregate containing both failures.
|
|
133
137
|
- Connected microservices are owned children of their parent `Application`: `startAllMicroservices()` starts them sequentially and rolls back already-started children with `bootstrap-failed` if a later child fails, while `Application.close(signal)` closes connected children before parent lifecycle hooks, adapter shutdown, and container disposal.
|
|
134
138
|
- `FluoFactory.createMicroservice()` preserves the original bootstrap/runtime-resolution error when cleanup fails and logs cleanup failures separately.
|
|
135
139
|
- Bootstrap resolves independent singleton lifecycle providers concurrently, then runs lifecycle hooks in deterministic provider order.
|
|
@@ -137,29 +141,37 @@ class UsersModule {}
|
|
|
137
141
|
- `createNodeHttpAdapter(...)`, `bootstrapNodeApplication(...)`, and `runNodeApplication(...)` accept `maxBodySize` only as a non-negative integer byte count and fail fast during adapter creation/bootstrap when the value is invalid.
|
|
138
142
|
- Response stream backpressure helpers settle `waitForDrain()` on `drain`, `close`, or `error` so streaming writers do not hang on dead connections.
|
|
139
143
|
- Runtime health modules report `/ready` as `starting` with HTTP 503 until bootstrap marks them ready, and they return to `starting` as soon as application/context shutdown begins, including failed shutdown attempts.
|
|
144
|
+
- Runtime health module readiness checks receive the current `RequestContext`, allowing public integrations to resolve runtime-exposed status providers without importing internal runtime tokens.
|
|
140
145
|
- Signal-driven shutdown helpers preserve bounded drain semantics, log timeout/failure conditions, and set `process.exitCode` when shutdown does not finish cleanly, but they leave final process termination ownership to the surrounding host runtime.
|
|
141
|
-
- Platform snapshot production
|
|
146
|
+
- Platform snapshot and diagnostic issue production stay in runtime; graph viewing, filtering presentation, and Mermaid rendering are Studio-owned contracts consumed by CLI and automation callers.
|
|
142
147
|
- Module graph compile-result caching is opt-in through `moduleGraphCache: true`; it keys entries by root module identity, runtime providers, validation tokens, core metadata versions, and the compile algorithm version, caches only successful compilations, and returns isolated graph copies so caller mutations cannot poison later bootstraps.
|
|
143
148
|
|
|
144
149
|
## Public API Overview
|
|
145
150
|
|
|
146
151
|
- `fluoFactory`: Lower-camel-case alias for the runtime bootstrap facade used in the package examples.
|
|
147
|
-
- `FluoFactory`: Class-based runtime bootstrap facade
|
|
152
|
+
- `FluoFactory`: Class-based runtime bootstrap facade with explicit static access.
|
|
148
153
|
- `Application`: Extends `ApplicationContext` with `listen()`, `dispatch()`, and `state`.
|
|
149
|
-
- `ApplicationContext`: Provides `get<T>(token)`, `close()`, and access to `container` and
|
|
154
|
+
- `ApplicationContext`: Provides `get<T>(token)`, `close()`, and access to `container`, `modules`, and bootstrap diagnostics.
|
|
150
155
|
- `LifecycleHooks`: Convenience union covering `OnModuleInit`, `OnApplicationBootstrap`, `OnModuleDestroy`, and `OnApplicationShutdown`.
|
|
151
|
-
- `
|
|
156
|
+
- `HealthModule.forRoot(options)`: Runtime-owned `/health` and `/ready` module facade whose readiness marker follows bootstrap and shutdown lifecycle transitions.
|
|
157
|
+
- `createHealthModule(options)`: Deprecated compatibility helper for the same runtime health module contract; prefer `HealthModule.forRoot(...)` in application-facing module imports.
|
|
158
|
+
- `ReadinessCheck`: Function type used by runtime health modules. Checks receive the `/ready` request context and return a boolean or promise.
|
|
152
159
|
- `defineModule(cls, metadata)`: Programmatic module definition helper.
|
|
153
160
|
- `bootstrapApplication(options)`: Lower-level async bootstrap function.
|
|
161
|
+
- `bootstrapModule(...)`: Lower-level module graph bootstrap helper.
|
|
162
|
+
- `createBootstrapTimingDiagnostics(...)`, `createRuntimeDiagnosticsGraph(...)`: Runtime-owned diagnostics snapshot helpers for CLI/support tooling. They produce machine-readable data; Studio owns viewer parsing, graph presentation, and Mermaid rendering.
|
|
163
|
+
- `createRequestAbortContext(...)`, `trackActiveRequestTransaction(...)`, `untrackActiveRequestTransaction(...)`: Request abort and active transaction helpers used by runtime-aware integrations.
|
|
154
164
|
|
|
155
165
|
## Platform-Specific Subpaths
|
|
156
166
|
|
|
157
167
|
| Subpath | Purpose |
|
|
158
168
|
| :--- | :--- |
|
|
159
169
|
| `@fluojs/runtime/node` | Supported Node.js entrypoint for logger factories, Node adapter/bootstrap helpers, and shutdown signal registration. |
|
|
160
|
-
| `@fluojs/runtime/web` | Shared Web-standard request/response utilities for Bun, Deno, and Cloudflare Workers
|
|
161
|
-
| `@fluojs/runtime/internal` |
|
|
162
|
-
| `@fluojs/runtime/internal-node` | Node-only internal seam
|
|
170
|
+
| `@fluojs/runtime/web` | Shared Web-standard request/response utilities for Bun, Deno, and Cloudflare Workers, including `createWebRequestResponseFactory`, `dispatchWebRequest`, `createWebFrameworkRequest`, and `parseMultipart`. |
|
|
171
|
+
| `@fluojs/runtime/internal` | Token-only internal seam for package integrations. |
|
|
172
|
+
| `@fluojs/runtime/internal-node` | Node-only internal seam for adapter/runtime plumbing; prefer `@fluojs/runtime/node` in application code. |
|
|
173
|
+
| `@fluojs/runtime/internal/http-adapter` | Internal HTTP adapter seam for platform packages. |
|
|
174
|
+
| `@fluojs/runtime/internal/request-response-factory` | Internal request/response factory seam for platform packages. |
|
|
163
175
|
|
|
164
176
|
### Node-Specific Subpath (`@fluojs/runtime/node`)
|
|
165
177
|
|
|
@@ -182,14 +194,25 @@ const adapter = createNodeHttpAdapter({
|
|
|
182
194
|
});
|
|
183
195
|
```
|
|
184
196
|
|
|
185
|
-
For the public Node runtime surface, `maxBodySize`
|
|
197
|
+
For the public Node runtime surface, `maxBodySize`, `retryDelayMs`, `retryLimit`, and `shutdownTimeoutMs` are number-only non-negative integers. Values such as `'1mb'`, fractional retry counts, or negative shutdown timeouts are rejected immediately during adapter creation instead of being coerced later. Node request context IDs prefer `x-request-id`; when it is absent, `x-correlation-id` is used as the request ID fallback for runtime error responses and request-aware integrations.
|
|
186
198
|
|
|
187
|
-
- `createConsoleApplicationLogger()`: Colorized console logger using `process.stdout`/`process.stderr`.
|
|
199
|
+
- `createConsoleApplicationLogger()`: Colorized console logger using `process.stdout`/`process.stderr`. The default remains the pretty format. Pass `{ mode: 'minimal' }` for concise `[fluo] LEVEL [context] message` lines, `{ mode: 'silent' }` to suppress runtime logger output, `{ level: 'warn' }` or another threshold to filter lower-severity messages, and `{ color: false }` when you need deterministic non-colored output.
|
|
188
200
|
- `createJsonApplicationLogger()`: Structured JSON logger using `process.stdout`/`process.stderr`.
|
|
189
|
-
- `createNodeHttpAdapter()`: Raw Node `http`/`https` adapter factory for adapter-first runtime setup. The helper normalizes the primary Node request `content-type` before JSON/multipart detection and accepts `maxBodySize` only as
|
|
190
|
-
- `bootstrapNodeApplication()` / `runNodeApplication()`: Node-specific bootstrap helpers used by
|
|
201
|
+
- `createNodeHttpAdapter()`: Raw Node `http`/`https` adapter factory for adapter-first runtime setup. The helper normalizes the primary Node request `content-type` before JSON/multipart detection and accepts `maxBodySize`, `retryDelayMs`, `retryLimit`, and `shutdownTimeoutMs` only as non-negative integers.
|
|
202
|
+
- `bootstrapNodeApplication()` / `runNodeApplication()`: Node-specific bootstrap helpers used by direct Node runtime flows.
|
|
191
203
|
- `createNodeShutdownSignalRegistration()`, `defaultNodeShutdownSignals()`, `registerShutdownSignals()`: Shutdown registration helpers for hosts that need explicit signal wiring.
|
|
192
204
|
|
|
205
|
+
Runtime app logging is separate from CLI lifecycle reporting. Configure `ApplicationLogger` when you want to change logs emitted by the application/runtime itself:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { createConsoleApplicationLogger, createJsonApplicationLogger } from '@fluojs/runtime/node';
|
|
209
|
+
|
|
210
|
+
const minimalLogger = createConsoleApplicationLogger({ mode: 'minimal', level: 'warn' });
|
|
211
|
+
const jsonLogger = createJsonApplicationLogger();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Use CLI reporter flags such as `fluo dev --verbose` when you need raw child-process output from the development command instead.
|
|
215
|
+
|
|
193
216
|
Lower-level Node compression internals stay behind the `@fluojs/runtime/internal-node` seam rather than the public `@fluojs/runtime/node` contract.
|
|
194
217
|
|
|
195
218
|
## Related Packages
|
|
@@ -198,7 +221,7 @@ Lower-level Node compression internals stay behind the `@fluojs/runtime/internal
|
|
|
198
221
|
- [@fluojs/di](../di): Dependency injection container implementation.
|
|
199
222
|
- [@fluojs/http](../http): HTTP routing, controllers, and dispatcher.
|
|
200
223
|
- [@fluojs/platform-nodejs](../platform-nodejs): Official Node.js HTTP adapter.
|
|
201
|
-
- [@fluojs/studio](../studio): Viewer and rendering helpers for runtime-produced snapshots.
|
|
224
|
+
- [@fluojs/studio](../studio): Viewer, filtering, and rendering helpers for runtime-produced snapshots and diagnostic issues.
|
|
202
225
|
|
|
203
226
|
## Example Sources
|
|
204
227
|
|
package/dist/bootstrap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EAEX,uBAAuB,EAGvB,2BAA2B,EAC3B,sBAAsB,EACtB,eAAe,EAGf,wBAAwB,EACxB,+BAA+B,EAC/B,yBAAyB,EAEzB,gBAAgB,EAChB,UAAU,EAMX,MAAM,YAAY,CAAC;AA6dpB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,gBAAgB,GAAG,CAAC,CAIjG;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B,GAAG,eAAe,CA+B7G;AAo0BD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,WAAW,CAAC,CAqIrG;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB;;;;;;;OAOG;WACU,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,WAAW,CAAC;IAOzG;;;;;;;OAOG;WACU,wBAAwB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,kBAAkB,CAAC;IA6G9B;;;;;;;;OAQG;WACU,kBAAkB,CAC7B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,uBAAuB,CAAC;CA+BpC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,oBAAc,CAAC"}
|
package/dist/bootstrap.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Container } from '@fluojs/di';
|
|
2
|
-
import { DefaultBinder } from '@fluojs/http/internal';
|
|
3
2
|
import { InvariantError } from '@fluojs/core';
|
|
4
|
-
import { defineModuleMetadata, getClassDiMetadata } from '@fluojs/core/internal';
|
|
5
3
|
import { createDispatcher, createHandlerMapping } from '@fluojs/http';
|
|
6
4
|
import { DuplicateProviderError } from './errors.js';
|
|
7
5
|
import { createBootstrapTimingDiagnostics } from './health/diagnostics.js';
|
|
6
|
+
import { defineRuntimeModuleMetadata, getRuntimeClassDiMetadata } from './internal/core-metadata.js';
|
|
7
|
+
import { RuntimeDefaultBinder } from './internal/http-runtime.js';
|
|
8
8
|
import { createConsoleApplicationLogger } from './logging/logger.js';
|
|
9
9
|
import { compileModuleGraph, providerToken } from './module-graph.js';
|
|
10
10
|
import { createRuntimePlatformShell } from './platform-shell.js';
|
|
11
|
-
import { APPLICATION_LOGGER, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER, PLATFORM_SHELL, RUNTIME_CONTAINER } from './tokens.js';
|
|
11
|
+
import { APPLICATION_LOGGER, BOOTSTRAP_READY_SIGNAL, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER, PLATFORM_SHELL, RUNTIME_CLEANUP_REGISTRATION, RUNTIME_CONTAINER } from './tokens.js';
|
|
12
12
|
const DEFAULT_MICROSERVICE_TOKEN = Symbol.for('fluo.microservices.service');
|
|
13
13
|
const runtimePerformance = globalThis.performance;
|
|
14
14
|
async function runExceptionFilters(filters, error, request, response, requestId) {
|
|
@@ -26,16 +26,16 @@ async function runExceptionFilters(filters, error, request, response, requestId)
|
|
|
26
26
|
}
|
|
27
27
|
function providerScope(provider) {
|
|
28
28
|
if (typeof provider === 'function') {
|
|
29
|
-
return
|
|
29
|
+
return getRuntimeClassDiMetadata(provider)?.scope ?? 'singleton';
|
|
30
30
|
}
|
|
31
31
|
if ('useValue' in provider) {
|
|
32
32
|
return 'singleton';
|
|
33
33
|
}
|
|
34
34
|
if ('useClass' in provider) {
|
|
35
|
-
return provider.scope ??
|
|
35
|
+
return provider.scope ?? getRuntimeClassDiMetadata(provider.useClass)?.scope ?? 'singleton';
|
|
36
36
|
}
|
|
37
37
|
if ('useFactory' in provider) {
|
|
38
|
-
return provider.scope ?? (provider.resolverClass ?
|
|
38
|
+
return provider.scope ?? (provider.resolverClass ? getRuntimeClassDiMetadata(provider.resolverClass)?.scope : undefined) ?? 'singleton';
|
|
39
39
|
}
|
|
40
40
|
return 'singleton';
|
|
41
41
|
}
|
|
@@ -65,6 +65,33 @@ async function runCleanupCallbacks(cleanups) {
|
|
|
65
65
|
}
|
|
66
66
|
return errors;
|
|
67
67
|
}
|
|
68
|
+
function createRuntimeCleanupRegistration(cleanups) {
|
|
69
|
+
return cleanup => {
|
|
70
|
+
cleanups.push(cleanup);
|
|
71
|
+
return () => {
|
|
72
|
+
const index = cleanups.indexOf(cleanup);
|
|
73
|
+
if (index >= 0) {
|
|
74
|
+
cleanups.splice(index, 1);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function createBootstrapReadySignal() {
|
|
80
|
+
let markReady;
|
|
81
|
+
let markFailed;
|
|
82
|
+
const ready = new Promise((resolve, reject) => {
|
|
83
|
+
markReady = resolve;
|
|
84
|
+
markFailed = reject;
|
|
85
|
+
});
|
|
86
|
+
ready.catch(() => undefined);
|
|
87
|
+
return {
|
|
88
|
+
markFailed,
|
|
89
|
+
markReady,
|
|
90
|
+
wait() {
|
|
91
|
+
return ready;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
68
95
|
async function closeRuntimeResources(options) {
|
|
69
96
|
const errors = [];
|
|
70
97
|
resetReadinessState(options.modules);
|
|
@@ -315,7 +342,7 @@ function registerModuleMiddleware(container, modules) {
|
|
|
315
342
|
* @returns The same `moduleType` reference for fluent helper composition.
|
|
316
343
|
*/
|
|
317
344
|
export function defineModule(moduleType, definition) {
|
|
318
|
-
|
|
345
|
+
defineRuntimeModuleMetadata(moduleType, definition);
|
|
319
346
|
return moduleType;
|
|
320
347
|
}
|
|
321
348
|
|
|
@@ -357,6 +384,7 @@ class FluoApplication {
|
|
|
357
384
|
applicationState = 'bootstrapped';
|
|
358
385
|
closed = false;
|
|
359
386
|
closingPromise;
|
|
387
|
+
listenPromise;
|
|
360
388
|
contextResolutionCache = new Map();
|
|
361
389
|
lifecycleInstances;
|
|
362
390
|
connectedMicroservices = [];
|
|
@@ -443,12 +471,27 @@ class FluoApplication {
|
|
|
443
471
|
* 준비 검사를 통과한 뒤 어댑터에 바인딩을 위임하고 상태를 `ready`로 전이한다.
|
|
444
472
|
*/
|
|
445
473
|
async listen() {
|
|
446
|
-
if (this.applicationState === 'closed') {
|
|
474
|
+
if (this.closed || this.closingPromise || this.applicationState === 'closed') {
|
|
447
475
|
throw new InvariantError('Application cannot listen after it has been closed.');
|
|
448
476
|
}
|
|
449
477
|
if (this.applicationState === 'ready') {
|
|
450
478
|
return;
|
|
451
479
|
}
|
|
480
|
+
if (this.listenPromise) {
|
|
481
|
+
await this.listenPromise;
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
this.listenPromise = this.startListening();
|
|
485
|
+
try {
|
|
486
|
+
await this.listenPromise;
|
|
487
|
+
} finally {
|
|
488
|
+
this.listenPromise = undefined;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async startListening() {
|
|
492
|
+
if (this.closed || this.closingPromise || this.applicationState === 'closed') {
|
|
493
|
+
throw new InvariantError('Application cannot listen after it has been closed.');
|
|
494
|
+
}
|
|
452
495
|
if (!this.hasHttpAdapter) {
|
|
453
496
|
throw new InvariantError('Application cannot listen without an HTTP adapter. Provide options.adapter for HTTP startup, or use createApplicationContext() for adapterless DI-only bootstrap.');
|
|
454
497
|
}
|
|
@@ -459,6 +502,9 @@ class FluoApplication {
|
|
|
459
502
|
this.logger.error('Failed to start the HTTP adapter.', error, 'FluoApplication');
|
|
460
503
|
throw error;
|
|
461
504
|
}
|
|
505
|
+
if (this.closed || this.closingPromise) {
|
|
506
|
+
throw new InvariantError('Application startup was interrupted by shutdown.');
|
|
507
|
+
}
|
|
462
508
|
this.applicationState = 'ready';
|
|
463
509
|
this.logger.log('fluo application successfully started.', 'FluoApplication');
|
|
464
510
|
}
|
|
@@ -479,6 +525,11 @@ class FluoApplication {
|
|
|
479
525
|
}
|
|
480
526
|
this.closingPromise = (async () => {
|
|
481
527
|
const errors = [];
|
|
528
|
+
if (this.listenPromise) {
|
|
529
|
+
try {
|
|
530
|
+
await this.listenPromise;
|
|
531
|
+
} catch {}
|
|
532
|
+
}
|
|
482
533
|
try {
|
|
483
534
|
await this.closeConnectedMicroservices(signal);
|
|
484
535
|
} catch (error) {
|
|
@@ -559,6 +610,7 @@ class FluoApplicationContext {
|
|
|
559
610
|
class FluoMicroserviceApplication {
|
|
560
611
|
closed = false;
|
|
561
612
|
closingPromise;
|
|
613
|
+
listenPromise;
|
|
562
614
|
microserviceState = 'bootstrapped';
|
|
563
615
|
constructor(context, logger, runtime, closeContextOnClose) {
|
|
564
616
|
this.context = context;
|
|
@@ -582,13 +634,31 @@ class FluoMicroserviceApplication {
|
|
|
582
634
|
return this.context.get(token);
|
|
583
635
|
}
|
|
584
636
|
async listen() {
|
|
585
|
-
if (this.microserviceState === 'closed') {
|
|
637
|
+
if (this.closed || this.closingPromise || this.microserviceState === 'closed') {
|
|
586
638
|
throw new InvariantError('Microservice cannot listen after it has been closed.');
|
|
587
639
|
}
|
|
588
640
|
if (this.microserviceState === 'ready') {
|
|
589
641
|
return;
|
|
590
642
|
}
|
|
643
|
+
if (this.listenPromise) {
|
|
644
|
+
await this.listenPromise;
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
this.listenPromise = this.startListening();
|
|
648
|
+
try {
|
|
649
|
+
await this.listenPromise;
|
|
650
|
+
} finally {
|
|
651
|
+
this.listenPromise = undefined;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
async startListening() {
|
|
655
|
+
if (this.closed || this.closingPromise || this.microserviceState === 'closed') {
|
|
656
|
+
throw new InvariantError('Microservice cannot listen after it has been closed.');
|
|
657
|
+
}
|
|
591
658
|
await this.runtime.listen();
|
|
659
|
+
if (this.closed || this.closingPromise) {
|
|
660
|
+
throw new InvariantError('Microservice startup was interrupted by shutdown.');
|
|
661
|
+
}
|
|
592
662
|
this.microserviceState = 'ready';
|
|
593
663
|
this.logger.log('fluo microservice successfully started.', 'FluoFactory');
|
|
594
664
|
}
|
|
@@ -613,6 +683,11 @@ class FluoMicroserviceApplication {
|
|
|
613
683
|
return;
|
|
614
684
|
}
|
|
615
685
|
this.closingPromise = (async () => {
|
|
686
|
+
if (this.listenPromise) {
|
|
687
|
+
try {
|
|
688
|
+
await this.listenPromise;
|
|
689
|
+
} catch {}
|
|
690
|
+
}
|
|
616
691
|
if (this.closeContextOnClose) {
|
|
617
692
|
const errors = [];
|
|
618
693
|
try {
|
|
@@ -825,13 +900,19 @@ function createRuntimeProviders(options, logger) {
|
|
|
825
900
|
useValue: logger
|
|
826
901
|
}];
|
|
827
902
|
}
|
|
828
|
-
function registerRuntimeBootstrapTokens(bootstrapped, adapter, platformShell) {
|
|
903
|
+
function registerRuntimeBootstrapTokens(bootstrapped, adapter, platformShell, runtimeCleanup, bootstrapReadySignal) {
|
|
829
904
|
registerRuntimeContextTokens(bootstrapped, {
|
|
830
905
|
provide: HTTP_APPLICATION_ADAPTER,
|
|
831
906
|
useValue: adapter
|
|
832
907
|
}, {
|
|
833
908
|
provide: PLATFORM_SHELL,
|
|
834
909
|
useValue: platformShell
|
|
910
|
+
}, {
|
|
911
|
+
provide: RUNTIME_CLEANUP_REGISTRATION,
|
|
912
|
+
useValue: createRuntimeCleanupRegistration(runtimeCleanup)
|
|
913
|
+
}, {
|
|
914
|
+
provide: BOOTSTRAP_READY_SIGNAL,
|
|
915
|
+
useValue: bootstrapReadySignal
|
|
835
916
|
});
|
|
836
917
|
}
|
|
837
918
|
function registerRuntimeContextTokens(bootstrapped, ...providers) {
|
|
@@ -843,21 +924,28 @@ function registerRuntimeContextTokens(bootstrapped, ...providers) {
|
|
|
843
924
|
useValue: bootstrapped.modules
|
|
844
925
|
});
|
|
845
926
|
}
|
|
846
|
-
function registerRuntimeApplicationContextTokens(bootstrapped, platformShell) {
|
|
927
|
+
function registerRuntimeApplicationContextTokens(bootstrapped, platformShell, runtimeCleanup, bootstrapReadySignal) {
|
|
847
928
|
registerRuntimeContextTokens(bootstrapped, {
|
|
848
929
|
provide: PLATFORM_SHELL,
|
|
849
930
|
useValue: platformShell
|
|
931
|
+
}, {
|
|
932
|
+
provide: RUNTIME_CLEANUP_REGISTRATION,
|
|
933
|
+
useValue: createRuntimeCleanupRegistration(runtimeCleanup)
|
|
934
|
+
}, {
|
|
935
|
+
provide: BOOTSTRAP_READY_SIGNAL,
|
|
936
|
+
useValue: bootstrapReadySignal
|
|
850
937
|
});
|
|
851
938
|
}
|
|
852
939
|
async function resolveBootstrapLifecycleInstances(bootstrapped, resolvedInstances) {
|
|
853
940
|
const lifecycleProviders = [...bootstrapped.effectiveProviders.runtimeProviders, ...bootstrapped.effectiveProviders.moduleProviders];
|
|
854
941
|
return resolveLifecycleInstances(bootstrapped.container, lifecycleProviders, resolvedInstances);
|
|
855
942
|
}
|
|
856
|
-
async function runBootstrapLifecycle(modules, lifecycleInstances, logger, platformShell) {
|
|
943
|
+
async function runBootstrapLifecycle(modules, lifecycleInstances, logger, platformShell, bootstrapReadySignal) {
|
|
857
944
|
resetReadinessState(modules);
|
|
858
945
|
await runBootstrapHooks(lifecycleInstances);
|
|
859
946
|
await platformShell.start();
|
|
860
947
|
markReadinessState(modules);
|
|
948
|
+
bootstrapReadySignal.markReady();
|
|
861
949
|
logCompiledModules(logger, modules);
|
|
862
950
|
}
|
|
863
951
|
function createFilterErrorHandler(filters) {
|
|
@@ -880,7 +968,7 @@ function createRuntimeDispatcherOptions(bootstrapped, options, handlerMapping, e
|
|
|
880
968
|
rootContainer: bootstrapped.container
|
|
881
969
|
};
|
|
882
970
|
if (converters.length > 0) {
|
|
883
|
-
dispatcherOptions.binder = new
|
|
971
|
+
dispatcherOptions.binder = new RuntimeDefaultBinder(converters);
|
|
884
972
|
}
|
|
885
973
|
if (errorHandler) {
|
|
886
974
|
dispatcherOptions.onError = errorHandler;
|
|
@@ -916,6 +1004,7 @@ export async function bootstrapApplication(options) {
|
|
|
916
1004
|
async listen() {}
|
|
917
1005
|
};
|
|
918
1006
|
const runtimeCleanup = [];
|
|
1007
|
+
const bootstrapReadySignal = createBootstrapReadySignal();
|
|
919
1008
|
const platformShell = createRuntimePlatformShell(options.platform?.components);
|
|
920
1009
|
const timingEnabled = options.diagnostics?.timing === true;
|
|
921
1010
|
const timingStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
@@ -929,7 +1018,7 @@ export async function bootstrapApplication(options) {
|
|
|
929
1018
|
logger,
|
|
930
1019
|
moduleGraphCache: options.moduleGraphCache,
|
|
931
1020
|
providers: runtimeProviders,
|
|
932
|
-
validationTokens: [RUNTIME_CONTAINER, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER]
|
|
1021
|
+
validationTokens: [RUNTIME_CONTAINER, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER, RUNTIME_CLEANUP_REGISTRATION, BOOTSTRAP_READY_SIGNAL]
|
|
933
1022
|
});
|
|
934
1023
|
if (timingEnabled) {
|
|
935
1024
|
timingPhases.push({
|
|
@@ -938,7 +1027,7 @@ export async function bootstrapApplication(options) {
|
|
|
938
1027
|
});
|
|
939
1028
|
}
|
|
940
1029
|
const registerTokensStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
941
|
-
registerRuntimeBootstrapTokens(bootstrapped, adapter, platformShell);
|
|
1030
|
+
registerRuntimeBootstrapTokens(bootstrapped, adapter, platformShell, runtimeCleanup, bootstrapReadySignal);
|
|
942
1031
|
if (timingEnabled) {
|
|
943
1032
|
timingPhases.push({
|
|
944
1033
|
durationMs: runtimePerformance.now() - registerTokensStart,
|
|
@@ -961,7 +1050,7 @@ export async function bootstrapApplication(options) {
|
|
|
961
1050
|
});
|
|
962
1051
|
}
|
|
963
1052
|
const lifecycleStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
964
|
-
await runBootstrapLifecycle(bootstrapped.modules, lifecycleInstances, logger, platformShell);
|
|
1053
|
+
await runBootstrapLifecycle(bootstrapped.modules, lifecycleInstances, logger, platformShell, bootstrapReadySignal);
|
|
965
1054
|
if (timingEnabled) {
|
|
966
1055
|
timingPhases.push({
|
|
967
1056
|
durationMs: runtimePerformance.now() - lifecycleStart,
|
|
@@ -977,8 +1066,9 @@ export async function bootstrapApplication(options) {
|
|
|
977
1066
|
});
|
|
978
1067
|
}
|
|
979
1068
|
const bootstrapTiming = timingEnabled ? createBootstrapTimingDiagnostics(timingPhases, runtimePerformance.now() - timingStart) : undefined;
|
|
980
|
-
return new FluoApplication(bootstrapped.container, bootstrapped.modules, options.rootModule, dispatcher, bootstrapTiming, adapter, hasHttpAdapter, platformShell, lifecycleInstances, logger, runtimeCleanup, createContextCacheableTokenSet(bootstrapped.effectiveProviders, [RUNTIME_CONTAINER, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER, PLATFORM_SHELL]));
|
|
1069
|
+
return new FluoApplication(bootstrapped.container, bootstrapped.modules, options.rootModule, dispatcher, bootstrapTiming, adapter, hasHttpAdapter, platformShell, lifecycleInstances, logger, runtimeCleanup, createContextCacheableTokenSet(bootstrapped.effectiveProviders, [RUNTIME_CONTAINER, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER, PLATFORM_SHELL, RUNTIME_CLEANUP_REGISTRATION, BOOTSTRAP_READY_SIGNAL]));
|
|
981
1070
|
} catch (error) {
|
|
1071
|
+
bootstrapReadySignal.markFailed(error);
|
|
982
1072
|
logger.error('Failed to bootstrap the fluo application. Check the error below for what failed and how to fix it.', error, 'FluoFactory');
|
|
983
1073
|
await runBootstrapFailureCleanup({
|
|
984
1074
|
container: bootstrappedContainer,
|
|
@@ -1025,6 +1115,7 @@ export class FluoFactory {
|
|
|
1025
1115
|
let bootstrappedContainer;
|
|
1026
1116
|
let bootstrappedModules = [];
|
|
1027
1117
|
const runtimeCleanup = [];
|
|
1118
|
+
const bootstrapReadySignal = createBootstrapReadySignal();
|
|
1028
1119
|
const platformShell = createRuntimePlatformShell(options.platform?.components);
|
|
1029
1120
|
const timingEnabled = options.diagnostics?.timing === true;
|
|
1030
1121
|
const timingStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
@@ -1038,7 +1129,7 @@ export class FluoFactory {
|
|
|
1038
1129
|
logger,
|
|
1039
1130
|
moduleGraphCache: options.moduleGraphCache,
|
|
1040
1131
|
providers: runtimeProviders,
|
|
1041
|
-
validationTokens: [RUNTIME_CONTAINER, COMPILED_MODULES]
|
|
1132
|
+
validationTokens: [RUNTIME_CONTAINER, COMPILED_MODULES, RUNTIME_CLEANUP_REGISTRATION, BOOTSTRAP_READY_SIGNAL]
|
|
1042
1133
|
});
|
|
1043
1134
|
if (timingEnabled) {
|
|
1044
1135
|
timingPhases.push({
|
|
@@ -1047,7 +1138,7 @@ export class FluoFactory {
|
|
|
1047
1138
|
});
|
|
1048
1139
|
}
|
|
1049
1140
|
const registerTokensStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
1050
|
-
registerRuntimeApplicationContextTokens(bootstrapped, platformShell);
|
|
1141
|
+
registerRuntimeApplicationContextTokens(bootstrapped, platformShell, runtimeCleanup, bootstrapReadySignal);
|
|
1051
1142
|
if (timingEnabled) {
|
|
1052
1143
|
timingPhases.push({
|
|
1053
1144
|
durationMs: runtimePerformance.now() - registerTokensStart,
|
|
@@ -1070,7 +1161,7 @@ export class FluoFactory {
|
|
|
1070
1161
|
});
|
|
1071
1162
|
}
|
|
1072
1163
|
const lifecycleStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
1073
|
-
await runBootstrapLifecycle(bootstrapped.modules, lifecycleInstances, logger, platformShell);
|
|
1164
|
+
await runBootstrapLifecycle(bootstrapped.modules, lifecycleInstances, logger, platformShell, bootstrapReadySignal);
|
|
1074
1165
|
if (timingEnabled) {
|
|
1075
1166
|
timingPhases.push({
|
|
1076
1167
|
durationMs: runtimePerformance.now() - lifecycleStart,
|
|
@@ -1078,8 +1169,9 @@ export class FluoFactory {
|
|
|
1078
1169
|
});
|
|
1079
1170
|
}
|
|
1080
1171
|
const bootstrapTiming = timingEnabled ? createBootstrapTimingDiagnostics(timingPhases, runtimePerformance.now() - timingStart) : undefined;
|
|
1081
|
-
return new FluoApplicationContext(bootstrapped.container, bootstrapped.modules, rootModule, bootstrapTiming, lifecycleInstances, runtimeCleanup, createContextCacheableTokenSet(bootstrapped.effectiveProviders, [RUNTIME_CONTAINER, COMPILED_MODULES, PLATFORM_SHELL]));
|
|
1172
|
+
return new FluoApplicationContext(bootstrapped.container, bootstrapped.modules, rootModule, bootstrapTiming, lifecycleInstances, runtimeCleanup, createContextCacheableTokenSet(bootstrapped.effectiveProviders, [RUNTIME_CONTAINER, COMPILED_MODULES, PLATFORM_SHELL, RUNTIME_CLEANUP_REGISTRATION, BOOTSTRAP_READY_SIGNAL]));
|
|
1082
1173
|
} catch (error) {
|
|
1174
|
+
bootstrapReadySignal.markFailed(error);
|
|
1083
1175
|
logger.error('Failed to bootstrap application context. Check the error below for what failed and how to fix it.', error, 'FluoFactory');
|
|
1084
1176
|
await runBootstrapFailureCleanup({
|
|
1085
1177
|
container: bootstrappedContainer,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/health/diagnostics.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/health/diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAY,KAAK,EAAE,MAAM,YAAY,CAAC;AAGlD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,aAAa,EAAE,+BAA+B,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,0BAA0B,EAAE,CAAC;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,UAAU,CAAC;IACjD,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C,aAAa,EAAE,KAAK,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC,CAAC;IACH,aAAa,EAAE,KAAK,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,eAAe,EAAE,KAAK,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,0BAA0B,CAAC,MAAM,CAAC,CAAC;QACjD,KAAK,EAAE,KAAK,CAAC;QACb,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC,CAAC;IACH,iBAAiB,EAAE,KAAK,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EACA,kBAAkB,GAClB,yBAAyB,GACzB,6BAA6B,GAC7B,yBAAyB,GACzB,mBAAmB,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,CAAC,CAAC;CACZ;AAiFD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,UAAU,EAAE,UAAU,GAAG,uBAAuB,CAkEjI;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM,CAoCtF;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,oBAAoB,EAAE,EAC9B,OAAO,EAAE,MAAM,GACd,0BAA0B,CAS5B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getRuntimeClassDiMetadata } from '../internal/core-metadata.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Describes the runtime diagnostics graph contract.
|
|
@@ -56,16 +56,16 @@ function providerShape(provider) {
|
|
|
56
56
|
}
|
|
57
57
|
function providerScope(provider) {
|
|
58
58
|
if (typeof provider === 'function') {
|
|
59
|
-
return
|
|
59
|
+
return getRuntimeClassDiMetadata(provider)?.scope ?? 'singleton';
|
|
60
60
|
}
|
|
61
61
|
if ('useValue' in provider || 'useExisting' in provider) {
|
|
62
62
|
return 'singleton';
|
|
63
63
|
}
|
|
64
64
|
if ('useFactory' in provider) {
|
|
65
|
-
return provider.scope ?? (provider.resolverClass ?
|
|
65
|
+
return provider.scope ?? (provider.resolverClass ? getRuntimeClassDiMetadata(provider.resolverClass)?.scope : undefined) ?? 'singleton';
|
|
66
66
|
}
|
|
67
67
|
if ('useClass' in provider) {
|
|
68
|
-
return provider.scope ??
|
|
68
|
+
return provider.scope ?? getRuntimeClassDiMetadata(provider.useClass)?.scope ?? 'singleton';
|
|
69
69
|
}
|
|
70
70
|
return 'singleton';
|
|
71
71
|
}
|