@fluojs/runtime 1.0.0-beta.1 → 1.0.0-beta.3
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 +12 -1
- package/README.md +12 -1
- package/dist/adapters/request-response-factory.d.ts +9 -0
- package/dist/adapters/request-response-factory.d.ts.map +1 -1
- package/dist/adapters/request-response-factory.js +11 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +230 -46
- package/dist/module-graph.d.ts.map +1 -1
- package/dist/module-graph.js +17 -7
- package/dist/node/internal-node-request.d.ts +21 -0
- package/dist/node/internal-node-request.d.ts.map +1 -1
- package/dist/node/internal-node-request.js +84 -30
- package/dist/node/internal-node.d.ts.map +1 -1
- package/dist/node/internal-node.js +5 -2
- package/dist/node/node-request.d.ts +1 -1
- package/dist/node/node-request.d.ts.map +1 -1
- package/dist/node/node-request.js +1 -1
- package/dist/platform-shell.d.ts +4 -0
- package/dist/platform-shell.d.ts.map +1 -1
- package/dist/platform-shell.js +72 -20
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web.d.ts.map +1 -1
- package/dist/web.js +90 -29
- package/package.json +6 -6
package/README.ko.md
CHANGED
|
@@ -27,7 +27,7 @@ npm install @fluojs/runtime
|
|
|
27
27
|
- **fluo 애플리케이션 부트스트랩**: 모듈을 실행 중인 HTTP 서버나 마이크로서비스로 변환할 때.
|
|
28
28
|
- **DI 및 라이프사이클 오케스트레이션**: 모듈 그래프 컴파일, 프로바이더 연결 및 애플리케이션 훅(`onModuleInit`, `onApplicationBootstrap`)을 관리할 때.
|
|
29
29
|
- **독립형 컨텍스트 생성**: HTTP 서버는 필요 없지만 DI가 필요한 CLI 태스크, 마이그레이션 또는 워커를 실행할 때.
|
|
30
|
-
- **진단 및 검사**:
|
|
30
|
+
- **진단 및 검사**: CLI 내보내기와 Studio 소유 그래프 보기/렌더링을 위한 기계 읽기 가능한 플랫폼 snapshot을 생산할 때.
|
|
31
31
|
|
|
32
32
|
## 퀵 스타트
|
|
33
33
|
|
|
@@ -123,9 +123,18 @@ class UsersModule {}
|
|
|
123
123
|
## 동작 계약
|
|
124
124
|
|
|
125
125
|
- 요청 바디 파싱은 Web 표준 요청과 Node 기반 요청 모두에서 바이트가 스트리밍되는 동안 `maxBodySize`를 강제합니다.
|
|
126
|
+
- Node 기반 및 Web 표준 요청 wrapper는 바디 파싱 전에 저비용 요청 metadata를 snapshot으로 고정한 뒤 dispatch 경계에서 `body`/`rawBody`를 한 번 materialize하므로 userland는 계속 동기 parsed 값을 관찰합니다.
|
|
127
|
+
- Node 기반 쿠키/쿼리 값과 Web 표준 헤더는 요청 wrapper가 생성되는 시점에 snapshot으로 고정된 뒤 요청별로 lazy하게 normalize되고 memoize됩니다. 이후 upstream 객체가 변경되어도 `FrameworkRequest` view는 바뀌지 않습니다.
|
|
128
|
+
- `ApplicationContext.get()`과 `Application.get()`은 bootstrap 시점에 알려진 직접 root singleton class/factory provider 조회만 memoize하며, alias, request, transient, 종료 이후, multi-provider, `container.override()` 해석 의미는 그대로 유지합니다.
|
|
129
|
+
- `multi: true` provider token은 context cache에 memoize되지 않습니다. 각 `get()` 호출은 DI로 위임되어 컨테이너가 새로운 contribution 배열을 조립하며, 각 contribution 자체는 해당 provider scope에 따라 재사용됩니다.
|
|
130
|
+
- `duplicateProviderPolicy`가 `warn` 또는 `ignore`일 때 context cache 적격성과 lifecycle hook 실행은 bootstrap이 선택한 effective winning provider를 기준으로 결정됩니다. stale losing provider는 cache entry나 lifecycle hook을 만들지 않습니다.
|
|
131
|
+
- 애플리케이션 또는 컨텍스트 bootstrap이 런타임 리소스나 lifecycle instance 생성 이후 실패하면 fluo는 readiness를 초기화하고, 등록된 runtime cleanup callback을 실행하며, 그 시점까지 해석된 instance의 shutdown hook을 `bootstrap-failed`로 호출하고, 컨테이너를 dispose하고, cleanup 실패를 로그로 남긴 뒤 원래 bootstrap error를 다시 던집니다.
|
|
132
|
+
- Bootstrap은 독립적인 singleton lifecycle provider를 병렬로 해석한 뒤 lifecycle hook은 결정적인 provider 순서대로 실행합니다.
|
|
126
133
|
- 멀티파트 파싱은 누적 바디 크기가 설정된 `multipart.maxTotalSize`를 넘으면 즉시 거부되며, 런타임 어댑터는 별도 재정의가 없으면 이 한도를 `maxBodySize`와 동일하게 맞춥니다.
|
|
127
134
|
- 응답 스트림 백프레셔 헬퍼는 `drain`, `close`, `error` 중 어느 경우에도 `waitForDrain()`을 완료시켜 끊어진 연결에서 스트리밍 작성기가 멈추지 않도록 합니다.
|
|
135
|
+
- 런타임 health 모듈은 bootstrap이 ready로 표시하기 전까지 `/ready`를 HTTP 503과 `starting`으로 보고하며, 애플리케이션/컨텍스트 종료가 시작되는 즉시, 종료 시도가 실패하더라도 다시 `starting`으로 내려갑니다.
|
|
128
136
|
- 시그널 기반 종료 헬퍼는 bounded drain semantics를 유지하면서 timeout/실패 상황을 로그와 `process.exitCode`로 보고하지만, 최종 프로세스 종료 소유권은 주변 호스트 런타임에 남겨 둡니다.
|
|
137
|
+
- 플랫폼 snapshot 생산은 런타임에 남아 있고, 그래프 보기와 Mermaid 렌더링은 CLI 및 자동화 호출자가 소비하는 Studio 소유 계약입니다.
|
|
129
138
|
|
|
130
139
|
## 공개 API 개요
|
|
131
140
|
|
|
@@ -134,6 +143,7 @@ class UsersModule {}
|
|
|
134
143
|
- `Application`: `ApplicationContext`를 확장하며 `listen()`, `dispatch()`, `state`를 포함합니다.
|
|
135
144
|
- `ApplicationContext`: `get<T>(token)`, `close()` 기능을 제공하며 `container`와 `modules`에 접근할 수 있습니다.
|
|
136
145
|
- `LifecycleHooks`: `OnModuleInit`, `OnApplicationBootstrap`, `OnModuleDestroy`, `OnApplicationShutdown`를 묶는 편의 union 타입입니다.
|
|
146
|
+
- `createHealthModule(options)`: bootstrap 및 shutdown 라이프사이클 전이에 맞춰 readiness marker를 관리하는 런타임 소유 `/health`, `/ready` 모듈 팩토리입니다.
|
|
137
147
|
- `defineModule(cls, metadata)`: 프로그래밍 방식의 모듈 정의 헬퍼입니다.
|
|
138
148
|
- `bootstrapApplication(options)`: 저수준 비동기 부트스트랩 함수입니다.
|
|
139
149
|
|
|
@@ -174,6 +184,7 @@ import {
|
|
|
174
184
|
- [@fluojs/di](../di): 의존성 주입(DI) 컨테이너 구현체.
|
|
175
185
|
- [@fluojs/http](../http): HTTP 라우팅, 컨트롤러 및 디스패처.
|
|
176
186
|
- [@fluojs/platform-nodejs](../platform-nodejs): 공식 Node.js HTTP 어댑터.
|
|
187
|
+
- [@fluojs/studio](../studio): 런타임이 생산한 snapshot을 위한 뷰어 및 렌더링 헬퍼.
|
|
177
188
|
|
|
178
189
|
## 예제 소스
|
|
179
190
|
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ 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
29
|
- **Create Standalone Contexts**: Run CLI tasks, migrations, or workers that need DI but not an HTTP server.
|
|
30
|
-
- **Diagnostic Inspection**:
|
|
30
|
+
- **Diagnostic Inspection**: Produce machine-readable platform snapshots for CLI export and Studio-owned graph viewing/rendering.
|
|
31
31
|
|
|
32
32
|
## Quick Start
|
|
33
33
|
|
|
@@ -123,9 +123,18 @@ class UsersModule {}
|
|
|
123
123
|
## Behavioral Contracts
|
|
124
124
|
|
|
125
125
|
- Request body parsing enforces `maxBodySize` while bytes are still streaming for both Web-standard and Node-backed requests.
|
|
126
|
+
- 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.
|
|
127
|
+
- 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.
|
|
128
|
+
- `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.
|
|
129
|
+
- `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.
|
|
130
|
+
- 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.
|
|
131
|
+
- 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.
|
|
132
|
+
- Bootstrap resolves independent singleton lifecycle providers concurrently, then runs lifecycle hooks in deterministic provider order.
|
|
126
133
|
- Multipart parsing rejects payloads when the cumulative body size exceeds the configured `multipart.maxTotalSize`; runtime adapters default that limit to `maxBodySize` unless you override it.
|
|
127
134
|
- Response stream backpressure helpers settle `waitForDrain()` on `drain`, `close`, or `error` so streaming writers do not hang on dead connections.
|
|
135
|
+
- 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.
|
|
128
136
|
- 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.
|
|
137
|
+
- Platform snapshot production stays in runtime; graph viewing and Mermaid rendering are Studio-owned contracts consumed by CLI and automation callers.
|
|
129
138
|
|
|
130
139
|
## Public API Overview
|
|
131
140
|
|
|
@@ -134,6 +143,7 @@ class UsersModule {}
|
|
|
134
143
|
- `Application`: Extends `ApplicationContext` with `listen()`, `dispatch()`, and `state`.
|
|
135
144
|
- `ApplicationContext`: Provides `get<T>(token)`, `close()`, and access to `container` and `modules`.
|
|
136
145
|
- `LifecycleHooks`: Convenience union covering `OnModuleInit`, `OnApplicationBootstrap`, `OnModuleDestroy`, and `OnApplicationShutdown`.
|
|
146
|
+
- `createHealthModule(options)`: Runtime-owned `/health` and `/ready` module factory whose readiness marker follows bootstrap and shutdown lifecycle transitions.
|
|
137
147
|
- `defineModule(cls, metadata)`: Programmatic module definition helper.
|
|
138
148
|
- `bootstrapApplication(options)`: Lower-level async bootstrap function.
|
|
139
149
|
|
|
@@ -174,6 +184,7 @@ Lower-level Node compression internals stay behind the `@fluojs/runtime/internal
|
|
|
174
184
|
- [@fluojs/di](../di): Dependency injection container implementation.
|
|
175
185
|
- [@fluojs/http](../http): HTTP routing, controllers, and dispatcher.
|
|
176
186
|
- [@fluojs/platform-nodejs](../platform-nodejs): Official Node.js HTTP adapter.
|
|
187
|
+
- [@fluojs/studio](../studio): Viewer and rendering helpers for runtime-produced snapshots.
|
|
177
188
|
|
|
178
189
|
## Example Sources
|
|
179
190
|
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Dispatcher, FrameworkRequest, FrameworkResponse } from '@fluojs/http';
|
|
2
|
+
/** Request/response factory seam used by shared HTTP adapter dispatch helpers. */
|
|
2
3
|
export interface RequestResponseFactory<RawRequest, RawResponse, Response extends FrameworkResponse = FrameworkResponse> {
|
|
3
4
|
createRequest(rawRequest: RawRequest, signal: AbortSignal): Promise<FrameworkRequest>;
|
|
4
5
|
createRequestSignal(rawResponse: RawResponse): AbortSignal;
|
|
5
6
|
createResponse(rawResponse: RawResponse, rawRequest: RawRequest): Response;
|
|
7
|
+
materializeRequest?(request: FrameworkRequest): Promise<void>;
|
|
6
8
|
resolveRequestId(rawRequest: RawRequest): string | undefined;
|
|
7
9
|
writeErrorResponse(error: unknown, response: Response, requestId?: string): Promise<void>;
|
|
8
10
|
}
|
|
11
|
+
/** Options for dispatching one raw platform request through a request/response factory. */
|
|
9
12
|
export interface DispatchWithRequestResponseFactoryOptions<RawRequest, RawResponse, Response extends FrameworkResponse = FrameworkResponse> {
|
|
10
13
|
dispatcher?: Dispatcher;
|
|
11
14
|
dispatcherNotReadyMessage: string;
|
|
@@ -13,5 +16,11 @@ export interface DispatchWithRequestResponseFactoryOptions<RawRequest, RawRespon
|
|
|
13
16
|
rawRequest: RawRequest;
|
|
14
17
|
rawResponse: RawResponse;
|
|
15
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Dispatches one raw platform request through the shared request/response factory lifecycle.
|
|
21
|
+
*
|
|
22
|
+
* @param options - Factory, dispatcher, and raw platform request/response values for one dispatch.
|
|
23
|
+
* @returns The framework response after dispatch, error serialization, or default finalization.
|
|
24
|
+
*/
|
|
16
25
|
export declare function dispatchWithRequestResponseFactory<RawRequest, RawResponse, Response extends FrameworkResponse = FrameworkResponse>({ dispatcher, dispatcherNotReadyMessage, factory, rawRequest, rawResponse, }: DispatchWithRequestResponseFactoryOptions<RawRequest, RawResponse, Response>): Promise<Response>;
|
|
17
26
|
//# sourceMappingURL=request-response-factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-response-factory.d.ts","sourceRoot":"","sources":["../../src/adapters/request-response-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEpF,MAAM,WAAW,sBAAsB,CACrC,UAAU,EACV,WAAW,EACX,QAAQ,SAAS,iBAAiB,GAAG,iBAAiB;IAEtD,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtF,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,WAAW,CAAC;IAC3D,cAAc,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC3E,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3F;AAED,MAAM,WAAW,yCAAyC,CACxD,UAAU,EACV,WAAW,EACX,QAAQ,SAAS,iBAAiB,GAAG,iBAAiB;IAEtD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,yBAAyB,EAAE,MAAM,CAAC;IAClC,OAAO,EAAE,sBAAsB,CAAC,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACnE,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAsB,kCAAkC,CACtD,UAAU,EACV,WAAW,EACX,QAAQ,SAAS,iBAAiB,GAAG,iBAAiB,EACtD,EACA,UAAU,EACV,yBAAyB,EACzB,OAAO,EACP,UAAU,EACV,WAAW,GACZ,EAAE,yCAAyC,CAAC,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"request-response-factory.d.ts","sourceRoot":"","sources":["../../src/adapters/request-response-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEpF,kFAAkF;AAClF,MAAM,WAAW,sBAAsB,CACrC,UAAU,EACV,WAAW,EACX,QAAQ,SAAS,iBAAiB,GAAG,iBAAiB;IAEtD,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtF,mBAAmB,CAAC,WAAW,EAAE,WAAW,GAAG,WAAW,CAAC;IAC3D,cAAc,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC3E,kBAAkB,CAAC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3F;AAED,2FAA2F;AAC3F,MAAM,WAAW,yCAAyC,CACxD,UAAU,EACV,WAAW,EACX,QAAQ,SAAS,iBAAiB,GAAG,iBAAiB;IAEtD,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,yBAAyB,EAAE,MAAM,CAAC;IAClC,OAAO,EAAE,sBAAsB,CAAC,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACnE,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAsB,kCAAkC,CACtD,UAAU,EACV,WAAW,EACX,QAAQ,SAAS,iBAAiB,GAAG,iBAAiB,EACtD,EACA,UAAU,EACV,yBAAyB,EACzB,OAAO,EACP,UAAU,EACV,WAAW,GACZ,EAAE,yCAAyC,CAAC,UAAU,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2BlG"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/** Request/response factory seam used by shared HTTP adapter dispatch helpers. */
|
|
2
|
+
|
|
3
|
+
/** Options for dispatching one raw platform request through a request/response factory. */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Dispatches one raw platform request through the shared request/response factory lifecycle.
|
|
7
|
+
*
|
|
8
|
+
* @param options - Factory, dispatcher, and raw platform request/response values for one dispatch.
|
|
9
|
+
* @returns The framework response after dispatch, error serialization, or default finalization.
|
|
10
|
+
*/
|
|
1
11
|
export async function dispatchWithRequestResponseFactory({
|
|
2
12
|
dispatcher,
|
|
3
13
|
dispatcherNotReadyMessage,
|
|
@@ -9,6 +19,7 @@ export async function dispatchWithRequestResponseFactory({
|
|
|
9
19
|
const signal = factory.createRequestSignal(rawResponse);
|
|
10
20
|
try {
|
|
11
21
|
const frameworkRequest = await factory.createRequest(rawRequest, signal);
|
|
22
|
+
await factory.materializeRequest?.(frameworkRequest);
|
|
12
23
|
if (!dispatcher) {
|
|
13
24
|
throw new Error(dispatcherNotReadyMessage);
|
|
14
25
|
}
|
package/dist/bootstrap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EAEX,uBAAuB,EAGvB,2BAA2B,EAC3B,sBAAsB,EACtB,eAAe,
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAoBA,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,EAKX,MAAM,YAAY,CAAC;AA2apB;;;;;;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;AA4pBD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,WAAW,CAAC,CAqHrG;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;IAoG9B;;;;;;;;OAQG;WACU,kBAAkB,CAC7B,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,uBAAuB,CAAC;CAuBpC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,oBAAc,CAAC"}
|
package/dist/bootstrap.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createDispatcher, createHandlerMapping } from '@fluojs/http';
|
|
|
6
6
|
import { DuplicateProviderError } from './errors.js';
|
|
7
7
|
import { createBootstrapTimingDiagnostics } from './health/diagnostics.js';
|
|
8
8
|
import { createConsoleApplicationLogger } from './logging/logger.js';
|
|
9
|
-
import { compileModuleGraph,
|
|
9
|
+
import { compileModuleGraph, providerToken } from './module-graph.js';
|
|
10
10
|
import { createRuntimePlatformShell } from './platform-shell.js';
|
|
11
11
|
import { APPLICATION_LOGGER, COMPILED_MODULES, HTTP_APPLICATION_ADAPTER, PLATFORM_SHELL, RUNTIME_CONTAINER } from './tokens.js';
|
|
12
12
|
const DEFAULT_MICROSERVICE_TOKEN = Symbol.for('fluo.microservices.service');
|
|
@@ -67,6 +67,7 @@ async function runCleanupCallbacks(cleanups) {
|
|
|
67
67
|
}
|
|
68
68
|
async function closeRuntimeResources(options) {
|
|
69
69
|
const errors = [];
|
|
70
|
+
resetReadinessState(options.modules);
|
|
70
71
|
errors.push(...(await runCleanupCallbacks(options.runtimeCleanup)));
|
|
71
72
|
try {
|
|
72
73
|
await runShutdownHooks(options.lifecycleInstances, options.signal);
|
|
@@ -91,6 +92,7 @@ async function closeRuntimeResources(options) {
|
|
|
91
92
|
}
|
|
92
93
|
async function runBootstrapFailureCleanup(options) {
|
|
93
94
|
const errors = [];
|
|
95
|
+
resetReadinessState(options.modules);
|
|
94
96
|
errors.push(...(await runCleanupCallbacks(options.runtimeCleanup)));
|
|
95
97
|
if (options.lifecycleInstances.length > 0) {
|
|
96
98
|
try {
|
|
@@ -156,44 +158,115 @@ function createDuplicateProviderMessage(token, moduleName, existingModuleName) {
|
|
|
156
158
|
const tokenLabel = typeof token === 'function' ? token.name || '<anonymous>' : String(token);
|
|
157
159
|
return `Duplicate provider token "${tokenLabel}" registered in module "${moduleName}". Previously registered in module "${existingModuleName}".`;
|
|
158
160
|
}
|
|
159
|
-
function
|
|
160
|
-
const
|
|
161
|
+
function createDuplicateRuntimeProviderMessage(token) {
|
|
162
|
+
const tokenLabel = typeof token === 'function' ? token.name || '<anonymous>' : String(token);
|
|
163
|
+
return `Duplicate runtime provider token "${tokenLabel}" registered.`;
|
|
164
|
+
}
|
|
165
|
+
function handleDuplicateProvider(token, moduleName, existingModuleName, policy, logger) {
|
|
166
|
+
const message = createDuplicateProviderMessage(token, moduleName, existingModuleName);
|
|
167
|
+
if (policy === 'throw') {
|
|
168
|
+
throw new DuplicateProviderError(message, {
|
|
169
|
+
module: moduleName,
|
|
170
|
+
token,
|
|
171
|
+
phase: 'provider registration',
|
|
172
|
+
hint: `Remove the duplicate registration from one of the modules, use container.override() for intentional replacements, or set duplicateProviderPolicy to 'warn' or 'ignore'.`
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (policy === 'warn') {
|
|
176
|
+
logger?.warn(message, 'BootstrapModule');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function handleDuplicateRuntimeProvider(token, policy, logger) {
|
|
180
|
+
const message = createDuplicateRuntimeProviderMessage(token);
|
|
181
|
+
if (policy === 'throw') {
|
|
182
|
+
throw new DuplicateProviderError(message, {
|
|
183
|
+
module: '<runtime>',
|
|
184
|
+
token,
|
|
185
|
+
phase: 'provider registration',
|
|
186
|
+
hint: `Remove the duplicate runtime provider registration, use container.override() after bootstrap for intentional replacements, or set duplicateProviderPolicy to 'warn' or 'ignore'.`
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (policy === 'warn') {
|
|
190
|
+
logger?.warn(message, 'BootstrapModule');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function selectEffectiveBootstrapProviders(modules, runtimeProviders, rootModule, policy, logger) {
|
|
194
|
+
const selectedRuntimeProviderIndexes = new Map();
|
|
195
|
+
const selectedModuleProviderIndexes = new Map();
|
|
196
|
+
const runtimeMultiTokens = new Set();
|
|
197
|
+
const runtimeSingleTokens = new Set();
|
|
198
|
+
const runtimeProviderEntries = [];
|
|
199
|
+
const moduleProviderEntries = [];
|
|
161
200
|
for (const runtimeProvider of runtimeProviders ?? []) {
|
|
162
201
|
const token = providerToken(runtimeProvider);
|
|
163
|
-
|
|
202
|
+
const multi = isMultiProvider(runtimeProvider);
|
|
203
|
+
const existingRuntimeProviderIndex = selectedRuntimeProviderIndexes.get(token);
|
|
204
|
+
if (multi) {
|
|
205
|
+
runtimeMultiTokens.add(token);
|
|
206
|
+
} else {
|
|
207
|
+
runtimeSingleTokens.add(token);
|
|
208
|
+
}
|
|
209
|
+
if (!multi && existingRuntimeProviderIndex !== undefined) {
|
|
210
|
+
handleDuplicateRuntimeProvider(token, policy, logger);
|
|
211
|
+
}
|
|
212
|
+
runtimeProviderEntries.push({
|
|
164
213
|
moduleName: '<runtime>',
|
|
165
214
|
provider: runtimeProvider,
|
|
166
215
|
source: 'runtime',
|
|
167
216
|
token
|
|
168
217
|
});
|
|
218
|
+
if (!multi) {
|
|
219
|
+
selectedRuntimeProviderIndexes.set(token, runtimeProviderEntries.length - 1);
|
|
220
|
+
}
|
|
169
221
|
}
|
|
170
222
|
for (const compiledModule of modules) {
|
|
171
223
|
for (const provider of compiledModule.definition.providers ?? []) {
|
|
172
224
|
const token = providerToken(provider);
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
module: compiledModule.type.name,
|
|
179
|
-
token,
|
|
180
|
-
phase: 'provider registration',
|
|
181
|
-
hint: `Remove the duplicate registration from one of the modules, use container.override() for intentional replacements, or set duplicateProviderPolicy to 'warn' or 'ignore'.`
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
if (policy === 'warn') {
|
|
185
|
-
logger?.warn(message, 'BootstrapModule');
|
|
186
|
-
}
|
|
225
|
+
const multi = isMultiProvider(provider);
|
|
226
|
+
const existingModuleProviderIndex = selectedModuleProviderIndexes.get(token);
|
|
227
|
+
const existing = existingModuleProviderIndex === undefined ? undefined : moduleProviderEntries[existingModuleProviderIndex];
|
|
228
|
+
if (!multi && existing && existing.source === 'module') {
|
|
229
|
+
handleDuplicateProvider(token, compiledModule.type.name, existing.moduleName, policy, logger);
|
|
187
230
|
}
|
|
188
|
-
|
|
231
|
+
const entry = {
|
|
189
232
|
moduleName: compiledModule.type.name,
|
|
233
|
+
moduleType: compiledModule.type,
|
|
190
234
|
provider,
|
|
191
235
|
source: 'module',
|
|
192
236
|
token
|
|
193
|
-
}
|
|
237
|
+
};
|
|
238
|
+
moduleProviderEntries.push(entry);
|
|
239
|
+
if (!multi) {
|
|
240
|
+
selectedModuleProviderIndexes.set(token, moduleProviderEntries.length - 1);
|
|
241
|
+
}
|
|
194
242
|
}
|
|
195
243
|
}
|
|
196
|
-
|
|
244
|
+
const selectedModuleProviderIndexesSet = new Set(selectedModuleProviderIndexes.values());
|
|
245
|
+
const selectedRuntimeProviderIndexesSet = new Set(selectedRuntimeProviderIndexes.values());
|
|
246
|
+
const moduleProviders = [];
|
|
247
|
+
const rootModuleProviders = [];
|
|
248
|
+
const effectiveRuntimeProviders = runtimeProviderEntries.filter((entry, index) => isMultiProvider(entry.provider) || selectedRuntimeProviderIndexesSet.has(index)).map(entry => entry.provider);
|
|
249
|
+
moduleProviderEntries.forEach((entry, index) => {
|
|
250
|
+
const multi = isMultiProvider(entry.provider);
|
|
251
|
+
if (entry.source !== 'module' || !multi && !selectedModuleProviderIndexesSet.has(index)) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (runtimeSingleTokens.has(entry.token) || runtimeMultiTokens.has(entry.token) && !multi) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
moduleProviders.push(entry.provider);
|
|
258
|
+
if (entry.moduleType === rootModule) {
|
|
259
|
+
rootModuleProviders.push(entry.provider);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
moduleProviders,
|
|
264
|
+
rootModuleProviders,
|
|
265
|
+
runtimeProviders: effectiveRuntimeProviders
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function isMultiProvider(provider) {
|
|
269
|
+
return typeof provider === 'object' && provider !== null && 'multi' in provider && provider.multi === true;
|
|
197
270
|
}
|
|
198
271
|
function registerControllers(container, modules) {
|
|
199
272
|
for (const compiledModule of modules) {
|
|
@@ -251,18 +324,18 @@ export function bootstrapModule(rootModule, options = {}) {
|
|
|
251
324
|
const container = new Container();
|
|
252
325
|
const policy = options.duplicateProviderPolicy ?? 'warn';
|
|
253
326
|
const runtimeProviders = options.providers ?? [];
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
container.register(...runtimeProviders);
|
|
327
|
+
const effectiveProviders = selectEffectiveBootstrapProviders(modules, runtimeProviders, rootModule, policy, options.logger);
|
|
328
|
+
if (effectiveProviders.runtimeProviders.length > 0) {
|
|
329
|
+
container.register(...effectiveProviders.runtimeProviders);
|
|
258
330
|
}
|
|
259
|
-
if (moduleProviders.length > 0) {
|
|
260
|
-
container.register(...moduleProviders);
|
|
331
|
+
if (effectiveProviders.moduleProviders.length > 0) {
|
|
332
|
+
container.register(...effectiveProviders.moduleProviders);
|
|
261
333
|
}
|
|
262
334
|
registerControllers(container, modules);
|
|
263
335
|
registerModuleMiddleware(container, modules);
|
|
264
336
|
return {
|
|
265
337
|
container,
|
|
338
|
+
effectiveProviders,
|
|
266
339
|
modules,
|
|
267
340
|
rootModule
|
|
268
341
|
};
|
|
@@ -275,9 +348,10 @@ class FluoApplication {
|
|
|
275
348
|
applicationState = 'bootstrapped';
|
|
276
349
|
closed = false;
|
|
277
350
|
closingPromise;
|
|
351
|
+
contextResolutionCache = new Map();
|
|
278
352
|
lifecycleInstances;
|
|
279
353
|
connectedMicroservices = [];
|
|
280
|
-
constructor(container, modules, rootModule, dispatcher, bootstrapTiming, adapter, hasHttpAdapter, platformShell, lifecycleInstances, logger, runtimeCleanup) {
|
|
354
|
+
constructor(container, modules, rootModule, dispatcher, bootstrapTiming, adapter, hasHttpAdapter, platformShell, lifecycleInstances, logger, runtimeCleanup, contextCacheableTokens) {
|
|
281
355
|
this.container = container;
|
|
282
356
|
this.modules = modules;
|
|
283
357
|
this.rootModule = rootModule;
|
|
@@ -288,13 +362,18 @@ class FluoApplication {
|
|
|
288
362
|
this.platformShell = platformShell;
|
|
289
363
|
this.logger = logger;
|
|
290
364
|
this.runtimeCleanup = runtimeCleanup;
|
|
365
|
+
this.contextCacheableTokens = contextCacheableTokens;
|
|
291
366
|
this.lifecycleInstances = lifecycleInstances;
|
|
367
|
+
installContextCacheInvalidation(this.container, this.contextResolutionCache, this.contextCacheableTokens);
|
|
292
368
|
}
|
|
293
369
|
get state() {
|
|
294
370
|
return this.applicationState;
|
|
295
371
|
}
|
|
296
372
|
async get(token) {
|
|
297
|
-
|
|
373
|
+
if (this.closed) {
|
|
374
|
+
return this.container.resolve(token);
|
|
375
|
+
}
|
|
376
|
+
return resolveContextToken(this.container, token, this.contextCacheableTokens, this.contextResolutionCache);
|
|
298
377
|
}
|
|
299
378
|
|
|
300
379
|
/**
|
|
@@ -363,6 +442,7 @@ class FluoApplication {
|
|
|
363
442
|
adapter: this.adapter,
|
|
364
443
|
container: this.container,
|
|
365
444
|
lifecycleInstances: this.lifecycleInstances,
|
|
445
|
+
modules: this.modules,
|
|
366
446
|
runtimeCleanup: this.runtimeCleanup,
|
|
367
447
|
signal
|
|
368
448
|
});
|
|
@@ -380,16 +460,22 @@ class FluoApplication {
|
|
|
380
460
|
class FluoApplicationContext {
|
|
381
461
|
closed = false;
|
|
382
462
|
closingPromise;
|
|
383
|
-
|
|
463
|
+
contextResolutionCache = new Map();
|
|
464
|
+
constructor(container, modules, rootModule, bootstrapTiming, lifecycleInstances, runtimeCleanup, contextCacheableTokens) {
|
|
384
465
|
this.container = container;
|
|
385
466
|
this.modules = modules;
|
|
386
467
|
this.rootModule = rootModule;
|
|
387
468
|
this.bootstrapTiming = bootstrapTiming;
|
|
388
469
|
this.lifecycleInstances = lifecycleInstances;
|
|
389
470
|
this.runtimeCleanup = runtimeCleanup;
|
|
471
|
+
this.contextCacheableTokens = contextCacheableTokens;
|
|
472
|
+
installContextCacheInvalidation(this.container, this.contextResolutionCache, this.contextCacheableTokens);
|
|
390
473
|
}
|
|
391
474
|
async get(token) {
|
|
392
|
-
|
|
475
|
+
if (this.closed) {
|
|
476
|
+
return this.container.resolve(token);
|
|
477
|
+
}
|
|
478
|
+
return resolveContextToken(this.container, token, this.contextCacheableTokens, this.contextResolutionCache);
|
|
393
479
|
}
|
|
394
480
|
async close(signal) {
|
|
395
481
|
if (this.closed) {
|
|
@@ -403,6 +489,7 @@ class FluoApplicationContext {
|
|
|
403
489
|
await closeRuntimeResources({
|
|
404
490
|
container: this.container,
|
|
405
491
|
lifecycleInstances: this.lifecycleInstances,
|
|
492
|
+
modules: this.modules,
|
|
406
493
|
runtimeCleanup: this.runtimeCleanup,
|
|
407
494
|
signal
|
|
408
495
|
});
|
|
@@ -488,22 +575,113 @@ class FluoMicroserviceApplication {
|
|
|
488
575
|
/**
|
|
489
576
|
* lifecycle hook이 있는 singleton provider 인스턴스를 미리 해석해 둔다.
|
|
490
577
|
*/
|
|
491
|
-
async function resolveLifecycleInstances(container, providers) {
|
|
492
|
-
const
|
|
578
|
+
async function resolveLifecycleInstances(container, providers, resolvedInstances = []) {
|
|
579
|
+
const lifecycleEntries = [];
|
|
493
580
|
const seen = new Set();
|
|
494
581
|
for (const provider of providers) {
|
|
495
|
-
const scope = providerScope(provider);
|
|
496
|
-
if (scope === 'request' || scope === 'transient') {
|
|
497
|
-
continue;
|
|
498
|
-
}
|
|
499
582
|
const token = providerToken(provider);
|
|
500
583
|
if (seen.has(token)) {
|
|
501
584
|
continue;
|
|
502
585
|
}
|
|
586
|
+
if (isHookBearingValueProvider(provider)) {
|
|
587
|
+
seen.add(token);
|
|
588
|
+
lifecycleEntries.push({
|
|
589
|
+
token,
|
|
590
|
+
useValue: provider.useValue
|
|
591
|
+
});
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
if (!isDirectSingletonContextProvider(provider)) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
503
597
|
seen.add(token);
|
|
504
|
-
|
|
598
|
+
lifecycleEntries.push({
|
|
599
|
+
token
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
const resolutionResults = await Promise.allSettled(lifecycleEntries.map(entry => entry.useValue ?? container.resolve(entry.token)));
|
|
603
|
+
let resolutionError;
|
|
604
|
+
let hasResolutionError = false;
|
|
605
|
+
for (const result of resolutionResults) {
|
|
606
|
+
if (result.status === 'fulfilled') {
|
|
607
|
+
resolvedInstances.push(result.value);
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (!hasResolutionError) {
|
|
611
|
+
resolutionError = result.reason;
|
|
612
|
+
hasResolutionError = true;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (hasResolutionError) {
|
|
616
|
+
throw resolutionError;
|
|
505
617
|
}
|
|
506
|
-
return
|
|
618
|
+
return resolvedInstances;
|
|
619
|
+
}
|
|
620
|
+
function createContextCacheableTokenSet(effectiveProviders, runtimeTokens) {
|
|
621
|
+
const cacheableTokens = new Set(runtimeTokens);
|
|
622
|
+
for (const provider of effectiveProviders.runtimeProviders) {
|
|
623
|
+
if (!isDirectSingletonContextProvider(provider)) {
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
cacheableTokens.add(providerToken(provider));
|
|
627
|
+
}
|
|
628
|
+
for (const provider of effectiveProviders.rootModuleProviders) {
|
|
629
|
+
if (!isDirectSingletonContextProvider(provider)) {
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
cacheableTokens.add(providerToken(provider));
|
|
633
|
+
}
|
|
634
|
+
return cacheableTokens;
|
|
635
|
+
}
|
|
636
|
+
function isDirectSingletonContextProvider(provider) {
|
|
637
|
+
if (isMultiProvider(provider)) {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
if (typeof provider === 'function') {
|
|
641
|
+
return providerScope(provider) === 'singleton';
|
|
642
|
+
}
|
|
643
|
+
if ('useExisting' in provider || 'useValue' in provider) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
return providerScope(provider) === 'singleton';
|
|
647
|
+
}
|
|
648
|
+
function isHookBearingValueProvider(provider) {
|
|
649
|
+
return typeof provider === 'object' && provider !== null && 'useValue' in provider && hasLifecycleHook(provider.useValue);
|
|
650
|
+
}
|
|
651
|
+
function hasLifecycleHook(value) {
|
|
652
|
+
return isOnModuleInit(value) || isOnApplicationBootstrap(value) || isOnModuleDestroy(value) || isOnApplicationShutdown(value);
|
|
653
|
+
}
|
|
654
|
+
function installContextCacheInvalidation(container, cache, cacheableTokens) {
|
|
655
|
+
const cacheInvalidatingContainer = container;
|
|
656
|
+
const override = cacheInvalidatingContainer.override.bind(container);
|
|
657
|
+
cacheInvalidatingContainer.override = (...providers) => {
|
|
658
|
+
const result = override(...providers);
|
|
659
|
+
cache.clear();
|
|
660
|
+
for (const provider of providers) {
|
|
661
|
+
const token = providerToken(provider);
|
|
662
|
+
if (isDirectSingletonContextProvider(provider)) {
|
|
663
|
+
cacheableTokens.add(token);
|
|
664
|
+
} else {
|
|
665
|
+
cacheableTokens.delete(token);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return result;
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
async function resolveContextToken(container, token, cacheableTokens, cache) {
|
|
672
|
+
if (!cacheableTokens.has(token)) {
|
|
673
|
+
return container.resolve(token);
|
|
674
|
+
}
|
|
675
|
+
const cached = cache.get(token);
|
|
676
|
+
if (cached) {
|
|
677
|
+
return cached;
|
|
678
|
+
}
|
|
679
|
+
const resolution = container.resolve(token).catch(error => {
|
|
680
|
+
cache.delete(token);
|
|
681
|
+
throw error;
|
|
682
|
+
});
|
|
683
|
+
cache.set(token, resolution);
|
|
684
|
+
return resolution;
|
|
507
685
|
}
|
|
508
686
|
|
|
509
687
|
/**
|
|
@@ -600,9 +778,9 @@ function registerRuntimeApplicationContextTokens(bootstrapped, platformShell) {
|
|
|
600
778
|
useValue: platformShell
|
|
601
779
|
});
|
|
602
780
|
}
|
|
603
|
-
async function resolveBootstrapLifecycleInstances(bootstrapped,
|
|
604
|
-
const lifecycleProviders = [...runtimeProviders, ...bootstrapped.
|
|
605
|
-
return resolveLifecycleInstances(bootstrapped.container, lifecycleProviders);
|
|
781
|
+
async function resolveBootstrapLifecycleInstances(bootstrapped, resolvedInstances) {
|
|
782
|
+
const lifecycleProviders = [...bootstrapped.effectiveProviders.runtimeProviders, ...bootstrapped.effectiveProviders.moduleProviders];
|
|
783
|
+
return resolveLifecycleInstances(bootstrapped.container, lifecycleProviders, resolvedInstances);
|
|
606
784
|
}
|
|
607
785
|
async function runBootstrapLifecycle(modules, lifecycleInstances, logger, platformShell) {
|
|
608
786
|
resetReadinessState(modules);
|
|
@@ -654,6 +832,7 @@ export async function bootstrapApplication(options) {
|
|
|
654
832
|
const logger = options.logger ?? createConsoleApplicationLogger();
|
|
655
833
|
let lifecycleInstances = [];
|
|
656
834
|
let bootstrappedContainer;
|
|
835
|
+
let bootstrappedModules = [];
|
|
657
836
|
const hasHttpAdapter = options.adapter !== undefined;
|
|
658
837
|
const adapter = options.adapter ?? {
|
|
659
838
|
async close() {},
|
|
@@ -689,8 +868,9 @@ export async function bootstrapApplication(options) {
|
|
|
689
868
|
});
|
|
690
869
|
}
|
|
691
870
|
bootstrappedContainer = bootstrapped.container;
|
|
871
|
+
bootstrappedModules = bootstrapped.modules;
|
|
692
872
|
const resolveLifecycleStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
693
|
-
lifecycleInstances = await resolveBootstrapLifecycleInstances(bootstrapped,
|
|
873
|
+
lifecycleInstances = await resolveBootstrapLifecycleInstances(bootstrapped, lifecycleInstances);
|
|
694
874
|
lifecycleInstances.push({
|
|
695
875
|
onModuleDestroy() {
|
|
696
876
|
return platformShell.stop();
|
|
@@ -719,13 +899,14 @@ export async function bootstrapApplication(options) {
|
|
|
719
899
|
});
|
|
720
900
|
}
|
|
721
901
|
const bootstrapTiming = timingEnabled ? createBootstrapTimingDiagnostics(timingPhases, runtimePerformance.now() - timingStart) : undefined;
|
|
722
|
-
return new FluoApplication(bootstrapped.container, bootstrapped.modules, options.rootModule, dispatcher, bootstrapTiming, adapter, hasHttpAdapter, platformShell, lifecycleInstances, logger, runtimeCleanup);
|
|
902
|
+
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]));
|
|
723
903
|
} catch (error) {
|
|
724
904
|
logger.error('Failed to bootstrap the fluo application. Check the error below for what failed and how to fix it.', error, 'FluoFactory');
|
|
725
905
|
await runBootstrapFailureCleanup({
|
|
726
906
|
container: bootstrappedContainer,
|
|
727
907
|
lifecycleInstances,
|
|
728
908
|
logger,
|
|
909
|
+
modules: bootstrappedModules,
|
|
729
910
|
runtimeCleanup,
|
|
730
911
|
scope: 'application'
|
|
731
912
|
});
|
|
@@ -764,6 +945,7 @@ export class FluoFactory {
|
|
|
764
945
|
const logger = options.logger ?? createConsoleApplicationLogger();
|
|
765
946
|
let lifecycleInstances = [];
|
|
766
947
|
let bootstrappedContainer;
|
|
948
|
+
let bootstrappedModules = [];
|
|
767
949
|
const runtimeCleanup = [];
|
|
768
950
|
const platformShell = createRuntimePlatformShell(options.platform?.components);
|
|
769
951
|
const timingEnabled = options.diagnostics?.timing === true;
|
|
@@ -794,8 +976,9 @@ export class FluoFactory {
|
|
|
794
976
|
});
|
|
795
977
|
}
|
|
796
978
|
bootstrappedContainer = bootstrapped.container;
|
|
979
|
+
bootstrappedModules = bootstrapped.modules;
|
|
797
980
|
const resolveLifecycleStart = timingEnabled ? runtimePerformance.now() : 0;
|
|
798
|
-
lifecycleInstances = await resolveBootstrapLifecycleInstances(bootstrapped,
|
|
981
|
+
lifecycleInstances = await resolveBootstrapLifecycleInstances(bootstrapped, lifecycleInstances);
|
|
799
982
|
lifecycleInstances.push({
|
|
800
983
|
onModuleDestroy() {
|
|
801
984
|
return platformShell.stop();
|
|
@@ -816,13 +999,14 @@ export class FluoFactory {
|
|
|
816
999
|
});
|
|
817
1000
|
}
|
|
818
1001
|
const bootstrapTiming = timingEnabled ? createBootstrapTimingDiagnostics(timingPhases, runtimePerformance.now() - timingStart) : undefined;
|
|
819
|
-
return new FluoApplicationContext(bootstrapped.container, bootstrapped.modules, rootModule, bootstrapTiming, lifecycleInstances, runtimeCleanup);
|
|
1002
|
+
return new FluoApplicationContext(bootstrapped.container, bootstrapped.modules, rootModule, bootstrapTiming, lifecycleInstances, runtimeCleanup, createContextCacheableTokenSet(bootstrapped.effectiveProviders, [RUNTIME_CONTAINER, COMPILED_MODULES, PLATFORM_SHELL]));
|
|
820
1003
|
} catch (error) {
|
|
821
1004
|
logger.error('Failed to bootstrap application context. Check the error below for what failed and how to fix it.', error, 'FluoFactory');
|
|
822
1005
|
await runBootstrapFailureCleanup({
|
|
823
1006
|
container: bootstrappedContainer,
|
|
824
1007
|
lifecycleInstances,
|
|
825
1008
|
logger,
|
|
1009
|
+
modules: bootstrappedModules,
|
|
826
1010
|
runtimeCleanup,
|
|
827
1011
|
scope: 'application context'
|
|
828
1012
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-graph.d.ts","sourceRoot":"","sources":["../src/module-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAK1C,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAMvD;AAyDD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,GAAE,QAAQ,EAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAE5E;
|
|
1
|
+
{"version":3,"file":"module-graph.d.ts","sourceRoot":"","sources":["../src/module-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAK1C,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAEvG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAMvD;AAyDD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,GAAE,QAAQ,EAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAE5E;AA8UD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B,GAAG,cAAc,EAAE,CASjH"}
|