@fluojs/platform-nodejs 1.0.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.ko.md +45 -10
  2. package/README.md +45 -10
  3. package/dist/index.d.ts +11 -56
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +10 -55
  6. package/dist/internal.d.ts +3 -0
  7. package/dist/internal.d.ts.map +1 -0
  8. package/dist/internal.js +2 -0
  9. package/dist/node/internal-node-compression.d.ts +22 -0
  10. package/dist/node/internal-node-compression.d.ts.map +1 -0
  11. package/dist/node/internal-node-compression.js +121 -0
  12. package/dist/node/internal-node-early-hints.d.ts +11 -0
  13. package/dist/node/internal-node-early-hints.d.ts.map +1 -0
  14. package/dist/node/internal-node-early-hints.js +99 -0
  15. package/dist/node/internal-node-listen.d.ts +24 -0
  16. package/dist/node/internal-node-listen.d.ts.map +1 -0
  17. package/dist/node/internal-node-listen.js +167 -0
  18. package/dist/node/internal-node-request.d.ts +163 -0
  19. package/dist/node/internal-node-request.d.ts.map +1 -0
  20. package/dist/node/internal-node-request.js +498 -0
  21. package/dist/node/internal-node-response-stream.d.ts +10 -0
  22. package/dist/node/internal-node-response-stream.d.ts.map +1 -0
  23. package/dist/node/internal-node-response-stream.js +69 -0
  24. package/dist/node/internal-node-response.d.ts +28 -0
  25. package/dist/node/internal-node-response.d.ts.map +1 -0
  26. package/dist/node/internal-node-response.js +181 -0
  27. package/dist/node/internal-node-shutdown.d.ts +34 -0
  28. package/dist/node/internal-node-shutdown.d.ts.map +1 -0
  29. package/dist/node/internal-node-shutdown.js +83 -0
  30. package/dist/node/internal-node.d.ts +114 -0
  31. package/dist/node/internal-node.d.ts.map +1 -0
  32. package/dist/node/internal-node.js +262 -0
  33. package/dist/node/json-logger.d.ts +8 -0
  34. package/dist/node/json-logger.d.ts.map +1 -0
  35. package/dist/node/json-logger.js +45 -0
  36. package/dist/node/logger.d.ts +37 -0
  37. package/dist/node/logger.d.ts.map +1 -0
  38. package/dist/node/logger.js +103 -0
  39. package/dist/node/node-compression.d.ts +2 -0
  40. package/dist/node/node-compression.d.ts.map +1 -0
  41. package/dist/node/node-compression.js +1 -0
  42. package/dist/node/node-request.d.ts +2 -0
  43. package/dist/node/node-request.d.ts.map +1 -0
  44. package/dist/node/node-request.js +1 -0
  45. package/dist/node/node-response.d.ts +2 -0
  46. package/dist/node/node-response.d.ts.map +1 -0
  47. package/dist/node/node-response.js +1 -0
  48. package/dist/node/node-shutdown.d.ts +2 -0
  49. package/dist/node/node-shutdown.d.ts.map +1 -0
  50. package/dist/node/node-shutdown.js +1 -0
  51. package/dist/node/node-static-assets.d.ts +26 -0
  52. package/dist/node/node-static-assets.d.ts.map +1 -0
  53. package/dist/node/node-static-assets.js +244 -0
  54. package/dist/node/node.d.ts +2 -0
  55. package/dist/node/node.d.ts.map +1 -0
  56. package/dist/node/node.js +1 -0
  57. package/package.json +17 -6
  58. package/dist/testing-http-adapter-portability.d.js +0 -0
package/README.ko.md CHANGED
@@ -4,9 +4,12 @@
4
4
 
5
5
  fluo 런타임을 위한 raw Node.js HTTP 어댑터 패키지입니다.
6
6
 
7
+ Coordinated Node 24 릴리스를 준비한다면 패키지 업그레이드 전에 [소비자 마이그레이션 가이드](../../docs/getting-started/migrate-node24.ko.md)를 따르세요.
8
+
7
9
  ## 목차
8
10
 
9
11
  - [설치](#설치)
12
+ - [Runtime Node import 마이그레이션](#runtime-node-import-마이그레이션)
10
13
  - [사용 시점](#사용-시점)
11
14
  - [빠른 시작](#빠른-시작)
12
15
  - [주요 패턴](#주요-패턴)
@@ -22,6 +25,19 @@ fluo 런타임을 위한 raw Node.js HTTP 어댑터 패키지입니다.
22
25
  npm install @fluojs/platform-nodejs
23
26
  ```
24
27
 
28
+ 이 패키지는 Node.js `>=24.0.0 <27`을 대상으로 하며 package manifest는 같은 `engines.node` 범위를 선언합니다. Node 24 LTS floor는 지원 정책 결정이며 RFC `QUERY` listener 동작은 이 지원 범위에서 계속 검증합니다. 이 패키지는 raw Node, Express, Fastify host가 사용하는 Node listener, filesystem, logger, compression, process-signal 구현을 소유합니다.
29
+
30
+ ## Runtime Node import 마이그레이션
31
+
32
+ 이전 mixed-runtime entrypoint에는 compatibility shim이 없습니다. Import를 직접 바꾸면 이동한 symbol 이름은 유지됩니다.
33
+
34
+ | 제거된 import | 대체 import |
35
+ | :--- | :--- |
36
+ | `@fluojs/runtime/node` | `@fluojs/platform-nodejs` |
37
+ | `@fluojs/runtime/internal-node` | `@fluojs/platform-nodejs/internal` |
38
+
39
+ Root replacement는 `createNodeHttpAdapter`, `NodeHttpApplicationAdapter`, `bootstrapNodeApplication`, `runNodeApplication`, Node logger factory, shutdown registration helper, `createNodeFileSystemAssetSource`를 포함합니다. 기존 `createNodejsAdapter`, `bootstrapNodejsApplication`, `runNodejsApplication` alias도 유지됩니다.
40
+
25
41
  ## 사용 시점
26
42
 
27
43
  Express나 Fastify와 같은 중간 프레임워크의 오버헤드 없이 Node.js 내장 `http` 또는 `https` 모듈에서 직접 fluo 애플리케이션을 실행하려는 경우에 사용합니다. 최소한의 리소스 사용, 저수준 최적화 또는 표준 Node API가 선호되는 환경에 이상적입니다.
@@ -42,20 +58,26 @@ await app.listen();
42
58
 
43
59
  ## 주요 패턴
44
60
 
61
+ ### Early Hints
62
+
63
+ Raw Node response는 `context.response.earlyHints`를 노출합니다. 이 optional capability를 확인한 뒤 HTTP `103` 하나마다 `write(...)`를 await하세요. 여러 write를 지원합니다. 각 write에는 비어 있지 않은 `link` value가 필요하며 Node가 허용하는 다른 informational field도 포함할 수 있습니다. Native write는 final response를 commit하거나 early field를 final header로 복사하지 않습니다. Late/native failure는 `EarlyHintsWriteError`로 reject되고 settlement 전에 연결이 끊기면 `RequestAbortedError`로 reject됩니다.
64
+
45
65
  ### 서버 옵션 커스텀
46
- 어댑터는 문서화된 Node.js transport 옵션인 host/port 바인딩, HTTPS 설정, request body 제한, raw-body 보존, listen retry 설정, shutdown drain bound를 제공합니다.
66
+ 어댑터는 문서화된 Node.js transport 옵션인 host/port 바인딩, plain HTTP 또는 HTTPS 생성 설정, request body 제한, raw-body 보존, listen retry 설정, shutdown drain bound를 제공합니다.
47
67
 
48
68
  ```typescript
49
69
  const adapter = createNodejsAdapter({
50
- port: 443,
51
- https: {
52
- key: fs.readFileSync('key.pem'),
53
- cert: fs.readFileSync('cert.pem'),
70
+ port: 3000,
71
+ http: {
72
+ maxHeaderSize: 16_384,
73
+ joinDuplicateHeaders: true,
54
74
  },
55
75
  maxBodySize: 1_048_576,
56
76
  });
57
77
  ```
58
78
 
79
+ `http`는 Node의 `node:http` `ServerOptions`를 받고 listener가 시작되기 전에 `createServer(options, handler)`로 전달합니다. `maxHeaderSize`, `insecureHTTPParser`, `joinDuplicateHeaders`, `highWaterMark` 같은 생성 시점 설정에 사용하세요. TLS에는 Node HTTPS server option이 담긴 `https`를 대신 제공하세요. `http`와 `https`는 동시에 사용할 수 없으며, 둘 다 제공하면 adapter가 server를 만들기 전에 throw하므로 어느 option도 조용히 무시되지 않습니다.
80
+
59
81
  `maxBodySize`는 바이트 수를 나타내는 숫자만 받습니다. 이 값은 raw Node 요청 바디가 아직 스트리밍되는 동안 바로 강제되며, 부트스트랩 시 `multipart.maxTotalSize`를 따로 재정의하지 않으면 같은 값이 멀티파트 전체 페이로드 한도의 기본값으로도 사용됩니다.
60
82
 
61
83
  `createNodejsAdapter()`는 기본 port로 `3000`을 사용하고 `process.env.PORT`를 무시하며, `port`, `maxBodySize`, `retryDelayMs`, `retryLimit`, adapter-level `shutdownTimeoutMs`가 잘못되면 throw합니다. 기본 request body cap은 `1 MiB`입니다.
@@ -88,17 +110,20 @@ await app.listen();
88
110
  ## 동작 계약
89
111
 
90
112
  - `createNodejsAdapter(options)`는 Node 내장 `http` 또는 `https` 서버 primitive 위에서 fluo를 직접 실행하는 adapter-first 진입점입니다.
113
+ - `http`는 plain HTTP server 생성용 Node `node:http` `ServerOptions`를 받고, `https`는 기존 TLS 생성 option을 유지합니다. 호출자는 두 field 중 하나만 제공해야 합니다.
91
114
  - `maxBodySize`는 0 이상의 정수 바이트 수만 받으며, raw Node 요청 바이트가 아직 스트리밍되는 동안 강제되고, 부트스트랩/실행 헬퍼에서 `multipart.maxTotalSize`를 명시적으로 제공하지 않으면 멀티파트 전체 크기 한도의 기본값이 됩니다.
92
115
  - Raw Node adapter는 대소문자가 섞인 JSON 및 multipart `content-type` 값을 normalize하고, request body가 `maxBodySize`를 넘으면 `413`을 반환하며, `x-request-id`와 `x-correlation-id` fallback을 request context와 error response에 전파하고, `getServer()` / `getRealtimeCapability()`를 통해 server-backed realtime capability를 노출합니다.
93
116
  - `bootstrapNodejsApplication(module, options)`는 raw Node 어댑터가 포함된 애플리케이션을 만들지만 리스닝은 시작하지 않으므로 이후 `app.listen()`과 `app.close()` 생명주기는 호출자가 소유합니다.
94
117
  - `runNodejsApplication(module, options)`는 부트스트랩, 리스닝 시작, graceful shutdown 배선을 함께 수행합니다. Listen retry는 `retryLimit`/`retryDelayMs`를 따르고, shutdown은 bounded drain 전에 idle keep-alive connection을 닫으며, 시그널 기반 종료가 타임아웃되거나 실패하면 해당 상태를 로그와 `process.exitCode`로 보고합니다. 최종 프로세스 종료는 호스트 프로세스가 계속 소유합니다.
95
- - 고급 압축 shutdown 유틸리티 함수는 기본 platform startup surface가 아니라 `@fluojs/runtime/node` 또는 runtime 내부 seam에 남아 있습니다.
118
+ - 지원되는 Node logger, shutdown, filesystem, raw adapter helper는 package root에 있고 저수준 request/response/compression plumbing은 `@fluojs/platform-nodejs/internal`에 있습니다.
96
119
 
97
120
  ## Conformance 커버리지
98
121
 
99
- `packages/platform-nodejs/src/index.test.ts`는 문서화된 Node.js 계약을 위한 package-local regression target입니다. 파일은 공유 `createHttpAdapterPortabilityHarness(...)` 검사를 실행하여 malformed cookie 보존, JSON/text raw-body capture, byte-exact raw-body capture, multipart raw-body 제외, multipart 전체 크기 기본값, SSE framing, response stream drain settlement, host 및 HTTPS startup logging, shutdown signal listener cleanup을 검증합니다.
122
+ `packages/platform-nodejs/src/index.test.ts`, `packages/platform-nodejs/src/lifecycle.test.ts`, `packages/platform-nodejs/src/lifecycle.integration.test.ts`는 문서화된 Node.js 계약을 위한 package-local regression target입니다. Adapter portability suite는 공유 `createHttpAdapterPortabilityHarness(...)` 검사를 실행하여 malformed cookie 보존, JSON/text raw-body capture, byte-exact raw-body capture, 단일 byte-range status/header/body semantic, multipart raw-body 제외, multipart 전체 크기 기본값, SSE framing, response stream drain settlement, host 및 HTTPS startup logging, shutdown signal listener cleanup을 검증합니다.
123
+
124
+ 이 패키지는 `HttpApplicationAdapter`를 노출하며 `platform.components`에 등록되는 runtime-managed `PlatformComponent`가 아닙니다. 따라서 generic `createPlatformConformanceHarness(...)` component lifecycle 검사는 이 패키지의 지원 계약 범위에 포함되지 않고, `createHttpAdapterPortabilityHarness(...)`가 적용되는 공유 harness입니다.
100
125
 
101
- 같은 파일은 package-specific public surface, type alias, adapter-first startup, lifecycle option validation, listen retry behavior, idle keep-alive shutdown, `maxBodySize` failure, 대소문자가 섞인 JSON 및 multipart content-type parsing, `x-correlation-id` request ID fallback, server-backed realtime capability 노출도 함께 다룹니다. Startup behavior를 바꿀 때는 README 예제 포인터를 아래 테스트 파일 및 Node.js 챕터 예제와 맞춰 유지하세요.
126
+ 같은 regression target들은 package-specific public surface, type alias, adapter-first startup, plain HTTP 생성 option과 HTTPS conflict boundary, lifecycle option validation, 실제로 관찰되는 listen retry, active-request bounded drain, 정상 및 실패 signal-driven shutdown, `process.env.PORT` isolation, zero/default `maxBodySize` boundary, idle keep-alive shutdown, 대소문자가 섞인 JSON 및 multipart content-type parsing, `x-correlation-id` request ID fallback, server-backed realtime capability 노출도 함께 다룹니다. Startup behavior를 바꿀 때는 README 예제 포인터를 아래 테스트 파일 및 Node.js 챕터 예제와 맞춰 유지하세요.
102
127
 
103
128
  ## 공개 API 개요
104
129
 
@@ -106,10 +131,18 @@ await app.listen();
106
131
  - `bootstrapNodejsApplication(module, options)`: 리스너를 시작하지 않고 애플리케이션 인스턴스를 생성합니다.
107
132
  - `runNodejsApplication(module, options)`: 생명주기 관리를 포함하여 애플리케이션을 부트스트랩하고 시작합니다.
108
133
  - `BootstrapNodejsApplicationOptions`: bootstrap-only Node.js 애플리케이션 생성 옵션입니다.
109
- - `NodejsAdapterOptions`: `port`, `host`, `https`, `maxBodySize`, retry 설정, raw body 보존, shutdown timeout을 포함하는 `createNodejsAdapter(...)`의 transport-level 옵션입니다.
134
+ - `NodejsAdapterOptions`: `port`, `host`, 상호 배타적인 `http` 또는 `https` 생성 option, `maxBodySize`, retry 설정, raw body 보존, shutdown timeout을 포함하는 `createNodejsAdapter(...)`의 transport-level 옵션입니다.
110
135
  - `NodejsApplicationSignal`: `runNodejsApplication(...)` shutdown 등록이 지원하는 시그널 이름입니다.
111
- - `NodejsHttpApplicationAdapter`: `createNodejsAdapter(...)`가 반환하는 어댑터 인스턴스를 설명하는 타입 전용 별칭이며, `@fluojs/runtime/node`가 공개하는 어댑터 surface를 그대로 보존합니다.
136
+ - `NodejsHttpApplicationAdapter`: `createNodejsAdapter(...)`가 반환하는 어댑터 인스턴스를 설명하는 타입 전용 별칭이며, `@fluojs/platform-nodejs`가 공개하는 어댑터 surface를 그대로 보존합니다.
112
137
  - `RunNodejsApplicationOptions`: 부트스트랩, 리스닝 시작, graceful shutdown 배선을 한 번에 수행하기 위한 옵션입니다.
138
+ - 이전 `@fluojs/runtime/node` export는 이 package root에서 이름을 유지합니다. `createNodeHttpAdapter`, `NodeHttpApplicationAdapter`, `bootstrapNodeApplication`, `runNodeApplication`, `createConsoleApplicationLogger`, `createJsonApplicationLogger`, `createNodeShutdownSignalRegistration`, `defaultNodeShutdownSignals`, `registerShutdownSignals`, `createNodeFileSystemAssetSource` 및 public option type이 포함됩니다.
139
+ - `@fluojs/platform-nodejs/internal`: `@fluojs/runtime/internal-node`를 대체하는 first-party Node adapter integration seam이며 저수준 compression 및 request/response helper를 포함합니다.
140
+
141
+ ## Multipart 스트리밍
142
+
143
+ 애플리케이션 생성 시 `multipart: { strategy: 'stream' }`을 설정하면 multipart part가 `RequestContext.request.body`의 `AsyncIterable`로 노출됩니다. Node listener는 iterator를 미리 읽거나 버퍼링하지 않으며, file part를 소비할 때만 바이트를 가져옵니다. 버퍼링 multipart parsing은 기본값이며 fields와 `request.files`를 노출하고, 하나의 request body에서 stream 소비와 함께 사용할 수 없습니다.
144
+
145
+ Runtime route dispatch는 route를 위해 만든 iterator를 소유하며 handler가 끝난 뒤 자동으로 `return()`을 호출해 active source를 cancel하고 release합니다. Standalone `parseMultipartStream(...)` consumer는 이 책임을 직접 집니다. iterator를 끝까지 소비하거나 일찍 끝낼 때 `return()`을 호출하세요.
113
146
 
114
147
  ## 관련 패키지
115
148
 
@@ -120,4 +153,6 @@ await app.listen();
120
153
  ## 예제 소스
121
154
 
122
155
  - `packages/platform-nodejs/src/index.test.ts`
156
+ - `packages/platform-nodejs/src/lifecycle.test.ts`
157
+ - `packages/platform-nodejs/src/lifecycle.integration.test.ts`
123
158
  - `book/intermediate/ch21-express-node.ko.md`
package/README.md CHANGED
@@ -4,9 +4,12 @@
4
4
 
5
5
  Raw Node.js HTTP adapter package for the fluo runtime.
6
6
 
7
+ Preparing for the coordinated Node 24 release? Follow the [consumer migration guide](../../docs/getting-started/migrate-node24.md) before upgrading packages.
8
+
7
9
  ## Table of Contents
8
10
 
9
11
  - [Installation](#installation)
12
+ - [Runtime Node Import Migration](#runtime-node-import-migration)
10
13
  - [When to Use](#when-to-use)
11
14
  - [Quick Start](#quick-start)
12
15
  - [Common Patterns](#common-patterns)
@@ -22,6 +25,19 @@ Raw Node.js HTTP adapter package for the fluo runtime.
22
25
  npm install @fluojs/platform-nodejs
23
26
  ```
24
27
 
28
+ This package targets Node.js `>=24.0.0 <27` and declares that exact `engines.node` range. The Node 24 LTS floor is a support-policy decision; listener-level RFC `QUERY` remains verified on the supported runtimes. The package owns the Node listener, filesystem, logger, compression, and process-signal implementations used by raw Node, Express, and Fastify hosts.
29
+
30
+ ## Runtime Node Import Migration
31
+
32
+ The former mixed-runtime entrypoints have no compatibility shim. Update imports directly; moved symbols retain their names:
33
+
34
+ | Removed import | Replacement |
35
+ | :--- | :--- |
36
+ | `@fluojs/runtime/node` | `@fluojs/platform-nodejs` |
37
+ | `@fluojs/runtime/internal-node` | `@fluojs/platform-nodejs/internal` |
38
+
39
+ The root replacement includes `createNodeHttpAdapter`, `NodeHttpApplicationAdapter`, `bootstrapNodeApplication`, `runNodeApplication`, Node logger factories, shutdown registration helpers, and `createNodeFileSystemAssetSource`. Existing `createNodejsAdapter`, `bootstrapNodejsApplication`, and `runNodejsApplication` aliases remain available.
40
+
25
41
  ## When to Use
26
42
 
27
43
  Use this package when you want to run a fluo application directly on the Node.js built-in `http` or `https` modules without the overhead of an intermediate framework like Express or Fastify. It is ideal for minimal footprints, custom low-level optimizations, or environments where standard Node APIs are preferred.
@@ -42,20 +58,26 @@ await app.listen();
42
58
 
43
59
  ## Common Patterns
44
60
 
61
+ ### Early Hints
62
+
63
+ Raw Node responses expose `context.response.earlyHints`. Check that optional capability, then await `write(...)` once per HTTP `103`; multiple writes are supported. Each write requires a non-empty `link` value and may include other Node-permitted informational fields. The native write does not commit the final response or copy early fields into final headers. Late/native failures reject with `EarlyHintsWriteError`, and a disconnect before settlement rejects with `RequestAbortedError`.
64
+
45
65
  ### Customizing Server Options
46
- The adapter exposes the documented Node.js transport options: host/port binding, HTTPS configuration, request body limits, raw-body preservation, listen retry settings, and shutdown drain bounds.
66
+ The adapter exposes the documented Node.js transport options: host/port binding, plain HTTP or HTTPS construction configuration, request body limits, raw-body preservation, listen retry settings, and shutdown drain bounds.
47
67
 
48
68
  ```typescript
49
69
  const adapter = createNodejsAdapter({
50
- port: 443,
51
- https: {
52
- key: fs.readFileSync('key.pem'),
53
- cert: fs.readFileSync('cert.pem'),
70
+ port: 3000,
71
+ http: {
72
+ maxHeaderSize: 16_384,
73
+ joinDuplicateHeaders: true,
54
74
  },
55
75
  maxBodySize: 1_048_576,
56
76
  });
57
77
  ```
58
78
 
79
+ `http` accepts Node's `node:http` `ServerOptions` and passes them to `createServer(options, handler)` before the listener starts. Use it for construction-time settings such as `maxHeaderSize`, `insecureHTTPParser`, `joinDuplicateHeaders`, or `highWaterMark`. For TLS, provide `https` with Node's HTTPS server options instead. `http` and `https` are mutually exclusive; supplying both throws before the adapter creates a server, so no option is silently ignored.
80
+
59
81
  `maxBodySize` accepts a byte count number. It is enforced while the raw Node request body is still streaming, and the same limit becomes the default total multipart payload cap unless you override `multipart.maxTotalSize` during bootstrap.
60
82
 
61
83
  `createNodejsAdapter()` defaults to port `3000`, ignores `process.env.PORT`, and throws when `port`, `maxBodySize`, `retryDelayMs`, `retryLimit`, or adapter-level `shutdownTimeoutMs` are invalid. The default request body cap is `1 MiB`.
@@ -88,17 +110,20 @@ await app.listen();
88
110
  ## Behavioral Contracts
89
111
 
90
112
  - `createNodejsAdapter(options)` is the adapter-first entrypoint for running fluo directly on Node's built-in `http` or `https` server primitives.
113
+ - `http` accepts Node `node:http` `ServerOptions` for plain HTTP server construction, while `https` keeps its existing TLS construction options; callers must supply at most one of those fields.
91
114
  - `maxBodySize` accepts a non-negative integer byte count, is enforced while raw Node request bytes are still streaming, and becomes the default multipart total-size cap unless `multipart.maxTotalSize` is explicitly provided through the bootstrap/run helpers.
92
115
  - The raw Node adapter normalizes mixed-case JSON and multipart `content-type` values, returns `413` when request bodies exceed `maxBodySize`, propagates `x-request-id` with `x-correlation-id` fallback into the request context and error responses, and exposes a server-backed realtime capability through `getServer()` / `getRealtimeCapability()`.
93
116
  - `bootstrapNodejsApplication(module, options)` creates an application with the raw Node adapter but does not start listening, so the caller owns the subsequent `app.listen()` and `app.close()` lifecycle.
94
117
  - `runNodejsApplication(module, options)` bootstraps, starts, and wires graceful shutdown. Listen retries honor `retryLimit`/`retryDelayMs`, shutdown closes idle keep-alive connections before bounded drain, and when signal-driven shutdown times out or fails it logs the condition and sets `process.exitCode`; final process termination remains owned by the host process.
95
- - Advanced compression and shutdown utility functions remain on `@fluojs/runtime/node` or internal runtime seams rather than this primary platform startup surface.
118
+ - Supported Node logger, shutdown, filesystem, and raw adapter helpers live on the package root; lower-level request/response/compression plumbing lives on `@fluojs/platform-nodejs/internal`.
96
119
 
97
120
  ## Conformance Coverage
98
121
 
99
- `packages/platform-nodejs/src/index.test.ts` is the package-local regression target for the documented Node.js contract. It runs the shared `createHttpAdapterPortabilityHarness(...)` checks for malformed cookie preservation, JSON/text raw-body capture, byte-exact raw-body capture, multipart raw-body exclusion, multipart total-size defaults, SSE framing, response stream drain settlement, host and HTTPS startup logging, and shutdown signal listener cleanup.
122
+ `packages/platform-nodejs/src/index.test.ts`, `packages/platform-nodejs/src/lifecycle.test.ts`, and `packages/platform-nodejs/src/lifecycle.integration.test.ts` are the package-local regression targets for the documented Node.js contract. The adapter portability suite runs the shared `createHttpAdapterPortabilityHarness(...)` checks for malformed cookie preservation, JSON/text raw-body capture, byte-exact raw-body capture, single byte-range status/header/body semantics, multipart raw-body exclusion, multipart total-size defaults, SSE framing, response stream drain settlement, host and HTTPS startup logging, and shutdown signal listener cleanup.
123
+
124
+ This package exposes an `HttpApplicationAdapter`; it is not a runtime-managed `PlatformComponent` registered under `platform.components`. Therefore the generic `createPlatformConformanceHarness(...)` component lifecycle checks are outside this package's supported contract, while `createHttpAdapterPortabilityHarness(...)` is the applicable shared harness.
100
125
 
101
- The same file also covers the package-specific public surface, type aliases, adapter-first startup, lifecycle option validation, listen retry behavior, idle keep-alive shutdown, `maxBodySize` failures, mixed-case JSON and multipart content-type parsing, `x-correlation-id` request ID fallback, and server-backed realtime capability exposure. Keep README example pointers aligned with that test file and the Node.js chapter examples below when changing startup behavior.
126
+ The same regression targets also cover the package-specific public surface, type aliases, adapter-first startup, plain HTTP construction options and their HTTPS conflict boundary, lifecycle option validation, observed listen retries, active-request bounded drain, normal and failed signal-driven shutdown, `process.env.PORT` isolation, zero and default `maxBodySize` boundaries, idle keep-alive shutdown, mixed-case JSON and multipart content-type parsing, `x-correlation-id` request ID fallback, and server-backed realtime capability exposure. Keep README example pointers aligned with those test files and the Node.js chapter examples below when changing startup behavior.
102
127
 
103
128
  ## Public API Overview
104
129
 
@@ -106,10 +131,18 @@ The same file also covers the package-specific public surface, type aliases, ada
106
131
  - `bootstrapNodejsApplication(module, options)`: Creates an application instance without starting the listener.
107
132
  - `runNodejsApplication(module, options)`: Bootstraps and starts the application with lifecycle management.
108
133
  - `BootstrapNodejsApplicationOptions`: Options for bootstrap-only Node.js application creation.
109
- - `NodejsAdapterOptions`: Transport-level options for `createNodejsAdapter(...)`, including `port`, `host`, `https`, `maxBodySize`, retry settings, raw body preservation, and shutdown timeout.
134
+ - `NodejsAdapterOptions`: Transport-level options for `createNodejsAdapter(...)`, including `port`, `host`, mutually exclusive `http` or `https` construction options, `maxBodySize`, retry settings, raw body preservation, and shutdown timeout.
110
135
  - `NodejsApplicationSignal`: Supported signal names for `runNodejsApplication(...)` shutdown registration.
111
- - `NodejsHttpApplicationAdapter`: Type-only alias describing the adapter instances returned by `createNodejsAdapter(...)`, while preserving the public adapter surface exported from `@fluojs/runtime/node`.
136
+ - `NodejsHttpApplicationAdapter`: Type-only alias describing the adapter instances returned by `createNodejsAdapter(...)`, while preserving the public adapter surface exported from `@fluojs/platform-nodejs`.
112
137
  - `RunNodejsApplicationOptions`: Options for one-call bootstrap, listen, and graceful shutdown wiring.
138
+ - Former `@fluojs/runtime/node` exports keep their names on this package root: `createNodeHttpAdapter`, `NodeHttpApplicationAdapter`, `bootstrapNodeApplication`, `runNodeApplication`, `createConsoleApplicationLogger`, `createJsonApplicationLogger`, `createNodeShutdownSignalRegistration`, `defaultNodeShutdownSignals`, `registerShutdownSignals`, and `createNodeFileSystemAssetSource` plus their public option types.
139
+ - `@fluojs/platform-nodejs/internal`: First-party Node adapter integration seam replacing `@fluojs/runtime/internal-node`; it includes lower-level compression and request/response helpers.
140
+
141
+ ## Multipart streaming
142
+
143
+ Set `multipart: { strategy: 'stream' }` when creating the application to expose multipart parts through `RequestContext.request.body` as an `AsyncIterable`. The Node listener creates the iterator without pre-reading or buffering it; consuming a file part pulls its bytes on demand. Buffered multipart parsing remains the default, exposes fields and `request.files`, and cannot be combined with stream consumption for the same request body.
144
+
145
+ Runtime route dispatch owns an iterator created for a route and automatically calls `return()` after the handler finishes, cancelling and releasing an active source. Standalone `parseMultipartStream(...)` consumers own that responsibility: consume the iterator to completion or call `return()` when ending early.
113
146
 
114
147
  ## Related Packages
115
148
 
@@ -120,4 +153,6 @@ The same file also covers the package-specific public surface, type aliases, ada
120
153
  ## Example Sources
121
154
 
122
155
  - `packages/platform-nodejs/src/index.test.ts`
156
+ - `packages/platform-nodejs/src/lifecycle.test.ts`
157
+ - `packages/platform-nodejs/src/lifecycle.integration.test.ts`
123
158
  - `book/intermediate/ch21-express-node.md`
package/dist/index.d.ts CHANGED
@@ -1,52 +1,21 @@
1
- import { type BootstrapNodeApplicationOptions, bootstrapNodeApplication, type NodeApplicationSignal, type NodeHttpAdapterOptions, type NodeHttpApplicationAdapter, type RunNodeApplicationOptions, runNodeApplication } from '@fluojs/runtime/node';
2
- /**
3
- * Options accepted by `bootstrapNodejsApplication(...)` before the listener starts.
4
- *
5
- * @remarks
6
- * This type mirrors the supported Node application bootstrap options from `@fluojs/runtime/node`
7
- * while keeping the `@fluojs/platform-nodejs` public surface documented at its package boundary.
8
- */
1
+ import { type BootstrapNodeApplicationOptions, bootstrapNodeApplication, type NodeApplicationSignal, type NodeHttpAdapterOptions, type NodeHttpApplicationAdapter, type RunNodeApplicationOptions, runNodeApplication } from './node/internal-node.js';
2
+ export { type BootstrapNodeApplicationOptions, bootstrapNodeApplication, type CorsInput, createNodeHttpAdapter, createNodeShutdownSignalRegistration, defaultNodeShutdownSignals, type NodeApplicationSignal, type NodeHttpAdapterOptions, NodeHttpApplicationAdapter, type RunNodeApplicationOptions, registerShutdownSignals, runNodeApplication, } from './node/internal-node.js';
3
+ export * from './node/json-logger.js';
4
+ export * from './node/logger.js';
5
+ export { createNodeFileSystemAssetSource, type NodeFileSystemAssetPrecompression, type NodeFileSystemAssetSourceOptions, } from './node/node-static-assets.js';
6
+ /** Options accepted by `bootstrapNodejsApplication(...)` before the listener starts. */
9
7
  export type BootstrapNodejsApplicationOptions = BootstrapNodeApplicationOptions;
10
- /**
11
- * POSIX signals that `runNodejsApplication(...)` can subscribe to for graceful shutdown.
12
- *
13
- * @remarks
14
- * Pass `false` to `RunNodejsApplicationOptions.shutdownSignals` when the host process owns signal
15
- * registration and should call `app.close()` itself.
16
- */
8
+ /** POSIX signals that `runNodejsApplication(...)` can subscribe to for graceful shutdown. */
17
9
  export type NodejsApplicationSignal = NodeApplicationSignal;
18
- /**
19
- * Transport-level settings for the raw Node.js adapter factory.
20
- *
21
- * @remarks
22
- * `maxBodySize` is enforced while request bytes stream in and also seeds the multipart total-size
23
- * limit unless `bootstrapNodejsApplication(...)` or `runNodejsApplication(...)` provides an
24
- * explicit `multipart.maxTotalSize` value.
25
- */
10
+ /** Transport-level settings for the raw Node.js adapter factory. */
26
11
  export type NodejsAdapterOptions = NodeHttpAdapterOptions;
27
- /**
28
- * Adapter instance returned by `createNodejsAdapter(...)`.
29
- *
30
- * @remarks
31
- * The alias preserves the public `@fluojs/runtime/node` adapter contract, including access to the
32
- * underlying Node server via `getServer()` for server-backed realtime integrations.
33
- */
12
+ /** Adapter instance returned by `createNodejsAdapter(...)`. */
34
13
  export type NodejsHttpApplicationAdapter = NodeHttpApplicationAdapter;
35
- /**
36
- * Options accepted by `runNodejsApplication(...)` for one-call bootstrap, listen, and shutdown wiring.
37
- *
38
- * @remarks
39
- * Signal-driven shutdown logs timeout or failure conditions and sets `process.exitCode`, but final
40
- * process termination remains owned by the surrounding host runtime.
41
- */
14
+ /** Options accepted by `runNodejsApplication(...)` for one-call bootstrap, listen, and shutdown wiring. */
42
15
  export type RunNodejsApplicationOptions = RunNodeApplicationOptions;
43
16
  /**
44
17
  * Bootstrap a fluo module with the raw Node.js adapter without starting the listener.
45
18
  *
46
- * @remarks
47
- * This alias keeps the package-level Node.js naming convention while delegating to the supported
48
- * `@fluojs/runtime/node` implementation.
49
- *
50
19
  * @param rootModule Root fluo module to bootstrap.
51
20
  * @param options Node.js bootstrap options applied before the listener starts.
52
21
  * @returns A fluo application instance whose listener is not started yet.
@@ -55,27 +24,13 @@ export declare const bootstrapNodejsApplication: typeof bootstrapNodeApplication
55
24
  /**
56
25
  * Create the raw Node.js HTTP adapter exposed by `@fluojs/platform-nodejs`.
57
26
  *
58
- * @remarks
59
- * Use this factory for adapter-first startup through `fluoFactory.create(...)` when the application
60
- * should run directly on Node's built-in `http` or `https` server primitives.
61
- *
62
27
  * @param options Transport-level Node.js settings such as port, retries, body-size limits, and HTTPS options.
63
28
  * @returns The Node.js HTTP adapter instance used by the Fluo runtime.
64
- *
65
- * @example
66
- * ```ts
67
- * const adapter = createNodejsAdapter({ port: 3000 });
68
- * ```
69
29
  */
70
- export declare function createNodejsAdapter(options?: NodeHttpAdapterOptions): NodeHttpApplicationAdapter;
30
+ export declare function createNodejsAdapter(options?: NodejsAdapterOptions): NodejsHttpApplicationAdapter;
71
31
  /**
72
32
  * Bootstrap and start a fluo module on the raw Node.js adapter with lifecycle shutdown wiring.
73
33
  *
74
- * @remarks
75
- * This alias is the zero-boilerplate package entrypoint for Node.js hosts. It preserves the runtime
76
- * helper behavior: graceful shutdown is bounded and reported, while final process exit remains under
77
- * host ownership.
78
- *
79
34
  * @param rootModule Root fluo module to bootstrap and start.
80
35
  * @param options Node.js run options, including optional shutdown signal ownership.
81
36
  * @returns A started fluo application instance.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,+BAA+B,EACpC,wBAAwB,EAExB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAE9B;;;;;;GAMG;AACH,MAAM,MAAM,iCAAiC,GAAG,+BAA+B,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAAG,qBAAqB,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,MAAM,4BAA4B,GAAG,0BAA0B,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,MAAM,2BAA2B,GAAG,yBAAyB,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,EAAE,OAAO,wBAAmD,CAAC;AAEpG;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,sBAA2B,GACnC,0BAA0B,CAE5B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,EAAE,OAAO,kBAAuC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,+BAA+B,EACpC,wBAAwB,EAExB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,KAAK,+BAA+B,EACpC,wBAAwB,EACxB,KAAK,SAAS,EACd,qBAAqB,EACrB,oCAAoC,EACpC,0BAA0B,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,0BAA0B,EAC1B,KAAK,yBAAyB,EAC9B,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EACL,+BAA+B,EAC/B,KAAK,iCAAiC,EACtC,KAAK,gCAAgC,GACtC,MAAM,8BAA8B,CAAC;AAEtC,wFAAwF;AACxF,MAAM,MAAM,iCAAiC,GAAG,+BAA+B,CAAC;AAEhF,6FAA6F;AAC7F,MAAM,MAAM,uBAAuB,GAAG,qBAAqB,CAAC;AAE5D,oEAAoE;AACpE,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE1D,+DAA+D;AAC/D,MAAM,MAAM,4BAA4B,GAAG,0BAA0B,CAAC;AAEtE,2GAA2G;AAC3G,MAAM,MAAM,2BAA2B,GAAG,yBAAyB,CAAC;AAEpE;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,EAAE,OAAO,wBAAmD,CAAC;AAEpG;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,oBAAyB,GACjC,4BAA4B,CAE9B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,OAAO,kBAAuC,CAAC"}
package/dist/index.js CHANGED
@@ -1,53 +1,22 @@
1
- import { bootstrapNodeApplication, createNodeHttpAdapter, runNodeApplication } from '@fluojs/runtime/node';
1
+ import { bootstrapNodeApplication, createNodeHttpAdapter, runNodeApplication } from './node/internal-node.js';
2
+ export { bootstrapNodeApplication, createNodeHttpAdapter, createNodeShutdownSignalRegistration, defaultNodeShutdownSignals, NodeHttpApplicationAdapter, registerShutdownSignals, runNodeApplication } from './node/internal-node.js';
3
+ export * from './node/json-logger.js';
4
+ export * from './node/logger.js';
5
+ export { createNodeFileSystemAssetSource } from './node/node-static-assets.js';
2
6
 
3
- /**
4
- * Options accepted by `bootstrapNodejsApplication(...)` before the listener starts.
5
- *
6
- * @remarks
7
- * This type mirrors the supported Node application bootstrap options from `@fluojs/runtime/node`
8
- * while keeping the `@fluojs/platform-nodejs` public surface documented at its package boundary.
9
- */
7
+ /** Options accepted by `bootstrapNodejsApplication(...)` before the listener starts. */
10
8
 
11
- /**
12
- * POSIX signals that `runNodejsApplication(...)` can subscribe to for graceful shutdown.
13
- *
14
- * @remarks
15
- * Pass `false` to `RunNodejsApplicationOptions.shutdownSignals` when the host process owns signal
16
- * registration and should call `app.close()` itself.
17
- */
9
+ /** POSIX signals that `runNodejsApplication(...)` can subscribe to for graceful shutdown. */
18
10
 
19
- /**
20
- * Transport-level settings for the raw Node.js adapter factory.
21
- *
22
- * @remarks
23
- * `maxBodySize` is enforced while request bytes stream in and also seeds the multipart total-size
24
- * limit unless `bootstrapNodejsApplication(...)` or `runNodejsApplication(...)` provides an
25
- * explicit `multipart.maxTotalSize` value.
26
- */
11
+ /** Transport-level settings for the raw Node.js adapter factory. */
27
12
 
28
- /**
29
- * Adapter instance returned by `createNodejsAdapter(...)`.
30
- *
31
- * @remarks
32
- * The alias preserves the public `@fluojs/runtime/node` adapter contract, including access to the
33
- * underlying Node server via `getServer()` for server-backed realtime integrations.
34
- */
13
+ /** Adapter instance returned by `createNodejsAdapter(...)`. */
35
14
 
36
- /**
37
- * Options accepted by `runNodejsApplication(...)` for one-call bootstrap, listen, and shutdown wiring.
38
- *
39
- * @remarks
40
- * Signal-driven shutdown logs timeout or failure conditions and sets `process.exitCode`, but final
41
- * process termination remains owned by the surrounding host runtime.
42
- */
15
+ /** Options accepted by `runNodejsApplication(...)` for one-call bootstrap, listen, and shutdown wiring. */
43
16
 
44
17
  /**
45
18
  * Bootstrap a fluo module with the raw Node.js adapter without starting the listener.
46
19
  *
47
- * @remarks
48
- * This alias keeps the package-level Node.js naming convention while delegating to the supported
49
- * `@fluojs/runtime/node` implementation.
50
- *
51
20
  * @param rootModule Root fluo module to bootstrap.
52
21
  * @param options Node.js bootstrap options applied before the listener starts.
53
22
  * @returns A fluo application instance whose listener is not started yet.
@@ -57,17 +26,8 @@ export const bootstrapNodejsApplication = bootstrapNodeApplication;
57
26
  /**
58
27
  * Create the raw Node.js HTTP adapter exposed by `@fluojs/platform-nodejs`.
59
28
  *
60
- * @remarks
61
- * Use this factory for adapter-first startup through `fluoFactory.create(...)` when the application
62
- * should run directly on Node's built-in `http` or `https` server primitives.
63
- *
64
29
  * @param options Transport-level Node.js settings such as port, retries, body-size limits, and HTTPS options.
65
30
  * @returns The Node.js HTTP adapter instance used by the Fluo runtime.
66
- *
67
- * @example
68
- * ```ts
69
- * const adapter = createNodejsAdapter({ port: 3000 });
70
- * ```
71
31
  */
72
32
  export function createNodejsAdapter(options = {}) {
73
33
  return createNodeHttpAdapter(options);
@@ -76,11 +36,6 @@ export function createNodejsAdapter(options = {}) {
76
36
  /**
77
37
  * Bootstrap and start a fluo module on the raw Node.js adapter with lifecycle shutdown wiring.
78
38
  *
79
- * @remarks
80
- * This alias is the zero-boilerplate package entrypoint for Node.js hosts. It preserves the runtime
81
- * helper behavior: graceful shutdown is bounded and reported, while final process exit remains under
82
- * host ownership.
83
- *
84
39
  * @param rootModule Root fluo module to bootstrap and start.
85
40
  * @param options Node.js run options, including optional shutdown signal ownership.
86
41
  * @returns A started fluo application instance.
@@ -0,0 +1,3 @@
1
+ export * from './node/internal-node.js';
2
+ export { createNodeEarlyHintsCapability } from './node/internal-node-early-hints.js';
3
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './node/internal-node.js';
2
+ export { createNodeEarlyHintsCapability } from './node/internal-node-early-hints.js';
@@ -0,0 +1,22 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ import type { FrameworkResponseCompression } from '@fluojs/http';
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
+ */
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
+ */
20
+ export declare function compressNodeResponse(response: ServerResponse, body: Uint8Array, encoding: Exclude<Encoding, 'identity'>): Promise<void>;
21
+ export {};
22
+ //# sourceMappingURL=internal-node-compression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-node-compression.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-compression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,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,CAqDf"}
@@ -0,0 +1,121 @@
1
+ import { createBrotliCompress, createGzip } from 'node:zlib';
2
+ const COMPRESS_THRESHOLD = 1024;
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
+ */
11
+ export function createNodeResponseCompression(response, acceptEncoding) {
12
+ if (!acceptEncoding) {
13
+ return undefined;
14
+ }
15
+ return {
16
+ async write(body, options = {}) {
17
+ if (body.byteLength < COMPRESS_THRESHOLD || shouldSkipContentType(options.contentType)) {
18
+ return false;
19
+ }
20
+ const encoding = selectEncoding(acceptEncoding);
21
+ if (encoding === 'identity') {
22
+ return false;
23
+ }
24
+ await compressNodeResponse(response, body, encoding);
25
+ return true;
26
+ }
27
+ };
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
+ */
38
+ export function compressNodeResponse(response, body, encoding) {
39
+ if (response.destroyed || response.socket?.destroyed) {
40
+ return Promise.reject(new Error('Node response closed before compression completed.'));
41
+ }
42
+ const stream = encoding === 'br' ? createBrotliCompress() : createGzip();
43
+ response.setHeader('Content-Encoding', encoding);
44
+ response.removeHeader('Content-Length');
45
+ return new Promise((resolve, reject) => {
46
+ let settled = false;
47
+ const cleanup = () => {
48
+ response.removeListener('close', rejectClosedResponse);
49
+ response.removeListener('error', rejectFailure);
50
+ response.removeListener('finish', resolveResponse);
51
+ stream.removeListener('error', rejectFailure);
52
+ };
53
+ const settle = (action, terminateStream = false) => {
54
+ if (settled) {
55
+ return;
56
+ }
57
+ settled = true;
58
+ if (terminateStream) {
59
+ stream.unpipe(response);
60
+ stream.destroy();
61
+ }
62
+ cleanup();
63
+ action();
64
+ };
65
+ const rejectFailure = error => {
66
+ settle(() => reject(error), true);
67
+ };
68
+ const rejectClosedResponse = () => {
69
+ settle(() => reject(new Error('Node response closed before compression completed.')), true);
70
+ };
71
+ const resolveResponse = () => {
72
+ if (response.destroyed || !response.writableEnded) {
73
+ rejectClosedResponse();
74
+ return;
75
+ }
76
+ settle(resolve);
77
+ };
78
+ response.once('close', rejectClosedResponse);
79
+ response.once('error', rejectFailure);
80
+ response.once('finish', resolveResponse);
81
+ stream.once('error', rejectFailure);
82
+ stream.pipe(response);
83
+ stream.end(body);
84
+ });
85
+ }
86
+ function shouldSkipContentType(contentType) {
87
+ if (!contentType) {
88
+ return false;
89
+ }
90
+ const lower = contentType.toLowerCase();
91
+ for (const prefix of SKIP_CONTENT_TYPES) {
92
+ if (lower.startsWith(prefix)) {
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+ function selectEncoding(acceptEncoding) {
99
+ if (!acceptEncoding) {
100
+ return 'identity';
101
+ }
102
+ const entries = acceptEncoding.split(',').map(entry => {
103
+ const [enc, qPart] = entry.trim().split(';');
104
+ const q = qPart ? parseFloat(qPart.replace('q=', '')) : 1;
105
+ return {
106
+ enc: enc?.trim() ?? '',
107
+ q: Number.isNaN(q) ? 1 : q
108
+ };
109
+ }).filter(entry => entry.q > 0).sort((a, b) => b.q - a.q);
110
+ for (const {
111
+ enc
112
+ } of entries) {
113
+ if (enc === 'br') {
114
+ return 'br';
115
+ }
116
+ if (enc === 'gzip') {
117
+ return 'gzip';
118
+ }
119
+ }
120
+ return 'identity';
121
+ }
@@ -0,0 +1,11 @@
1
+ import { type ServerResponse } from 'node:http';
2
+ import { type FrameworkResponseEarlyHints } from '@fluojs/http';
3
+ /**
4
+ * Create the request-scoped Early Hints writer shared by Node-backed adapters.
5
+ *
6
+ * @param response Native Node response that emits HTTP 103 informational responses.
7
+ * @param isCommitted Probe for facade-level final response ownership.
8
+ * @returns An Early Hints capability that settles on native write, error, or disconnect.
9
+ */
10
+ export declare function createNodeEarlyHintsCapability(response: ServerResponse, isCommitted: () => boolean): FrameworkResponseEarlyHints;
11
+ //# sourceMappingURL=internal-node-early-hints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-node-early-hints.d.ts","sourceRoot":"","sources":["../../src/node/internal-node-early-hints.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAGL,KAAK,2BAA2B,EAEjC,MAAM,cAAc,CAAC;AAEtB;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,MAAM,OAAO,GACzB,2BAA2B,CA2E7B"}