@fluojs/runtime 1.0.0-beta.3 → 1.0.0-beta.5

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 CHANGED
@@ -123,6 +123,7 @@ class UsersModule {}
123
123
  ## 동작 계약
124
124
 
125
125
  - 요청 바디 파싱은 Web 표준 요청과 Node 기반 요청 모두에서 바이트가 스트리밍되는 동안 `maxBodySize`를 강제합니다.
126
+ - `@fluojs/runtime/node`에서는 Node 요청 바디 파싱 전에 primary `content-type` media type을 normalize한 뒤 JSON 및 멀티파트 여부를 판단하므로, 대소문자가 섞인 JSON/멀티파트 헤더도 문서화된 파서 동작을 그대로 유지합니다.
126
127
  - Node 기반 및 Web 표준 요청 wrapper는 바디 파싱 전에 저비용 요청 metadata를 snapshot으로 고정한 뒤 dispatch 경계에서 `body`/`rawBody`를 한 번 materialize하므로 userland는 계속 동기 parsed 값을 관찰합니다.
127
128
  - Node 기반 쿠키/쿼리 값과 Web 표준 헤더는 요청 wrapper가 생성되는 시점에 snapshot으로 고정된 뒤 요청별로 lazy하게 normalize되고 memoize됩니다. 이후 upstream 객체가 변경되어도 `FrameworkRequest` view는 바뀌지 않습니다.
128
129
  - `ApplicationContext.get()`과 `Application.get()`은 bootstrap 시점에 알려진 직접 root singleton class/factory provider 조회만 memoize하며, alias, request, transient, 종료 이후, multi-provider, `container.override()` 해석 의미는 그대로 유지합니다.
@@ -131,6 +132,7 @@ class UsersModule {}
131
132
  - 애플리케이션 또는 컨텍스트 bootstrap이 런타임 리소스나 lifecycle instance 생성 이후 실패하면 fluo는 readiness를 초기화하고, 등록된 runtime cleanup callback을 실행하며, 그 시점까지 해석된 instance의 shutdown hook을 `bootstrap-failed`로 호출하고, 컨테이너를 dispose하고, cleanup 실패를 로그로 남긴 뒤 원래 bootstrap error를 다시 던집니다.
132
133
  - Bootstrap은 독립적인 singleton lifecycle provider를 병렬로 해석한 뒤 lifecycle hook은 결정적인 provider 순서대로 실행합니다.
133
134
  - 멀티파트 파싱은 누적 바디 크기가 설정된 `multipart.maxTotalSize`를 넘으면 즉시 거부되며, 런타임 어댑터는 별도 재정의가 없으면 이 한도를 `maxBodySize`와 동일하게 맞춥니다.
135
+ - `createNodeHttpAdapter(...)`, `bootstrapNodeApplication(...)`, `runNodeApplication(...)`는 `maxBodySize`를 0 이상의 정수 바이트 수로만 받으며, 값이 잘못되면 어댑터 생성/부트스트랩 단계에서 즉시 실패합니다.
134
136
  - 응답 스트림 백프레셔 헬퍼는 `drain`, `close`, `error` 중 어느 경우에도 `waitForDrain()`을 완료시켜 끊어진 연결에서 스트리밍 작성기가 멈추지 않도록 합니다.
135
137
  - 런타임 health 모듈은 bootstrap이 ready로 표시하기 전까지 `/ready`를 HTTP 503과 `starting`으로 보고하며, 애플리케이션/컨텍스트 종료가 시작되는 즉시, 종료 시도가 실패하더라도 다시 `starting`으로 내려갑니다.
136
138
  - 시그널 기반 종료 헬퍼는 bounded drain semantics를 유지하면서 timeout/실패 상황을 로그와 `process.exitCode`로 보고하지만, 최종 프로세스 종료 소유권은 주변 호스트 런타임에 남겨 둡니다.
@@ -170,9 +172,18 @@ import {
170
172
  } from '@fluojs/runtime/node';
171
173
  ```
172
174
 
175
+ ```typescript
176
+ const adapter = createNodeHttpAdapter({
177
+ port: 3000,
178
+ maxBodySize: 1_048_576,
179
+ });
180
+ ```
181
+
182
+ 공개 Node 런타임 surface에서 `maxBodySize`는 바이트 수를 나타내는 숫자만 허용합니다. `'1mb'` 같은 값은 나중에 암묵 변환되지 않고 어댑터 생성 시점에 즉시 거부됩니다.
183
+
173
184
  - `createConsoleApplicationLogger()`: `process.stdout`/`process.stderr`를 사용하는 컬러 콘솔 로거.
174
185
  - `createJsonApplicationLogger()`: `process.stdout`/`process.stderr`를 사용하는 구조화된 JSON 로거.
175
- - `createNodeHttpAdapter()`: 어댑터 우선 런타임 구성을 위한 raw Node `http`/`https` 어댑터 팩토리.
186
+ - `createNodeHttpAdapter()`: 어댑터 우선 런타임 구성을 위한 raw Node `http`/`https` 어댑터 팩토리입니다. primary Node 요청 `content-type`을 JSON/멀티파트 판별 전에 normalize하며, `maxBodySize`는 숫자 바이트 수만 받습니다.
176
187
  - `bootstrapNodeApplication()` / `runNodeApplication()`: 호환 패키지와 직접 Node 런타임 흐름에서 사용하는 Node 전용 부트스트랩 헬퍼.
177
188
  - `createNodeShutdownSignalRegistration()`, `defaultNodeShutdownSignals()`, `registerShutdownSignals()`: 호스트가 명시적으로 시그널 wiring을 제어할 때 쓰는 종료 등록 헬퍼.
178
189
 
package/README.md CHANGED
@@ -123,6 +123,7 @@ 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
+ - 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.
126
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.
127
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.
128
129
  - `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.
@@ -131,6 +132,7 @@ class UsersModule {}
131
132
  - 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
133
  - Bootstrap resolves independent singleton lifecycle providers concurrently, then runs lifecycle hooks in deterministic provider order.
133
134
  - 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.
135
+ - `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.
134
136
  - Response stream backpressure helpers settle `waitForDrain()` on `drain`, `close`, or `error` so streaming writers do not hang on dead connections.
135
137
  - 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.
136
138
  - 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.
@@ -170,9 +172,18 @@ import {
170
172
  } from '@fluojs/runtime/node';
171
173
  ```
172
174
 
175
+ ```typescript
176
+ const adapter = createNodeHttpAdapter({
177
+ port: 3000,
178
+ maxBodySize: 1_048_576,
179
+ });
180
+ ```
181
+
182
+ For the public Node runtime surface, `maxBodySize` is a byte-count number only. Values such as `'1mb'` are rejected immediately during adapter creation instead of being coerced later.
183
+
173
184
  - `createConsoleApplicationLogger()`: Colorized console logger using `process.stdout`/`process.stderr`.
174
185
  - `createJsonApplicationLogger()`: Structured JSON logger using `process.stdout`/`process.stderr`.
175
- - `createNodeHttpAdapter()`: Raw Node `http`/`https` adapter factory for adapter-first runtime setup.
186
+ - `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 numeric bytes.
176
187
  - `bootstrapNodeApplication()` / `runNodeApplication()`: Node-specific bootstrap helpers used by compatibility packages and direct Node runtime flows.
177
188
  - `createNodeShutdownSignalRegistration()`, `defaultNodeShutdownSignals()`, `registerShutdownSignals()`: Shutdown registration helpers for hosts that need explicit signal wiring.
178
189
 
@@ -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,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"}
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;AAmqBD;;;;;;;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
@@ -796,15 +796,21 @@ function createFilterErrorHandler(filters) {
796
796
  return async (error, request, response, requestId) => runExceptionFilters(filters, error, request, response, requestId);
797
797
  }
798
798
  function createRuntimeDispatcherOptions(bootstrapped, options, handlerMapping, errorHandler, logger) {
799
+ const converters = options.converters ?? [];
799
800
  const dispatcherOptions = {
800
801
  appMiddleware: options.middleware ?? [],
801
- binder: new DefaultBinder(options.converters ?? []),
802
802
  handlerMapping,
803
803
  interceptors: options.interceptors ?? [],
804
804
  logger,
805
805
  observers: options.observers ?? [],
806
+ requestScope: {
807
+ converterDefinitions: converters
808
+ },
806
809
  rootContainer: bootstrapped.container
807
810
  };
811
+ if (converters.length > 0) {
812
+ dispatcherOptions.binder = new DefaultBinder(converters);
813
+ }
808
814
  if (errorHandler) {
809
815
  dispatcherOptions.onError = errorHandler;
810
816
  }
@@ -1,11 +1,17 @@
1
1
  import type { Scope } from '@fluojs/di';
2
2
  import type { CompiledModule, ModuleType } from '../types.js';
3
+ /**
4
+ * Describes the runtime diagnostics graph contract.
5
+ */
3
6
  export interface RuntimeDiagnosticsGraph {
4
7
  version: 1;
5
8
  rootModule: string;
6
9
  modules: RuntimeDiagnosticsModule[];
7
10
  relationships: RuntimeDiagnosticsRelationships;
8
11
  }
12
+ /**
13
+ * Describes the runtime diagnostics module contract.
14
+ */
9
15
  export interface RuntimeDiagnosticsModule {
10
16
  name: string;
11
17
  global: boolean;
@@ -14,12 +20,18 @@ export interface RuntimeDiagnosticsModule {
14
20
  providers: RuntimeDiagnosticsProvider[];
15
21
  exports: string[];
16
22
  }
23
+ /**
24
+ * Describes the runtime diagnostics provider contract.
25
+ */
17
26
  export interface RuntimeDiagnosticsProvider {
18
27
  token: string;
19
28
  type: 'class' | 'factory' | 'value' | 'existing';
20
29
  scope: Scope;
21
30
  multi: boolean;
22
31
  }
32
+ /**
33
+ * Describes the runtime diagnostics relationships contract.
34
+ */
23
35
  export interface RuntimeDiagnosticsRelationships {
24
36
  moduleImports: Array<{
25
37
  from: string;
@@ -41,16 +53,42 @@ export interface RuntimeDiagnosticsRelationships {
41
53
  module: string;
42
54
  }>;
43
55
  }
56
+ /**
57
+ * Describes the bootstrap timing phase contract.
58
+ */
44
59
  export interface BootstrapTimingPhase {
45
60
  durationMs: number;
46
61
  name: 'bootstrap_module' | 'register_runtime_tokens' | 'resolve_lifecycle_instances' | 'run_bootstrap_lifecycle' | 'create_dispatcher';
47
62
  }
63
+ /**
64
+ * Describes the bootstrap timing diagnostics contract.
65
+ */
48
66
  export interface BootstrapTimingDiagnostics {
49
67
  phases: BootstrapTimingPhase[];
50
68
  totalMs: number;
51
69
  version: 1;
52
70
  }
71
+ /**
72
+ * Create runtime diagnostics graph.
73
+ *
74
+ * @param modules The modules.
75
+ * @param rootModule The root module.
76
+ * @returns The create runtime diagnostics graph result.
77
+ */
53
78
  export declare function createRuntimeDiagnosticsGraph(modules: readonly CompiledModule[], rootModule: ModuleType): RuntimeDiagnosticsGraph;
79
+ /**
80
+ * Render runtime diagnostics mermaid.
81
+ *
82
+ * @param graph The graph.
83
+ * @returns The render runtime diagnostics mermaid result.
84
+ */
54
85
  export declare function renderRuntimeDiagnosticsMermaid(graph: RuntimeDiagnosticsGraph): string;
86
+ /**
87
+ * Create bootstrap timing diagnostics.
88
+ *
89
+ * @param phases The phases.
90
+ * @param totalMs The total ms.
91
+ * @returns The create bootstrap timing diagnostics result.
92
+ */
55
93
  export declare function createBootstrapTimingDiagnostics(phases: BootstrapTimingPhase[], totalMs: number): BootstrapTimingDiagnostics;
56
94
  //# sourceMappingURL=diagnostics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/health/diagnostics.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAY,KAAK,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9D,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,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,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,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,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EACA,kBAAkB,GAClB,yBAAyB,GACzB,6BAA6B,GAC7B,yBAAyB,GACzB,mBAAmB,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,CAAC,CAAC;CACZ;AAiFD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,EAAE,UAAU,EAAE,UAAU,GAAG,uBAAuB,CAkEjI;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM,CAoCtF;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,oBAAoB,EAAE,EAC9B,OAAO,EAAE,MAAM,GACd,0BAA0B,CAS5B"}
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/health/diagnostics.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAY,KAAK,EAAE,MAAM,YAAY,CAAC;AAElD,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,29 @@
1
1
  import { getClassDiMetadata } from '@fluojs/core/internal';
2
+
3
+ /**
4
+ * Describes the runtime diagnostics graph contract.
5
+ */
6
+
7
+ /**
8
+ * Describes the runtime diagnostics module contract.
9
+ */
10
+
11
+ /**
12
+ * Describes the runtime diagnostics provider contract.
13
+ */
14
+
15
+ /**
16
+ * Describes the runtime diagnostics relationships contract.
17
+ */
18
+
19
+ /**
20
+ * Describes the bootstrap timing phase contract.
21
+ */
22
+
23
+ /**
24
+ * Describes the bootstrap timing diagnostics contract.
25
+ */
26
+
2
27
  function roundMs(value) {
3
28
  return Number(value.toFixed(3));
4
29
  }
@@ -59,6 +84,14 @@ function normalizeProvider(provider) {
59
84
  type: providerShape(provider)
60
85
  };
61
86
  }
87
+
88
+ /**
89
+ * Create runtime diagnostics graph.
90
+ *
91
+ * @param modules The modules.
92
+ * @param rootModule The root module.
93
+ * @returns The create runtime diagnostics graph result.
94
+ */
62
95
  export function createRuntimeDiagnosticsGraph(modules, rootModule) {
63
96
  const moduleDiagnostics = [];
64
97
  const moduleImports = [];
@@ -119,6 +152,13 @@ export function createRuntimeDiagnosticsGraph(modules, rootModule) {
119
152
  version: 1
120
153
  };
121
154
  }
155
+
156
+ /**
157
+ * Render runtime diagnostics mermaid.
158
+ *
159
+ * @param graph The graph.
160
+ * @returns The render runtime diagnostics mermaid result.
161
+ */
122
162
  export function renderRuntimeDiagnosticsMermaid(graph) {
123
163
  const lines = ['graph TD'];
124
164
  const nodeByModule = new Map();
@@ -143,6 +183,14 @@ export function renderRuntimeDiagnosticsMermaid(graph) {
143
183
  }
144
184
  return lines.join('\n');
145
185
  }
186
+
187
+ /**
188
+ * Create bootstrap timing diagnostics.
189
+ *
190
+ * @param phases The phases.
191
+ * @param totalMs The total ms.
192
+ * @returns The create bootstrap timing diagnostics result.
193
+ */
146
194
  export function createBootstrapTimingDiagnostics(phases, totalMs) {
147
195
  return {
148
196
  phases: phases.map(phase => ({
@@ -1,18 +1,39 @@
1
1
  import type { ModuleType } from '../types.js';
2
+ /**
3
+ * Describes the health status contract.
4
+ */
2
5
  export interface HealthStatus {
3
6
  status: 'ok' | 'unavailable';
4
7
  }
8
+ /**
9
+ * Describes the health check response contract.
10
+ */
5
11
  export interface HealthCheckResponse {
6
12
  body: unknown;
7
13
  statusCode?: number;
8
14
  }
15
+ /**
16
+ * Describes the readiness status contract.
17
+ */
9
18
  export interface ReadinessStatus {
10
19
  status: 'ready' | 'starting' | 'unavailable';
11
20
  }
21
+ /**
22
+ * Describes the health module options contract.
23
+ */
12
24
  export interface HealthModuleOptions {
13
25
  healthCheck?: (ctx: import('@fluojs/http').RequestContext) => HealthStatus | HealthCheckResponse | Promise<HealthStatus | HealthCheckResponse>;
14
26
  path?: string;
15
27
  }
28
+ /**
29
+ * Defines the readiness check type.
30
+ */
16
31
  export type ReadinessCheck = () => boolean | Promise<boolean>;
32
+ /**
33
+ * Create health module.
34
+ *
35
+ * @param options The options.
36
+ * @returns The create health module result.
37
+ */
17
38
  export declare function createHealthModule(options?: HealthModuleOptions): ModuleType;
18
39
  //# sourceMappingURL=health.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/health/health.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,GAAG,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;CAC9C;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,cAAc,EAAE,cAAc,KACrD,YAAY,GACZ,mBAAmB,GACnB,OAAO,CAAC,YAAY,GAAG,mBAAmB,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9D,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,UAAU,CA0EhF"}
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/health/health.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,GAAG,aAAa,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,aAAa,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,cAAc,EAAE,cAAc,KACrD,YAAY,GACZ,mBAAmB,GACnB,OAAO,CAAC,YAAY,GAAG,mBAAmB,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,UAAU,CA0EhF"}
@@ -5,6 +5,33 @@ function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.descrip
5
5
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
6
6
  import { Controller, Get } from '@fluojs/http';
7
7
  import { defineModule } from '../bootstrap.js';
8
+
9
+ /**
10
+ * Describes the health status contract.
11
+ */
12
+
13
+ /**
14
+ * Describes the health check response contract.
15
+ */
16
+
17
+ /**
18
+ * Describes the readiness status contract.
19
+ */
20
+
21
+ /**
22
+ * Describes the health module options contract.
23
+ */
24
+
25
+ /**
26
+ * Defines the readiness check type.
27
+ */
28
+
29
+ /**
30
+ * Create health module.
31
+ *
32
+ * @param options The options.
33
+ * @returns The create health module result.
34
+ */
8
35
  export function createHealthModule(options = {}) {
9
36
  let _initProto, _initClass;
10
37
  const basePath = options.path ?? '';
@@ -1,3 +1,8 @@
1
1
  import type { ApplicationLogger } from '../types.js';
2
+ /**
3
+ * Create json application logger.
4
+ *
5
+ * @returns The create json application logger result.
6
+ */
2
7
  export declare function createJsonApplicationLogger(): ApplicationLogger;
3
8
  //# sourceMappingURL=json-logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-logger.d.ts","sourceRoot":"","sources":["../../src/logging/json-logger.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqCrD,wBAAgB,2BAA2B,IAAI,iBAAiB,CAe/D"}
1
+ {"version":3,"file":"json-logger.d.ts","sourceRoot":"","sources":["../../src/logging/json-logger.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqCrD;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,iBAAiB,CAe/D"}
@@ -21,6 +21,12 @@ function buildEntry(level, message, context, error) {
21
21
  }
22
22
  return entry;
23
23
  }
24
+
25
+ /**
26
+ * Create json application logger.
27
+ *
28
+ * @returns The create json application logger result.
29
+ */
24
30
  export function createJsonApplicationLogger() {
25
31
  return {
26
32
  debug(message, context) {
@@ -1,3 +1,8 @@
1
1
  import type { ApplicationLogger } from '../types.js';
2
+ /**
3
+ * Create console application logger.
4
+ *
5
+ * @returns The create console application logger result.
6
+ */
2
7
  export declare function createConsoleApplicationLogger(): ApplicationLogger;
3
8
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAuBrD,wBAAgB,8BAA8B,IAAI,iBAAiB,CAmBlE"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logging/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAuBrD;;;;GAIG;AACH,wBAAgB,8BAA8B,IAAI,iBAAiB,CAmBlE"}
@@ -15,6 +15,12 @@ function formatLog(level, context, message, color) {
15
15
  const contextLabel = colorize(`[${context}]`, BRIGHT_YELLOW, color);
16
16
  return `${prefix} ${pid} - ${timestamp} ${levelLabel} ${contextLabel} ${message}`;
17
17
  }
18
+
19
+ /**
20
+ * Create console application logger.
21
+ *
22
+ * @returns The create console application logger result.
23
+ */
18
24
  export function createConsoleApplicationLogger() {
19
25
  return {
20
26
  debug(message, context = 'fluo') {
@@ -1,7 +1,22 @@
1
1
  import type { ServerResponse } from 'node:http';
2
2
  import type { FrameworkResponseCompression } from '@fluojs/http';
3
3
  type Encoding = 'br' | 'gzip' | 'identity';
4
+ /**
5
+ * Create node response compression.
6
+ *
7
+ * @param response The response.
8
+ * @param acceptEncoding The accept encoding.
9
+ * @returns The create node response compression result.
10
+ */
4
11
  export declare function createNodeResponseCompression(response: ServerResponse, acceptEncoding: string | undefined): FrameworkResponseCompression | undefined;
12
+ /**
13
+ * Compress node response.
14
+ *
15
+ * @param response The response.
16
+ * @param body The body.
17
+ * @param encoding The encoding.
18
+ * @returns The compress node response result.
19
+ */
5
20
  export declare function compressNodeResponse(response: ServerResponse, body: Uint8Array, encoding: Exclude<Encoding, 'identity'>): Promise<void>;
6
21
  export {};
7
22
  //# sourceMappingURL=internal-node-compression.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-node-compression.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-compression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,KAAK,EACV,4BAA4B,EAE7B,MAAM,cAAc,CAAC;AAiBtB,KAAK,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC;AAE3C,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,cAAc,EACxB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,4BAA4B,GAAG,SAAS,CAqB1C;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAcf"}
1
+ {"version":3,"file":"internal-node-compression.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-compression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,KAAK,EACV,4BAA4B,EAE7B,MAAM,cAAc,CAAC;AAiBtB,KAAK,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,cAAc,EACxB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,4BAA4B,GAAG,SAAS,CAqB1C;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -1,6 +1,13 @@
1
1
  import { createBrotliCompress, createGzip } from 'node:zlib';
2
2
  const COMPRESS_THRESHOLD = 1024;
3
3
  const SKIP_CONTENT_TYPES = new Set(['image/', 'audio/', 'video/', 'application/zip', 'application/gzip', 'application/x-bzip', 'application/x-bzip2', 'application/x-xz', 'application/zstd', 'application/octet-stream']);
4
+ /**
5
+ * Create node response compression.
6
+ *
7
+ * @param response The response.
8
+ * @param acceptEncoding The accept encoding.
9
+ * @returns The create node response compression result.
10
+ */
4
11
  export function createNodeResponseCompression(response, acceptEncoding) {
5
12
  if (!acceptEncoding) {
6
13
  return undefined;
@@ -19,6 +26,15 @@ export function createNodeResponseCompression(response, acceptEncoding) {
19
26
  }
20
27
  };
21
28
  }
29
+
30
+ /**
31
+ * Compress node response.
32
+ *
33
+ * @param response The response.
34
+ * @param body The body.
35
+ * @param encoding The encoding.
36
+ * @returns The compress node response result.
37
+ */
22
38
  export function compressNodeResponse(response, body, encoding) {
23
39
  const stream = encoding === 'br' ? createBrotliCompress() : createGzip();
24
40
  response.setHeader('Content-Encoding', encoding);
@@ -1 +1 @@
1
- {"version":3,"file":"internal-node-request.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAInB,OAAO,EAEL,wBAAwB,EACxB,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AAUzB;;GAEG;AACH,qBAAa,mCAAoC,SAAQ,wBAAwB;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,eAAe;IAIrD,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;CAahD;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAkB,EAC7B,eAAe,UAAQ,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAW3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAkB,EAC7B,eAAe,UAAQ,GACtB,gBAAgB,CAsDlB;AAED;;;;;GAKG;AACH,wBAAsB,+BAA+B,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAyBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,WAAW,CAezE;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAG5F"}
1
+ {"version":3,"file":"internal-node-request.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAInB,OAAO,EAEL,wBAAwB,EACxB,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AAUzB;;GAEG;AACH,qBAAa,mCAAoC,SAAQ,wBAAwB;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,eAAe;IAIrD,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;CAahD;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAkB,EAC7B,eAAe,UAAQ,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAW3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAkB,EAC7B,eAAe,UAAQ,GACtB,gBAAgB,CA2DlB;AAoBD;;;;;GAKG;AACH,wBAAsB,+BAA+B,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAyBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,WAAW,CAezE;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAG5F"}
@@ -56,8 +56,8 @@ export function createDeferredFrameworkRequest(request, signal, multipartOptions
56
56
  const searchParams = new URLSearchParams(url.searchParams);
57
57
  const cookies = createMemoizedValue(() => parseCookieHeader(cookieHeader));
58
58
  const query = createMemoizedValue(() => parseQueryParams(searchParams));
59
- const contentType = readPrimaryHeaderValue(headers['content-type']);
60
- const isMultipart = typeof contentType === 'string' && contentType.includes('multipart/form-data');
59
+ const contentType = normalizePrimaryContentType(headers['content-type']);
60
+ const isMultipart = contentType === 'multipart/form-data';
61
61
  const materializeBody = createMemoizedAsyncValue(async () => {
62
62
  if (isMultipart) {
63
63
  const result = await parseMultipart({
@@ -73,6 +73,10 @@ export function createDeferredFrameworkRequest(request, signal, multipartOptions
73
73
  frameworkRequest.files = result.files;
74
74
  return;
75
75
  }
76
+ if (!hasNodeRequestBody(request)) {
77
+ frameworkRequest.body = undefined;
78
+ return;
79
+ }
76
80
  const bodyResult = await readRequestBody(request, headers['content-type'], maxBodySize, preserveRawBody);
77
81
  frameworkRequest.body = bodyResult.body;
78
82
  if (bodyResult.rawBody) {
@@ -97,6 +101,19 @@ export function createDeferredFrameworkRequest(request, signal, multipartOptions
97
101
  };
98
102
  return frameworkRequest;
99
103
  }
104
+ function hasNodeRequestBody(request) {
105
+ const contentLength = request.headers['content-length'];
106
+ const transferEncoding = request.headers['transfer-encoding'];
107
+ const primaryContentLength = Array.isArray(contentLength) ? contentLength[0] : contentLength;
108
+ if (transferEncoding !== undefined) {
109
+ return true;
110
+ }
111
+ if (primaryContentLength === undefined) {
112
+ return true;
113
+ }
114
+ const parsedContentLength = Number(primaryContentLength);
115
+ return !Number.isFinite(parsedContentLength) || parsedContentLength !== 0;
116
+ }
100
117
 
101
118
  /**
102
119
  * Materializes a deferred Node framework request body exactly once.
@@ -187,6 +204,15 @@ function readPrimaryHeaderValue(headerValue) {
187
204
  }
188
205
  return headerValue;
189
206
  }
207
+ function normalizePrimaryContentType(headerValue) {
208
+ const primaryHeaderValue = readPrimaryHeaderValue(headerValue);
209
+ if (typeof primaryHeaderValue !== 'string') {
210
+ return undefined;
211
+ }
212
+ const [mediaType] = primaryHeaderValue.split(';', 1);
213
+ const normalizedMediaType = mediaType?.trim().toLowerCase();
214
+ return normalizedMediaType && normalizedMediaType.length > 0 ? normalizedMediaType : undefined;
215
+ }
190
216
  function decodeCookieValue(raw) {
191
217
  try {
192
218
  return decodeURIComponent(raw);
@@ -231,8 +257,8 @@ async function readRequestBody(request, contentType, maxBodySize = 1 * 1024 * 10
231
257
  rawBody: preserveRawBody ? rawBody : undefined
232
258
  };
233
259
  }
234
- const primaryContentType = Array.isArray(contentType) ? contentType[0] : contentType;
235
- if (typeof primaryContentType === 'string' && primaryContentType.includes('application/json')) {
260
+ const primaryContentType = normalizePrimaryContentType(contentType);
261
+ if (primaryContentType === 'application/json') {
236
262
  try {
237
263
  return {
238
264
  body: JSON.parse(bodyText),
@@ -1,8 +1,28 @@
1
1
  import type { ServerResponse } from 'node:http';
2
2
  import { type FrameworkResponseCompression, type FrameworkResponse } from '@fluojs/http';
3
+ /**
4
+ * Defines the mutable framework response type.
5
+ */
3
6
  export type MutableFrameworkResponse = FrameworkResponse & {
4
7
  statusSet?: boolean;
5
8
  };
6
- export declare function createFrameworkResponse(response: ServerResponse, compression?: FrameworkResponseCompression): MutableFrameworkResponse;
9
+ type FrameworkResponseCompressionFactory = () => FrameworkResponseCompression | undefined;
10
+ /**
11
+ * Create framework response.
12
+ *
13
+ * @param response The response.
14
+ * @param compression The compression.
15
+ * @returns The create framework response result.
16
+ */
17
+ export declare function createFrameworkResponse(response: ServerResponse, compression?: FrameworkResponseCompression | FrameworkResponseCompressionFactory): MutableFrameworkResponse;
18
+ /**
19
+ * Write node adapter error response.
20
+ *
21
+ * @param error The error.
22
+ * @param response The response.
23
+ * @param requestId The request id.
24
+ * @returns The write node adapter error response result.
25
+ */
7
26
  export declare function writeNodeAdapterErrorResponse(error: unknown, response: FrameworkResponse, requestId?: string): Promise<void>;
27
+ export {};
8
28
  //# sourceMappingURL=internal-node-response.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-node-response.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAIL,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA6CnF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,cAAc,EACxB,WAAW,CAAC,EAAE,4BAA4B,GACzC,wBAAwB,CA+F1B;AAED,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf"}
1
+ {"version":3,"file":"internal-node-response.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,OAAO,EAIL,KAAK,4BAA4B,EACjC,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAC;AAEtB;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnF,KAAK,mCAAmC,GAAG,MAAM,4BAA4B,GAAG,SAAS,CAAC;AA6C1F;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,cAAc,EACxB,WAAW,CAAC,EAAE,4BAA4B,GAAG,mCAAmC,GAC/E,wBAAwB,CAqH1B;AAED;;;;;;;GAOG;AACH,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf"}
@@ -1,4 +1,9 @@
1
1
  import { createErrorResponse, HttpException, InternalServerErrorException } from '@fluojs/http';
2
+
3
+ /**
4
+ * Defines the mutable framework response type.
5
+ */
6
+
2
7
  function createFrameworkResponseStream(response) {
3
8
  return {
4
9
  close() {
@@ -39,7 +44,28 @@ function createFrameworkResponseStream(response) {
39
44
  }
40
45
  };
41
46
  }
47
+
48
+ /**
49
+ * Create framework response.
50
+ *
51
+ * @param response The response.
52
+ * @param compression The compression.
53
+ * @returns The create framework response result.
54
+ */
42
55
  export function createFrameworkResponse(response, compression) {
56
+ let activeStream;
57
+ const resolveCompression = (() => {
58
+ const factory = typeof compression === 'function' ? compression : () => compression;
59
+ let resolved = false;
60
+ let value;
61
+ return () => {
62
+ if (!resolved) {
63
+ value = factory();
64
+ resolved = true;
65
+ }
66
+ return value;
67
+ };
68
+ })();
43
69
  const mergeSetCookieHeader = (current, incoming) => {
44
70
  const nextValues = Array.isArray(incoming) ? incoming : [incoming];
45
71
  if (current === undefined) {
@@ -56,7 +82,10 @@ export function createFrameworkResponse(response, compression) {
56
82
  committed: response.headersSent || response.writableEnded,
57
83
  headers: {},
58
84
  raw: response,
59
- stream: createFrameworkResponseStream(response),
85
+ get stream() {
86
+ activeStream ??= createFrameworkResponseStream(response);
87
+ return activeStream;
88
+ },
60
89
  redirect(status, location) {
61
90
  this.setStatus(status);
62
91
  this.setHeader('Location', location);
@@ -74,9 +103,10 @@ export function createFrameworkResponse(response, compression) {
74
103
  }
75
104
  const contentType = response.getHeader('Content-Type');
76
105
  const payload = typeof serialized.payload === 'string' ? Buffer.from(serialized.payload, 'utf8') : serialized.payload;
77
- if (compression) {
106
+ const activeCompression = resolveCompression();
107
+ if (activeCompression) {
78
108
  this.committed = true;
79
- return Promise.resolve(compression.write(payload, {
109
+ return Promise.resolve(activeCompression.write(payload, {
80
110
  contentType
81
111
  })).then(handled => {
82
112
  if (!handled && !response.writableEnded) {
@@ -113,6 +143,15 @@ export function createFrameworkResponse(response, compression) {
113
143
  };
114
144
  return frameworkResponse;
115
145
  }
146
+
147
+ /**
148
+ * Write node adapter error response.
149
+ *
150
+ * @param error The error.
151
+ * @param response The response.
152
+ * @param requestId The request id.
153
+ * @returns The write node adapter error response result.
154
+ */
116
155
  export async function writeNodeAdapterErrorResponse(error, response, requestId) {
117
156
  const httpError = toHttpException(error);
118
157
  response.setStatus(httpError.status);
@@ -11,6 +11,9 @@ declare module '@fluojs/http' {
11
11
  rawBody?: Uint8Array;
12
12
  }
13
13
  }
14
+ /**
15
+ * Describes the node http adapter options contract.
16
+ */
14
17
  export interface NodeHttpAdapterOptions {
15
18
  host?: string;
16
19
  https?: HttpsServerOptions;
@@ -21,8 +24,17 @@ export interface NodeHttpAdapterOptions {
21
24
  retryLimit?: number;
22
25
  shutdownTimeoutMs?: number;
23
26
  }
27
+ /**
28
+ * Defines the node application signal type.
29
+ */
24
30
  export type NodeApplicationSignal = 'SIGINT' | 'SIGTERM';
31
+ /**
32
+ * Defines the cors input type.
33
+ */
25
34
  export type CorsInput = false | string | string[] | CorsOptions;
35
+ /**
36
+ * Describes the bootstrap node application options contract.
37
+ */
26
38
  export interface BootstrapNodeApplicationOptions extends Omit<CreateApplicationOptions, 'adapter' | 'logger' | 'middleware'> {
27
39
  compression?: boolean;
28
40
  cors?: CorsInput;
@@ -41,6 +53,9 @@ export interface BootstrapNodeApplicationOptions extends Omit<CreateApplicationO
41
53
  securityHeaders?: false | SecurityHeadersOptions;
42
54
  shutdownTimeoutMs?: number;
43
55
  }
56
+ /**
57
+ * Describes the run node application options contract.
58
+ */
44
59
  export interface RunNodeApplicationOptions extends BootstrapNodeApplicationOptions {
45
60
  forceExitTimeoutMs?: number;
46
61
  shutdownSignals?: false | readonly NodeApplicationSignal[];
@@ -50,22 +65,21 @@ interface NodeListenTarget {
50
65
  url: string;
51
66
  }
52
67
  type NodeServer = ReturnType<typeof createHttpServer> | ReturnType<typeof createHttpsServer>;
68
+ /**
69
+ * Represents the node http application adapter.
70
+ */
53
71
  export declare class NodeHttpApplicationAdapter implements HttpApplicationAdapter {
54
72
  private readonly port;
55
73
  private readonly host;
56
74
  private readonly retryDelayMs;
57
75
  private readonly retryLimit;
58
- private readonly compression;
59
76
  private readonly httpsOptions;
60
- private readonly multipartOptions?;
61
- private readonly maxBodySize;
62
- private readonly preserveRawBody;
63
77
  private readonly shutdownTimeoutMs;
64
78
  private readonly server;
65
79
  private dispatcher?;
66
80
  private readonly requestResponseFactory;
67
81
  private readonly sockets;
68
- constructor(port: number, host: string | undefined, retryDelayMs: number | undefined, retryLimit: number | undefined, compression: boolean | undefined, httpsOptions: HttpsServerOptions | undefined, multipartOptions?: MultipartOptions | undefined, maxBodySize?: number, preserveRawBody?: boolean, shutdownTimeoutMs?: number);
82
+ constructor(port: number, host: string | undefined, retryDelayMs: number | undefined, retryLimit: number | undefined, compression: boolean | undefined, httpsOptions: HttpsServerOptions | undefined, multipartOptions?: MultipartOptions, maxBodySize?: number, preserveRawBody?: boolean, shutdownTimeoutMs?: number);
69
83
  getServer(): NodeServer;
70
84
  getRealtimeCapability(): import("@fluojs/http").ServerBackedHttpAdapterRealtimeCapability;
71
85
  getListenTarget(): NodeListenTarget;
@@ -73,8 +87,30 @@ export declare class NodeHttpApplicationAdapter implements HttpApplicationAdapte
73
87
  close(): Promise<void>;
74
88
  private handleRequest;
75
89
  }
90
+ /**
91
+ * Create node http adapter.
92
+ *
93
+ * @param options The options.
94
+ * @param compression The compression.
95
+ * @param multipartOptions The multipart options.
96
+ * @returns The create node http adapter result.
97
+ */
76
98
  export declare function createNodeHttpAdapter(options?: NodeHttpAdapterOptions, compression?: boolean, multipartOptions?: MultipartOptions): HttpApplicationAdapter;
99
+ /**
100
+ * Bootstrap node application.
101
+ *
102
+ * @param rootModule The root module.
103
+ * @param options The options.
104
+ * @returns The bootstrap node application result.
105
+ */
77
106
  export declare function bootstrapNodeApplication(rootModule: ModuleType, options: BootstrapNodeApplicationOptions): Promise<Application>;
107
+ /**
108
+ * Run node application.
109
+ *
110
+ * @param rootModule The root module.
111
+ * @param options The options.
112
+ * @returns The run node application result.
113
+ */
78
114
  export declare function runNodeApplication(rootModule: ModuleType, options: RunNodeApplicationOptions): Promise<Application>;
79
115
  export { compressNodeResponse, createNodeResponseCompression, createNodeShutdownSignalRegistration, defaultNodeShutdownSignals, registerShutdownSignals, };
80
116
  //# sourceMappingURL=internal-node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal-node.d.ts","sourceRoot":"","sources":["../../src/node/internal-node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"internal-node.d.ts","sourceRoot":"","sources":["../../src/node/internal-node.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,32 @@ import { createDeferredFrameworkRequest, NodeRequestPayloadTooLargeException, cr
7
7
  import { createFrameworkResponse, writeNodeAdapterErrorResponse } from './internal-node-response.js';
8
8
  import { createNodeShutdownSignalRegistration, defaultNodeShutdownSignals, registerShutdownSignals } from './internal-node-shutdown.js';
9
9
  import { dispatchWithRequestResponseFactory } from '../adapters/request-response-factory.js';
10
+
11
+ /**
12
+ * Describes the node http adapter options contract.
13
+ */
14
+
15
+ /**
16
+ * Defines the node application signal type.
17
+ */
18
+
19
+ /**
20
+ * Defines the cors input type.
21
+ */
22
+
10
23
  const DEFAULT_SHUTDOWN_TIMEOUT_MS = 10_000;
24
+
25
+ /**
26
+ * Describes the bootstrap node application options contract.
27
+ */
28
+
29
+ /**
30
+ * Describes the run node application options contract.
31
+ */
32
+
33
+ /**
34
+ * Represents the node http application adapter.
35
+ */
11
36
  export class NodeHttpApplicationAdapter {
12
37
  server;
13
38
  dispatcher;
@@ -18,11 +43,7 @@ export class NodeHttpApplicationAdapter {
18
43
  this.host = host;
19
44
  this.retryDelayMs = retryDelayMs;
20
45
  this.retryLimit = retryLimit;
21
- this.compression = compression;
22
46
  this.httpsOptions = httpsOptions;
23
- this.multipartOptions = multipartOptions;
24
- this.maxBodySize = maxBodySize;
25
- this.preserveRawBody = preserveRawBody;
26
47
  this.shutdownTimeoutMs = shutdownTimeoutMs;
27
48
  this.requestResponseFactory = createNodeRequestResponseFactory(compression, multipartOptions, maxBodySize, preserveRawBody);
28
49
  this.server = createNodeServer(this.httpsOptions, (request, response) => {
@@ -84,7 +105,7 @@ function createNodeRequestResponseFactory(compression, multipartOptions, maxBody
84
105
  return createRequestSignal(response);
85
106
  },
86
107
  createResponse(response, request) {
87
- return createFrameworkResponse(response, compression ? createNodeResponseCompression(response, request.headers['accept-encoding']) : undefined);
108
+ return createFrameworkResponse(response, compression ? () => createNodeResponseCompression(response, request.headers['accept-encoding']) : undefined);
88
109
  },
89
110
  resolveRequestId(request) {
90
111
  return resolveRequestIdFromHeaders(request.headers);
@@ -97,12 +118,37 @@ function createNodeRequestResponseFactory(compression, multipartOptions, maxBody
97
118
  }
98
119
  };
99
120
  }
121
+
122
+ /**
123
+ * Create node http adapter.
124
+ *
125
+ * @param options The options.
126
+ * @param compression The compression.
127
+ * @param multipartOptions The multipart options.
128
+ * @returns The create node http adapter result.
129
+ */
100
130
  export function createNodeHttpAdapter(options = {}, compression = false, multipartOptions) {
101
- return new NodeHttpApplicationAdapter(resolveNodePort(options.port), options.host, options.retryDelayMs, options.retryLimit, compression, options.https, multipartOptions, options.maxBodySize, options.rawBody, options.shutdownTimeoutMs);
131
+ return new NodeHttpApplicationAdapter(resolveNodePort(options.port), options.host, options.retryDelayMs, options.retryLimit, compression, options.https, multipartOptions, resolveNodeMaxBodySize(options.maxBodySize), options.rawBody, options.shutdownTimeoutMs);
102
132
  }
133
+
134
+ /**
135
+ * Bootstrap node application.
136
+ *
137
+ * @param rootModule The root module.
138
+ * @param options The options.
139
+ * @returns The bootstrap node application result.
140
+ */
103
141
  export async function bootstrapNodeApplication(rootModule, options) {
104
142
  return bootstrapHttpAdapterApplication(rootModule, options, createNodeHttpAdapter(options, options.compression ?? false, options.multipart));
105
143
  }
144
+
145
+ /**
146
+ * Run node application.
147
+ *
148
+ * @param rootModule The root module.
149
+ * @param options The options.
150
+ * @returns The run node application result.
151
+ */
106
152
  export async function runNodeApplication(rootModule, options) {
107
153
  const adapter = createNodeHttpAdapter(options, options.compression ?? false, options.multipart);
108
154
  return runHttpAdapterApplication(rootModule, {
@@ -209,4 +255,11 @@ function resolveNodePort(value) {
209
255
  throw new Error(`Invalid PORT value: ${String(value ?? 3000)}.`);
210
256
  }
211
257
  return port;
258
+ }
259
+ function resolveNodeMaxBodySize(value) {
260
+ const maxBodySize = value ?? 1 * 1024 * 1024;
261
+ if (!Number.isInteger(maxBodySize) || maxBodySize < 0) {
262
+ throw new Error(`Invalid maxBodySize value: ${String(value ?? 1 * 1024 * 1024)}. Expected a non-negative integer number of bytes.`);
263
+ }
264
+ return maxBodySize;
212
265
  }
@@ -1,17 +1,45 @@
1
+ /**
2
+ * Defines the request abort context type.
3
+ */
1
4
  export type RequestAbortContext = {
2
5
  controller: AbortController;
3
6
  cleanup(): void;
4
7
  signal: AbortSignal;
5
8
  };
9
+ /**
10
+ * Defines the active request transaction type.
11
+ */
6
12
  export type ActiveRequestTransaction = {
7
13
  abort(reason?: unknown): void;
8
14
  settled: Promise<void>;
9
15
  };
16
+ /**
17
+ * Defines the active request transaction handle type.
18
+ */
10
19
  export type ActiveRequestTransactionHandle = {
11
20
  active: ActiveRequestTransaction;
12
21
  settle(): void;
13
22
  };
23
+ /**
24
+ * Create request abort context.
25
+ *
26
+ * @param signal The signal.
27
+ * @returns The create request abort context result.
28
+ */
14
29
  export declare function createRequestAbortContext(signal?: AbortSignal): RequestAbortContext;
30
+ /**
31
+ * Track active request transaction.
32
+ *
33
+ * @param activeRequestTransactions The active request transactions.
34
+ * @param controller The controller.
35
+ * @returns The track active request transaction result.
36
+ */
15
37
  export declare function trackActiveRequestTransaction(activeRequestTransactions: Set<ActiveRequestTransaction>, controller: AbortController): ActiveRequestTransactionHandle;
38
+ /**
39
+ * Untrack active request transaction.
40
+ *
41
+ * @param activeRequestTransactions The active request transactions.
42
+ * @param handle The handle.
43
+ */
16
44
  export declare function untrackActiveRequestTransaction(activeRequestTransactions: Set<ActiveRequestTransaction>, handle: ActiveRequestTransactionHandle): void;
17
45
  //# sourceMappingURL=request-transaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"request-transaction.d.ts","sourceRoot":"","sources":["../src/request-transaction.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,mBAAmB,CAiBnF;AAED,wBAAgB,6BAA6B,CAC3C,yBAAyB,EAAE,GAAG,CAAC,wBAAwB,CAAC,EACxD,UAAU,EAAE,eAAe,GAC1B,8BAA8B,CAgBhC;AAED,wBAAgB,+BAA+B,CAC7C,yBAAyB,EAAE,GAAG,CAAC,wBAAwB,CAAC,EACxD,MAAM,EAAE,8BAA8B,GACrC,IAAI,CAGN"}
1
+ {"version":3,"file":"request-transaction.d.ts","sourceRoot":"","sources":["../src/request-transaction.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,mBAAmB,CAiBnF;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,yBAAyB,EAAE,GAAG,CAAC,wBAAwB,CAAC,EACxD,UAAU,EAAE,eAAe,GAC1B,8BAA8B,CAgBhC;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,yBAAyB,EAAE,GAAG,CAAC,wBAAwB,CAAC,EACxD,MAAM,EAAE,8BAA8B,GACrC,IAAI,CAGN"}
@@ -1,3 +1,21 @@
1
+ /**
2
+ * Defines the request abort context type.
3
+ */
4
+
5
+ /**
6
+ * Defines the active request transaction type.
7
+ */
8
+
9
+ /**
10
+ * Defines the active request transaction handle type.
11
+ */
12
+
13
+ /**
14
+ * Create request abort context.
15
+ *
16
+ * @param signal The signal.
17
+ * @returns The create request abort context result.
18
+ */
1
19
  export function createRequestAbortContext(signal) {
2
20
  const controller = new AbortController();
3
21
  const forwardAbort = () => controller.abort(signal?.reason);
@@ -16,6 +34,14 @@ export function createRequestAbortContext(signal) {
16
34
  signal: controller.signal
17
35
  };
18
36
  }
37
+
38
+ /**
39
+ * Track active request transaction.
40
+ *
41
+ * @param activeRequestTransactions The active request transactions.
42
+ * @param controller The controller.
43
+ * @returns The track active request transaction result.
44
+ */
19
45
  export function trackActiveRequestTransaction(activeRequestTransactions, controller) {
20
46
  let settle;
21
47
  const settled = new Promise(resolve => {
@@ -33,6 +59,13 @@ export function trackActiveRequestTransaction(activeRequestTransactions, control
33
59
  settle
34
60
  };
35
61
  }
62
+
63
+ /**
64
+ * Untrack active request transaction.
65
+ *
66
+ * @param activeRequestTransactions The active request transactions.
67
+ * @param handle The handle.
68
+ */
36
69
  export function untrackActiveRequestTransaction(activeRequestTransactions, handle) {
37
70
  activeRequestTransactions.delete(handle.active);
38
71
  handle.settle();
package/dist/web.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,wCAAwC,CAAC;AAEhD,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB;CACF;AAOD;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,sCAAsC;IACvF,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,UAAU,IAAI,QAAQ,CAAC;CACxB;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAwLhD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,sCAA2C,GACnD,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,oBAAoB,CAAC,CA6BhF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,UAAU,EACV,yBAAiG,EACjG,OAAO,EACP,GAAG,OAAO,EACX,EAAE,yBAAyB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAU/C;AAED;;;;;;;;;GASG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAwB,EACnC,eAAe,UAAQ,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAW3B"}
1
+ {"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAMtB,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,wCAAwC,CAAC;AAEhD,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,OAAO,CAAC,EAAE,UAAU,CAAC;KACtB;CACF;AAOD;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,sCAAsC;IACvF,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,UAAU,IAAI,QAAQ,CAAC;CACxB;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAiNhD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,sCAA2C,GACnD,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,EAAE,oBAAoB,CAAC,CA6BhF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,UAAU,EACV,yBAAiG,EACjG,OAAO,EACP,GAAG,OAAO,EACX,EAAE,yBAAyB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAU/C;AAED;;;;;;;;;GASG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,WAAW,EACnB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,WAAW,SAAwB,EACnC,eAAe,UAAQ,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAW3B"}
package/dist/web.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { BadRequestException, createErrorResponse, HttpException, InternalServerErrorException, PayloadTooLargeException } from '@fluojs/http';
2
+ import { attachFrameworkRequestNativeRouteHandoff, consumeRawRequestNativeRouteHandoff } from '@fluojs/http/internal';
2
3
  import { parseMultipart } from './multipart.js';
3
4
  import { dispatchWithRequestResponseFactory } from './adapters/request-response-factory.js';
4
5
  const DEFAULT_MAX_BODY_SIZE = 1 * 1024 * 1024;
@@ -93,12 +94,12 @@ class MutableWebFrameworkResponse {
93
94
  statusCode;
94
95
  statusSet;
95
96
  finalizedResponse;
96
- responseStream = new WebResponseStream(() => {
97
- this.streamActive = true;
98
- });
97
+ responseStream;
99
98
  responseBody;
100
99
  streamActive = false;
101
- stream = this.responseStream;
100
+ get stream() {
101
+ return this.getOrCreateResponseStream();
102
+ }
102
103
  redirect(status, location) {
103
104
  this.setStatus(status);
104
105
  this.setHeader('Location', location);
@@ -116,6 +117,17 @@ class MutableWebFrameworkResponse {
116
117
  this.responseBody = serialized.payload;
117
118
  this.committed = true;
118
119
  }
120
+ async sendSimpleJson(body) {
121
+ if (this.finalizedResponse) {
122
+ this.committed = true;
123
+ return;
124
+ }
125
+ if (!hasHeader(this.headers, 'content-type')) {
126
+ this.setHeader('Content-Type', 'application/json; charset=utf-8');
127
+ }
128
+ this.responseBody = JSON.stringify(body);
129
+ this.committed = true;
130
+ }
119
131
  setHeader(name, value) {
120
132
  const existingHeaderName = findHeaderName(this.headers, name) ?? name;
121
133
  if (name.toLowerCase() === 'set-cookie') {
@@ -135,12 +147,19 @@ class MutableWebFrameworkResponse {
135
147
  status: this.statusCode ?? 200
136
148
  };
137
149
  const responseBody = this.responseBody instanceof Uint8Array ? this.responseBody.slice().buffer : this.responseBody;
138
- this.finalizedResponse = this.streamActive ? new Response(this.responseStream.readable, init) : new Response(responseBody ?? '', init);
150
+ const nativeResponseBody = isResponseBodyForbidden(init.status) ? undefined : responseBody ?? '';
151
+ this.finalizedResponse = this.streamActive ? new Response(this.getOrCreateResponseStream().readable, init) : new Response(nativeResponseBody, init);
139
152
  this.raw = this.finalizedResponse;
140
153
  this.committed = true;
141
154
  }
142
155
  return this.finalizedResponse;
143
156
  }
157
+ getOrCreateResponseStream() {
158
+ this.responseStream ??= new WebResponseStream(() => {
159
+ this.streamActive = true;
160
+ });
161
+ return this.responseStream;
162
+ }
144
163
  }
145
164
 
146
165
  /**
@@ -249,6 +268,11 @@ function createDeferredWebFrameworkRequest(request, signal, multipartOptions, ma
249
268
  frameworkRequest.files = result.files;
250
269
  return;
251
270
  }
271
+ validateWebRequestContentLength(request, maxBodySize);
272
+ if (!request.body) {
273
+ frameworkRequest.body = undefined;
274
+ return;
275
+ }
252
276
  const bodyResult = await readWebRequestBody(request.clone(), contentType, maxBodySize, preserveRawBody);
253
277
  frameworkRequest.body = bodyResult.body;
254
278
  if (bodyResult.rawBody) {
@@ -273,7 +297,18 @@ function createDeferredWebFrameworkRequest(request, signal, multipartOptions, ma
273
297
  url: url.pathname + url.search,
274
298
  materializeBody
275
299
  };
276
- return frameworkRequest;
300
+ const nativeRouteHandoff = consumeRawRequestNativeRouteHandoff(request);
301
+ return nativeRouteHandoff ? attachFrameworkRequestNativeRouteHandoff(frameworkRequest, nativeRouteHandoff) : frameworkRequest;
302
+ }
303
+ function validateWebRequestContentLength(request, maxBodySize) {
304
+ const contentLength = request.headers.get('content-length');
305
+ if (contentLength === null) {
306
+ return;
307
+ }
308
+ const parsedContentLength = Number(contentLength);
309
+ if (Number.isFinite(parsedContentLength) && parsedContentLength > maxBodySize) {
310
+ throw new PayloadTooLargeException(REQUEST_BODY_LIMIT_MESSAGE);
311
+ }
277
312
  }
278
313
 
279
314
  /**
@@ -347,13 +382,7 @@ function parseCookieHeader(cookieHeader) {
347
382
  }));
348
383
  }
349
384
  async function readWebRequestBody(request, contentType, maxBodySize = DEFAULT_MAX_BODY_SIZE, preserveRawBody = false) {
350
- const contentLength = request.headers.get('content-length');
351
- if (contentLength !== null) {
352
- const parsedContentLength = Number(contentLength);
353
- if (Number.isFinite(parsedContentLength) && parsedContentLength > maxBodySize) {
354
- throw new PayloadTooLargeException(REQUEST_BODY_LIMIT_MESSAGE);
355
- }
356
- }
385
+ validateWebRequestContentLength(request, maxBodySize);
357
386
  if (!request.body) {
358
387
  return {
359
388
  body: undefined
@@ -437,6 +466,9 @@ function findHeaderName(headers, name) {
437
466
  function hasHeader(headers, name) {
438
467
  return findHeaderName(headers, name) !== undefined;
439
468
  }
469
+ function isResponseBodyForbidden(status) {
470
+ return status === 204 || status === 205 || status === 304;
471
+ }
440
472
  function toResponseHeaders(headers) {
441
473
  const responseHeaders = new Headers();
442
474
  for (const [name, value] of Object.entries(headers)) {
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "module-graph",
10
10
  "orchestration"
11
11
  ],
12
- "version": "1.0.0-beta.3",
12
+ "version": "1.0.0-beta.5",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -69,8 +69,8 @@
69
69
  "dependencies": {
70
70
  "@fluojs/config": "^1.0.0-beta.3",
71
71
  "@fluojs/core": "^1.0.0-beta.2",
72
- "@fluojs/di": "^1.0.0-beta.3",
73
- "@fluojs/http": "^1.0.0-beta.2"
72
+ "@fluojs/di": "^1.0.0-beta.5",
73
+ "@fluojs/http": "^1.0.0-beta.4"
74
74
  },
75
75
  "devDependencies": {
76
76
  "vitest": "^3.2.4",