@fluojs/platform-deno 1.0.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md CHANGED
@@ -43,33 +43,75 @@ await runDenoApplication(AppModule, {
43
43
  });
44
44
  ```
45
45
 
46
+ Managed startup 경로는 network listener를 열고 기본적으로 `SIGINT`/`SIGTERM` listener를 등록합니다. Network 접근 권한을 부여해 entrypoint를 실행하세요.
47
+
48
+ ```bash
49
+ deno run --allow-net main.ts
50
+ ```
51
+
52
+ Signal listener 등록에는 별도의 Deno permission이 필요하지 않습니다. Adapter 자체는 environment variable을 읽지 않습니다. 애플리케이션 코드가 해당 key를 읽을 때만 `--allow-env=PORT,DATABASE_URL`처럼 범위를 제한한 권한을 추가하세요. Signal로 트리거된 애플리케이션 close 실패는 helper가 log한 뒤 swallow하며 exit status를 설정하지 않습니다. Failure-status propagation 또는 forced termination이 필요한 host는 `runDenoApplication(...)`에 `shutdownSignals: false`를 전달하고 signal과 shutdown을 직접 조율해야 합니다.
53
+
46
54
  ## 주요 패턴
47
55
 
48
- ### 수동 요청 디스패칭
49
- 테스트나 커스텀 `Deno.serve` 구현을 위해 어댑터의 `handle` 메서드를 사용하여 네이티브 웹 요청을 수동으로 디스패치할 수 있습니다. 다만 `handle(...)`은 런타임 dispatcher가 바인딩된 뒤에만 동작하므로, 먼저 `app.listen()`(또는 `runDenoApplication(...)`)으로 부트스트랩을 완료해야 합니다.
56
+ ### Early Hints 미지원
57
+
58
+ Deno의 Fetch `Response`는 final response 이전 informational response를 표현할 수 없으므로 `context.response.earlyHints`가 없습니다. 사용 전에 capability 존재 여부를 확인하세요. Adapter는 요청된 `103`을 silent no-op 또는 final-response header로 바꾸지 않습니다.
59
+
60
+ ### 스트리밍 멀티파트 소비
61
+
62
+ 애플리케이션 bootstrap에서 `multipart: { strategy: 'stream' }`을 설정하면 멀티파트 데이터를 점진적으로
63
+ 받습니다. 멀티파트 route에서 `RequestContext.request.body`는 `AsyncIterableIterator<MultipartPart>`입니다.
64
+ field part는 `kind: 'field'`, `name`, `value`, `headers`를, file part는 `kind: 'file'`, `name`, `filename`,
65
+ `contentType`, `headers`, 그리고 `stream`의 single-consumer `ReadableStream<Uint8Array>`를 제공합니다. 다음
66
+ part를 요청하기 전에 각 file stream을 끝까지 소비하거나 cancel하세요.
67
+
68
+ Runtime route dispatch는 route를 위해 만든 iterator를 소유하며 handler가 끝난 뒤 자동으로 `return()`을 호출해
69
+ active source를 cancel하고 release합니다. Standalone `parseMultipartStream(...)` consumer는 이 책임을 직접
70
+ 집니다. iterator를 끝까지 소비하거나 일찍 끝낼 때 `return()`을 호출하세요.
71
+
72
+ ### Host-Owned Deno.serve
73
+ 애플리케이션이 `Deno.serve(...)`를 소유한다면 `app.listen()`을 호출하지 않고 fluo 애플리케이션을 bootstrap한 뒤 public dispatcher로 request handler를 만드세요. `createDenoFetchHandler(...)`는 request 변환과 dispatch만 수행하며 server를 시작하거나 shutdown, signal, websocket upgrade를 소유하지 않습니다.
50
74
 
51
75
  ```typescript
52
- import { createDenoAdapter } from '@fluojs/platform-deno';
76
+ import { createDenoAdapter, createDenoFetchHandler } from '@fluojs/platform-deno';
53
77
  import { fluoFactory } from '@fluojs/runtime';
54
78
 
55
- const adapter = createDenoAdapter({ port: 3000 });
79
+ const adapter = createDenoAdapter();
56
80
  const app = await fluoFactory.create(AppModule, { adapter });
81
+ const handler = createDenoFetchHandler({
82
+ dispatcher: app.dispatcher,
83
+ rawBody: true,
84
+ });
57
85
 
58
- await app.listen();
86
+ const server = Deno.serve({ port: 3000 }, handler);
59
87
 
60
- const response = await adapter.handle(new Request('http://localhost:3000/health'));
88
+ try {
89
+ await server.finished;
90
+ } finally {
91
+ await app.close();
92
+ }
61
93
  ```
62
94
 
63
- ### Deno 네이티브 WebSocket 지원
95
+ 주변 host는 `server` 중지와 process signal 조율을 소유하고 websocket upgrade를 별도로 처리할지 결정해야 합니다. Fluo가 managed server lifecycle을 소유해야 한다면 `app.listen()`을 사용하고, shutdown signal listener까지 추가로 설치해야 한다면 `runDenoApplication(...)`을 사용하세요. `adapter.handle(...)`은 managed adapter의 `listen(dispatcher)` binding이 끝난 뒤에만 사용할 수 있습니다.
96
+
97
+ ### 직접 어댑터 생성
98
+ 애플리케이션 코드는 보통 `createDenoAdapter(options)`, `bootstrapDenoApplication(...)`, `runDenoApplication(...)`을 사용해 adapter setup 경계를 명확히 유지하는 편을 권장합니다. 커스텀 orchestration이나 테스트에서는 `new DenoHttpApplicationAdapter(options?)`를 직접 사용할 수 있으며, constructor는 factory와 같은 optional public `DenoAdapterOptions`를 받고 options가 생략되면 기본 port를 적용하며, portable `host` alias보다 `hostname`을 우선하고, 잘못된 `port` 또는 `maxBodySize` 값은 setup 시점에 거절합니다.
99
+
100
+ ### Opt-in Deno WebSocket 바인딩
64
101
  어댑터는 애플리케이션이 `@fluojs/websockets/deno` 바인딩을 import하고 설정한 뒤에 Deno의 네이티브 `Deno.upgradeWebSocket`을 지원합니다. 해당 바인딩이 없으면 websocket upgrade 요청은 암묵적으로 upgrade되지 않고 일반 HTTP dispatch 경로를 계속 따릅니다.
65
102
 
66
103
  ```typescript
67
104
  import { Module } from '@fluojs/core';
68
- import { WebSocketGateway } from '@fluojs/websockets';
69
- import { DenoWebSocketModule } from '@fluojs/websockets/deno';
105
+ import { DenoWebSocketModule, OnMessage, WebSocketGateway } from '@fluojs/websockets/deno';
106
+ import type { DenoServerWebSocket } from '@fluojs/websockets/deno';
70
107
 
71
108
  @WebSocketGateway({ path: '/ws' })
72
- export class MyGateway {}
109
+ export class MyGateway {
110
+ @OnMessage('ping')
111
+ handlePing(_payload: unknown, socket: DenoServerWebSocket) {
112
+ socket.send(JSON.stringify({ event: 'pong', data: 'hello from deno' }));
113
+ }
114
+ }
73
115
 
74
116
  @Module({
75
117
  imports: [DenoWebSocketModule.forRoot()],
@@ -78,6 +120,14 @@ export class MyGateway {}
78
120
  export class RealtimeModule {}
79
121
  ```
80
122
 
123
+ #### Deno websocket 수신 payload
124
+
125
+ `DenoWebSocketMessage`는 Deno binding이 전달할 수 있는 모든 payload form,
126
+ `ArrayBuffer | ArrayBufferView | Blob | string`을 나타냅니다. 기존에 이 public
127
+ union을 `Blob | string`으로 exhaustive narrowing한 handler는 `ArrayBuffer`와
128
+ `ArrayBufferView` branch를 추가해야 합니다. `@fluojs/websockets/deno` binding은
129
+ 이미 gateway handler를 dispatch하기 전에 이 binary payload를 수용하고 normalize합니다.
130
+
81
131
  ## HTTPS와 런타임 이식성
82
132
 
83
133
  `https` 옵션으로 Deno TLS 인증서 자료를 전달하면 `Deno.serve`를 HTTPS 모드로 시작할 수 있습니다. 어댑터는 `https.cert`와 `https.key`를 Deno의 `cert` 및 `key`로 전달하며, 시작 로그도 `https://` listen URL을 보고하므로 Deno 패키지가 공유 HTTP 어댑터 이식성 계약과 정렬됩니다.
@@ -95,23 +145,33 @@ await runDenoApplication(AppModule, {
95
145
 
96
146
  `hostname`은 Deno 네이티브 옵션 이름으로 유지됩니다. 공유 HTTP 어댑터 테스트와 교차 런타임 설정 헬퍼를 위해 `host`도 이식성 alias로 허용하며, 둘 다 제공하면 `Deno.serve(...)` bind target과 보고되는 listen URL에는 `hostname`이 우선합니다.
97
147
 
98
- Advanced option에는 test 또는 non-hosted runtime을 위한 injectable `serve`, `upgradeWebSocket` seam, `rawBody`, `maxBodySize`, `multipart`, `shutdownSignals`가 포함됩니다. Seam을 주입하지 않으면 어댑터는 listen/upgrade 시점에 `globalThis.Deno.serve`와 `globalThis.Deno.upgradeWebSocket`으로 fallback합니다. `runDenoApplication(...)`은 기본적으로 `SIGINT`/`SIGTERM`을 연결하고, `shutdownSignals: false`는 signal registration을 끄며, 여러 signal을 등록하다 실패하면 이미 연결한 listener를 rollback합니다. Close는 Deno serve signal을 abort하기 전에 active request drain을 최대 10초 기다립니다. `handle(...)`은 `listen()`이 dispatcher를 bind하기 전에는 JSON `500`, shutdown 진행 중에는 JSON `503`을 반환합니다.
148
+ Advanced option에는 test 또는 non-hosted runtime을 위한 injectable `serve`, `upgradeWebSocket` seam, `rawBody`, `maxBodySize`, `multipart`, `shutdownSignals`가 포함됩니다. `createDenoFetchHandler(...)`는 같은 request parsing option을 받으며 byte-exact JSON/text raw body를 보존하고 multipart request에서는 `rawBody`를 제외합니다. Seam을 주입하지 않으면 managed adapter는 listen/upgrade 시점에 `globalThis.Deno.serve`와 `globalThis.Deno.upgradeWebSocket`으로 fallback합니다. `runDenoApplication(...)`은 기본적으로 `SIGINT`/`SIGTERM`을 연결하고, `shutdownSignals: false`는 signal registration을 끄며, 여러 signal을 등록하다 실패하면 이미 연결한 listener를 rollback합니다. 이미 실행 중인 adapter에 대한 중복 `listen(...)` 호출은 원래 dispatcher pipeline을 보존하는 no-op입니다. Close는 Deno serve signal을 abort하기 전에 active request drain을 최대 10초 기다립니다. Graceful shutdown이 reject되면 adapter는 ingress를 abort하지만 `server.finished`가 settle될 때까지 active server controller를 유지하고, 그 뒤에만 managed lifecycle을 해제한 다음 원래 shutdown error를 다시 throw합니다. `handle(...)`은 websocket upgrade 요청을 포함해 `listen()`이 dispatcher를 bind하기 전에는 JSON `500`, shutdown 진행 중에는 JSON `503`을 반환합니다.
99
149
 
100
150
  ## Conformance 커버리지
101
151
 
102
- `packages/platform-deno/src/adapter.test.ts`는 문서화된 Deno 계약을 검증하는 package-local regression 대상입니다. 이 파일은 shared Web dispatch delegation, HTTPS startup forwarding, `host` alias 및 `hostname` 우선순위 listen-target handling, 기본 `SIGINT`/`SIGTERM` signal listener 등록, `shutdownSignals: false`, partial signal-registration failure 이후 listener rollback, websocket upgrade binding 및 no-binding HTTP fallback, global Deno serve/upgrade fallback seam, listen 전 `500` 처리, shutdown 중 `503` 처리, serve-signal abort 전 in-flight request drain, bounded 10초 close timeout을 검증합니다.
152
+ `packages/platform-deno/src/adapter.test.ts`는 managed Deno 계약을 검증하는 package-local regression 대상입니다. 이 파일은 shared Web dispatch delegation, `listen(dispatcher)` 이후 direct `adapter.handle(...)` success-path dispatch, 직접 constructor/factory option normalization, HTTPS startup forwarding, `Deno.serve(...)` bind target과 startup log에 대한 `host` alias 및 `hostname` 우선순위, 중복 `listen(...)` no-op dispatcher 보존, 기본 `SIGINT`/`SIGTERM` signal listener 등록, `shutdownSignals: false`, partial signal-registration failure 이후 listener rollback, websocket upgrade binding 및 no-binding HTTP fallback, websocket listen 전 bootstrap gating, global Deno serve/upgrade fallback seam, listen 전 `500` 처리, shutdown 중 `503` 처리, serve-signal abort 전 in-flight request drain, `server.finished`까지 shutdown failure ownership 유지, bounded 10초 close timeout을 검증합니다. `packages/platform-deno/src/fetch-handler.test.ts`는 host-owned handler에 shared web-runtime portability harness를 적용하여 cookie/query decoding, JSON/text와 byte-exact raw body, multipart exclusion, SSE framing, dispatch가 `Deno.serve(...)`를 호출하지 않는다는 사실을 검증합니다. `packages/platform-deno/src/declaration-surface.test.ts`는 package를 다시 build하고 manifest가 export하는 declaration을 검증합니다.
153
+
154
+ 공유 edge portability suite인 `packages/testing/src/portability/web-runtime-adapter-portability.test.ts`는 Deno를 Bun 및 Cloudflare Workers와 함께 실행해 malformed cookie 보존, query decoding, JSON/text raw-body capture, 단일 byte-range status/header/body semantic, multipart raw-body 제외, SSE framing을 검증합니다. 패키지 테스트의 README parity assertion은 이 edge-runtime 커버리지 문서가 한국어 mirror와 계속 동기화되도록 확인합니다.
103
155
 
104
- 공유 edge portability suite인 `packages/testing/src/portability/web-runtime-adapter-portability.test.ts`는 DenoBun Cloudflare Workers와 함께 실행해 malformed cookie 보존, query decoding, JSON/text raw-body capture, multipart raw-body 제외, SSE framing을 검증합니다. 패키지 테스트의 README parity assertion은edge-runtime 커버리지 문서가 한국어 mirror와 계속 동기화되도록 확인합니다.
156
+ Deno 2 smoke lane은 public `npm:@fluojs/platform-deno` root import검사하고 built adapter closure를 네이티브로 실행합니다. Listener 없이 host-owned `createDenoFetchHandler(...)` request를 dispatch한 뒤, port `0`과 `shutdownSignals: false`로 `runDenoApplication(...)`을 시작하고 실제 route를 fetch한 다음 애플리케이션을 닫습니다.managed-listener test에서는 signal ownership을 의도적으로 비활성화하며, signal registration은 별도의 package-local contract suite에서 다룹니다.
105
157
 
106
158
  ## 공개 API 개요
107
159
 
108
- - `createDenoAdapter(options)`: Deno HTTP 어댑터를 위한 팩토리입니다.
160
+ - `createDenoAdapter(options)`: Deno HTTP 어댑터를 위한 팩토리이며, 직접 생성과 같은 validation 및 normalization을 공유합니다.
161
+ - `createDenoFetchHandler(options)`: 이미 bootstrap된 `app.dispatcher`에서 `Deno.serve(...)`를 시작하거나 소유하지 않는 `Request` handler를 동기적으로 생성합니다.
109
162
  - `bootstrapDenoApplication(module, options)`: 커스텀 오케스트레이션을 위한 고급 부트스트랩입니다.
110
163
  - `runDenoApplication(module, options)`: Deno를 위한 권장 빠른 시작 헬퍼입니다.
111
- - `DenoHttpApplicationAdapter`: `handle(...)`, `getListenTarget()`, `getRealtimeCapability()`, `getServer()`, `configureWebSocketBinding(...)`를 제공하는 핵심 adapter 구현체입니다.
112
- - `handle(request)`: 수동 `Request` to `Response` 디스패처입니다.
164
+ - `DenoHttpApplicationAdapter(options?)`: 핵심 adapter 구현체입니다. Direct `new DenoHttpApplicationAdapter()` 또는 `new DenoHttpApplicationAdapter(options)`는 `createDenoAdapter(options)`와 같은 기본 port, `host` alias 처리, `hostname` 우선순위, 숫자 option validation을 적용합니다.
165
+ - `listen(dispatcher)`: fluo HTTP dispatcher를 bind하고 `Deno.serve`를 시작합니다. 중복 호출은 원래 dispatcher를 보존하는 no-op입니다.
166
+ - `close()`: 새 유입을 중단하고 active request를 최대 10초 drain한 뒤, shutdown이 끝나지 않으면 Deno serve signal을 abort합니다. Graceful shutdown이 reject되면 `server.finished`가 settle될 때까지 `getServer()` ownership을 유지하고, 이후 managed lifecycle을 해제한 다음 원래 shutdown error를 다시 throw합니다.
167
+ - `handle(request)`: 수동 `Request` to `Response` 디스패처입니다. `listen(dispatcher)`가 runtime dispatcher를 bind한 뒤에는 성공 경로를 실행하고, bind 전에는 JSON `500`, shutdown 중에는 JSON `503`을 반환합니다.
168
+ - `getListenTarget()`: Deno `hostname` 또는 portable `host` alias를 사용해 bind target과 public URL을 보고합니다.
169
+ - `getRealtimeCapability()`: runtime integration을 위한 fetch-style Deno websocket upgrade capability를 보고합니다.
170
+ - `getServer()`: adapter가 listen 중일 때 active `Deno.serve` controller를 반환합니다.
171
+ - `configureWebSocketBinding(...)`: `listen(dispatcher)`가 server를 시작하기 전에 `@fluojs/websockets/deno` binding을 설치합니다.
172
+ - `DenoWebSocketMessage`: 전체 수신 websocket payload union인 `ArrayBuffer | ArrayBufferView | Blob | string`입니다.
113
173
  - `https: { cert, key }`: `Deno.serve`로 전달되고 보고되는 listen URL에 반영되는 HTTPS 시작 옵션입니다.
114
- - Option 및 seam type: `DenoServeOptions`, `DenoServeController`, `DenoServerWebSocket`, websocket binding interface, bootstrap/run option, listen-target helper.
174
+ - Option 및 seam type: `CreateDenoFetchHandlerOptions`, `DenoServeOptions`, `DenoServeController`, `DenoServerWebSocket`, websocket binding interface, bootstrap/run option, listen-target helper.
115
175
 
116
176
  ## 관련 패키지
117
177
 
@@ -122,4 +182,5 @@ Advanced option에는 test 또는 non-hosted runtime을 위한 injectable `serve
122
182
  ## 예제 소스
123
183
 
124
184
  - `packages/platform-deno/src/adapter.test.ts`
185
+ - `packages/platform-deno/src/fetch-handler.test.ts`
125
186
  - `packages/websockets/src/deno/deno.test.ts`
package/README.md CHANGED
@@ -43,33 +43,75 @@ await runDenoApplication(AppModule, {
43
43
  });
44
44
  ```
45
45
 
46
+ The managed startup path opens a network listener and registers `SIGINT`/`SIGTERM` listeners by default. Run the entrypoint with network access:
47
+
48
+ ```bash
49
+ deno run --allow-net main.ts
50
+ ```
51
+
52
+ Signal listener registration does not require a separate Deno permission. The adapter does not read environment variables. Add a scoped grant such as `--allow-env=PORT,DATABASE_URL` only when application code reads those keys. Signal-triggered application-close failures are logged and swallowed by the helper; it does not set an exit status. Hosts that require failure-status propagation or forced termination must pass `shutdownSignals: false` to `runDenoApplication(...)` and coordinate signals and shutdown themselves.
53
+
46
54
  ## Common Patterns
47
55
 
48
- ### Manual Request Dispatching
49
- For testing or custom `Deno.serve` implementations, you can use the adapter's `handle` method to dispatch native web requests manually. Bind the dispatcher first via `app.listen()` (or `runDenoApplication(...)`), because `handle(...)` only works after the runtime has been bootstrapped.
56
+ ### Early Hints are unsupported
57
+
58
+ Deno's Fetch `Response` cannot represent an informational response before the final response, so `context.response.earlyHints` is absent. Check for capability presence before use. The adapter never turns a requested `103` into a silent no-op or a final-response header.
59
+
60
+ ### Streaming multipart consumption
61
+
62
+ Set `multipart: { strategy: 'stream' }` at application bootstrap to receive multipart data incrementally. For
63
+ multipart routes, `RequestContext.request.body` is an `AsyncIterableIterator<MultipartPart>`: field parts expose
64
+ `kind: 'field'`, `name`, `value`, and `headers`; file parts expose `kind: 'file'`, `name`, `filename`,
65
+ `contentType`, `headers`, and a single-consumer `ReadableStream<Uint8Array>` at `stream`. Finish or cancel each file
66
+ stream before requesting the next part.
67
+
68
+ Runtime route dispatch owns an iterator created for a route and automatically calls `return()` after the handler
69
+ finishes, cancelling and releasing an active source. Standalone `parseMultipartStream(...)` consumers own that
70
+ responsibility: consume the iterator to completion or call `return()` when ending early.
71
+
72
+ ### Host-Owned Deno.serve
73
+ If your application owns `Deno.serve(...)`, bootstrap the fluo application without calling `app.listen()` and create a request handler from its public dispatcher. `createDenoFetchHandler(...)` only translates and dispatches requests; it never starts a server or owns shutdown, signals, or websocket upgrades.
50
74
 
51
75
  ```typescript
52
- import { createDenoAdapter } from '@fluojs/platform-deno';
76
+ import { createDenoAdapter, createDenoFetchHandler } from '@fluojs/platform-deno';
53
77
  import { fluoFactory } from '@fluojs/runtime';
54
78
 
55
- const adapter = createDenoAdapter({ port: 3000 });
79
+ const adapter = createDenoAdapter();
56
80
  const app = await fluoFactory.create(AppModule, { adapter });
81
+ const handler = createDenoFetchHandler({
82
+ dispatcher: app.dispatcher,
83
+ rawBody: true,
84
+ });
57
85
 
58
- await app.listen();
86
+ const server = Deno.serve({ port: 3000 }, handler);
59
87
 
60
- const response = await adapter.handle(new Request('http://localhost:3000/health'));
88
+ try {
89
+ await server.finished;
90
+ } finally {
91
+ await app.close();
92
+ }
61
93
  ```
62
94
 
63
- ### Deno-Native WebSocket Support
95
+ The surrounding host must stop `server`, coordinate process signals, and decide whether websocket upgrades are handled separately. Use `app.listen()` when fluo should own the managed server lifecycle, or `runDenoApplication(...)` when fluo should additionally install shutdown signal listeners. `adapter.handle(...)` remains available only after the managed adapter's `listen(dispatcher)` binding has completed.
96
+
97
+ ### Direct Adapter Construction
98
+ Application code should usually prefer `createDenoAdapter(options)`, `bootstrapDenoApplication(...)`, or `runDenoApplication(...)` so adapter setup stays explicit. Custom orchestration and tests may use `new DenoHttpApplicationAdapter(options?)` directly; the constructor accepts the same optional public `DenoAdapterOptions` as the factory, applies the default port when options are omitted, preserves `hostname` over the portable `host` alias, and rejects invalid `port` or `maxBodySize` values during setup.
99
+
100
+ ### Opt-in Deno WebSocket Binding
64
101
  The adapter supports Deno's native `Deno.upgradeWebSocket` after the application imports and configures the `@fluojs/websockets/deno` binding. Without that binding, websocket upgrade requests continue through normal HTTP dispatch instead of being upgraded implicitly.
65
102
 
66
103
  ```typescript
67
104
  import { Module } from '@fluojs/core';
68
- import { WebSocketGateway } from '@fluojs/websockets';
69
- import { DenoWebSocketModule } from '@fluojs/websockets/deno';
105
+ import { DenoWebSocketModule, OnMessage, WebSocketGateway } from '@fluojs/websockets/deno';
106
+ import type { DenoServerWebSocket } from '@fluojs/websockets/deno';
70
107
 
71
108
  @WebSocketGateway({ path: '/ws' })
72
- export class MyGateway {}
109
+ export class MyGateway {
110
+ @OnMessage('ping')
111
+ handlePing(_payload: unknown, socket: DenoServerWebSocket) {
112
+ socket.send(JSON.stringify({ event: 'pong', data: 'hello from deno' }));
113
+ }
114
+ }
73
115
 
74
116
  @Module({
75
117
  imports: [DenoWebSocketModule.forRoot()],
@@ -78,6 +120,14 @@ export class MyGateway {}
78
120
  export class RealtimeModule {}
79
121
  ```
80
122
 
123
+ #### Deno websocket inbound payloads
124
+
125
+ `DenoWebSocketMessage` represents every payload form the Deno binding can deliver:
126
+ `ArrayBuffer | ArrayBufferView | Blob | string`. Existing handlers that narrowed this
127
+ public union exhaustively for `Blob | string` must add `ArrayBuffer` and
128
+ `ArrayBufferView` branches. The `@fluojs/websockets/deno` binding already accepts and
129
+ normalizes these binary payloads before it dispatches gateway handlers.
130
+
81
131
  ## HTTPS and Runtime Portability
82
132
 
83
133
  Pass Deno TLS certificate material through the `https` option to start `Deno.serve` in HTTPS mode. The adapter forwards `https.cert` and `https.key` to Deno as `cert` and `key`, and startup logging reports an `https://` listen URL so the Deno package stays aligned with the shared HTTP adapter portability contract.
@@ -95,23 +145,33 @@ await runDenoApplication(AppModule, {
95
145
 
96
146
  `hostname` remains the Deno-native option name. The adapter also accepts `host` as a portability alias for shared HTTP adapter tests and cross-runtime configuration helpers; when both are provided, `hostname` wins for the `Deno.serve(...)` bind target and reported listen URL.
97
147
 
98
- Advanced options include injectable `serve` and `upgradeWebSocket` seams for tests or non-hosted runtimes, `rawBody`, `maxBodySize`, `multipart`, and `shutdownSignals`. When a seam is not injected, the adapter falls back to `globalThis.Deno.serve` and `globalThis.Deno.upgradeWebSocket` at listen/upgrade time. `runDenoApplication(...)` wires `SIGINT`/`SIGTERM` by default, `shutdownSignals: false` disables signal registration, and failed multi-signal registration rolls back listeners that were already attached. Close waits up to 10 seconds for active requests to drain before aborting the Deno serve signal. `handle(...)` returns a JSON `500` before `listen()` binds the dispatcher and a JSON `503` while shutdown is in progress.
148
+ Advanced options include injectable `serve` and `upgradeWebSocket` seams for tests or non-hosted runtimes, `rawBody`, `maxBodySize`, `multipart`, and `shutdownSignals`. `createDenoFetchHandler(...)` accepts the same request parsing options and preserves byte-exact JSON/text raw bodies while excluding `rawBody` for multipart requests. When a seam is not injected, the managed adapter falls back to `globalThis.Deno.serve` and `globalThis.Deno.upgradeWebSocket` at listen/upgrade time. `runDenoApplication(...)` wires `SIGINT`/`SIGTERM` by default, `shutdownSignals: false` disables signal registration, and failed multi-signal registration rolls back listeners that were already attached. Duplicate `listen(...)` calls on an already-running adapter are no-ops that preserve the original dispatcher pipeline. Close waits up to 10 seconds for active requests to drain before aborting the Deno serve signal. If graceful shutdown rejects, the adapter aborts ingress but retains the active server controller until `server.finished` settles; only then does it release the managed lifecycle and rethrow the original shutdown error. `handle(...)` returns a JSON `500` before `listen()` binds the dispatcher, including websocket upgrade requests, and a JSON `503` while shutdown is in progress.
99
149
 
100
150
  ## Conformance Coverage
101
151
 
102
- `packages/platform-deno/src/adapter.test.ts` is the package-local regression target for the documented Deno contract. It covers shared Web dispatch delegation, HTTPS startup forwarding, `host` alias and `hostname` precedence listen-target handling, default `SIGINT`/`SIGTERM` signal listener registration, `shutdownSignals: false`, listener rollback after partial signal-registration failure, websocket upgrade binding and no-binding HTTP fallback, global Deno serve/upgrade fallback seams, pre-listen `500` handling, shutdown `503` handling, in-flight request drain before serve-signal abort, and the bounded 10-second close timeout.
152
+ `packages/platform-deno/src/adapter.test.ts` is the package-local regression target for the managed Deno contract. It covers shared Web dispatch delegation, direct `adapter.handle(...)` success-path dispatch after `listen(dispatcher)`, direct constructor/factory option normalization, HTTPS startup forwarding, `host` alias and `hostname` precedence for the `Deno.serve(...)` bind target and startup log, duplicate `listen(...)` no-op dispatcher preservation, default `SIGINT`/`SIGTERM` signal listener registration, `shutdownSignals: false`, listener rollback after partial signal-registration failure, websocket upgrade binding and no-binding HTTP fallback, websocket pre-listen bootstrap gating, global Deno serve/upgrade fallback seams, pre-listen `500` handling, shutdown `503` handling, in-flight request drain before serve-signal abort, shutdown-failure ownership until `server.finished`, and the bounded 10-second close timeout. `packages/platform-deno/src/fetch-handler.test.ts` applies the shared web-runtime portability harness to the host-owned handler, covering cookies/query decoding, JSON/text and byte-exact raw bodies, multipart exclusion, SSE framing, and proof that dispatch does not call `Deno.serve(...)`. `packages/platform-deno/src/declaration-surface.test.ts` rebuilds the package and verifies the manifest-exported declarations.
153
+
154
+ The shared edge portability suite in `packages/testing/src/portability/web-runtime-adapter-portability.test.ts` exercises Deno beside Bun and Cloudflare Workers for malformed cookie preservation, query decoding, JSON/text raw-body capture, single byte-range status/header/body semantics, multipart raw-body exclusion, and SSE framing. The README parity assertion in the package test keeps these documented edge-runtime coverage claims synchronized with the Korean mirror.
103
155
 
104
- The shared edge portability suite in `packages/testing/src/portability/web-runtime-adapter-portability.test.ts` exercises Deno beside Bun and Cloudflare Workers for malformed cookie preservation, query decoding, JSON/text raw-body capture, multipart raw-body exclusion, and SSE framing. The README parity assertion in the package test keeps these documented edge-runtime coverage claims synchronized with the Korean mirror.
156
+ The Deno 2 smoke lane checks the public `npm:@fluojs/platform-deno` root import and executes the built adapter closure natively. It dispatches a host-owned `createDenoFetchHandler(...)` request without a listener, then starts `runDenoApplication(...)` on port `0` with `shutdownSignals: false`, fetches a real route, and closes the application. Signal ownership remains disabled for that managed-listener test; signal registration is covered separately by the package-local contract suite.
105
157
 
106
158
  ## Public API Overview
107
159
 
108
- - `createDenoAdapter(options)`: Factory for the Deno HTTP adapter.
160
+ - `createDenoAdapter(options)`: Factory for the Deno HTTP adapter; it shares validation and normalization with direct construction.
161
+ - `createDenoFetchHandler(options)`: Synchronously creates a `Request` handler from an already bootstrapped `app.dispatcher` without starting or owning `Deno.serve(...)`.
109
162
  - `bootstrapDenoApplication(module, options)`: Advanced bootstrap for custom orchestration.
110
163
  - `runDenoApplication(module, options)`: Recommended quick-start helper for Deno.
111
- - `DenoHttpApplicationAdapter`: Core adapter implementation with `handle(...)`, `getListenTarget()`, `getRealtimeCapability()`, `getServer()`, and `configureWebSocketBinding(...)`.
112
- - `handle(request)`: Manual `Request` to `Response` dispatcher.
164
+ - `DenoHttpApplicationAdapter(options?)`: Core adapter implementation. Direct `new DenoHttpApplicationAdapter()` or `new DenoHttpApplicationAdapter(options)` applies the same default port, `host` alias handling, `hostname` precedence, and numeric option validation as `createDenoAdapter(options)`.
165
+ - `listen(dispatcher)`: Binds the fluo HTTP dispatcher and starts `Deno.serve`; duplicate calls are no-ops while preserving the original dispatcher.
166
+ - `close()`: Stops ingress, drains active requests for up to 10 seconds, and aborts the Deno serve signal if shutdown does not settle. A rejected graceful shutdown retains `getServer()` ownership until `server.finished` settles, then releases the managed lifecycle and rethrows the original shutdown error.
167
+ - `handle(request)`: Manual `Request` to `Response` dispatcher. It succeeds after `listen(dispatcher)` binds the runtime dispatcher, returns JSON `500` before binding, and returns JSON `503` while shutdown is in progress.
168
+ - `getListenTarget()`: Reports the bind target and public URL using Deno `hostname` or the portable `host` alias.
169
+ - `getRealtimeCapability()`: Reports the fetch-style Deno websocket upgrade capability for runtime integration.
170
+ - `getServer()`: Returns the active `Deno.serve` controller while the adapter is listening.
171
+ - `configureWebSocketBinding(...)`: Installs the `@fluojs/websockets/deno` binding before `listen(dispatcher)` starts the server.
172
+ - `DenoWebSocketMessage`: The full inbound websocket payload union: `ArrayBuffer | ArrayBufferView | Blob | string`.
113
173
  - `https: { cert, key }`: HTTPS startup options forwarded to `Deno.serve` and reflected in the reported listen URL.
114
- - Option and seam types: `DenoServeOptions`, `DenoServeController`, `DenoServerWebSocket`, websocket binding interfaces, bootstrap/run options, and listen-target helpers.
174
+ - Option and seam types: `CreateDenoFetchHandlerOptions`, `DenoServeOptions`, `DenoServeController`, `DenoServerWebSocket`, websocket binding interfaces, bootstrap/run options, and listen-target helpers.
115
175
 
116
176
  ## Related Packages
117
177
 
@@ -122,4 +182,5 @@ The shared edge portability suite in `packages/testing/src/portability/web-runti
122
182
  ## Example Sources
123
183
 
124
184
  - `packages/platform-deno/src/adapter.test.ts`
185
+ - `packages/platform-deno/src/fetch-handler.test.ts`
125
186
  - `packages/websockets/src/deno/deno.test.ts`
package/dist/adapter.d.ts CHANGED
@@ -23,7 +23,7 @@ export interface DenoServeController {
23
23
  /** Deno shutdown signals supported by `runDenoApplication(...)`. */
24
24
  export type DenoApplicationSignal = 'SIGINT' | 'SIGTERM';
25
25
  /** Message payloads delivered through Deno server websocket bindings. */
26
- export type DenoWebSocketMessage = Blob | string;
26
+ export type DenoWebSocketMessage = ArrayBuffer | ArrayBufferView | Blob | string;
27
27
  /** Server-side websocket shape used by the Deno platform binding seam. */
28
28
  export interface DenoServerWebSocket extends Pick<WebSocket, 'addEventListener' | 'close' | 'removeEventListener' | 'send'> {
29
29
  readonly readyState: number;
@@ -91,6 +91,9 @@ declare global {
91
91
  }
92
92
  /**
93
93
  * Deno-backed HTTP adapter that preserves request draining and websocket binding seams.
94
+ *
95
+ * Direct construction accepts the same public options as `createDenoAdapter(...)` and applies the
96
+ * same default port, `host` alias, `hostname` precedence, and numeric option validation.
94
97
  */
95
98
  export declare class DenoHttpApplicationAdapter implements HttpApplicationAdapter {
96
99
  private abortController?;
@@ -103,7 +106,12 @@ export declare class DenoHttpApplicationAdapter implements HttpApplicationAdapte
103
106
  private websocketBinding?;
104
107
  private readonly options;
105
108
  private readonly webRequestResponseFactory;
106
- constructor(options: Required<Pick<DenoAdapterOptions, 'hostname' | 'port'>> & DenoAdapterOptions);
109
+ /**
110
+ * Create a Deno adapter with the same normalization rules as `createDenoAdapter(...)`.
111
+ *
112
+ * @param options Transport, parsing, and websocket-host configuration for the Deno runtime.
113
+ */
114
+ constructor(options?: DenoAdapterOptions);
107
115
  getServer(): DenoServeController | undefined;
108
116
  getListenTarget(): HttpAdapterListenTarget;
109
117
  getRealtimeCapability(): import("@fluojs/http").FetchStyleHttpAdapterRealtimeCapability;
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
package/dist/adapter.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createFetchStyleHttpAdapterRealtimeCapability } from '@fluojs/http/internal';
2
2
  import { bootstrapHttpAdapterApplication, createDefaultApplicationLogger, runHttpAdapterApplication } from '@fluojs/runtime/internal/http-adapter';
3
- import { createWebRequestResponseFactory, dispatchWebRequest } from '@fluojs/runtime/web';
3
+ import { createWebRequestResponseFactory, startWebRequestDispatch } from '@fluojs/runtime/web';
4
+ import { validateNonNegativeIntegerOption } from './options.js';
4
5
 
5
6
  /** Listen target metadata reported by `Deno.serve(...)` callbacks. */
6
7
 
@@ -41,7 +42,11 @@ const DEFAULT_PORT = 3000;
41
42
  const DEFAULT_SHUTDOWN_TIMEOUT_MS = 10_000;
42
43
  /**
43
44
  * Deno-backed HTTP adapter that preserves request draining and websocket binding seams.
45
+ *
46
+ * Direct construction accepts the same public options as `createDenoAdapter(...)` and applies the
47
+ * same default port, `host` alias, `hostname` precedence, and numeric option validation.
44
48
  */
49
+ // allow: SIZE_OK — Deno serve, websocket, signal, and shutdown state form one adapter lifecycle state machine.
45
50
  export class DenoHttpApplicationAdapter {
46
51
  abortController;
47
52
  closeInFlight;
@@ -53,12 +58,19 @@ export class DenoHttpApplicationAdapter {
53
58
  websocketBinding;
54
59
  options;
55
60
  webRequestResponseFactory;
56
- constructor(options) {
57
- this.options = options;
61
+
62
+ /**
63
+ * Create a Deno adapter with the same normalization rules as `createDenoAdapter(...)`.
64
+ *
65
+ * @param options Transport, parsing, and websocket-host configuration for the Deno runtime.
66
+ */
67
+ constructor(options = {}) {
68
+ const resolvedOptions = normalizeDenoAdapterOptions(options);
69
+ this.options = resolvedOptions;
58
70
  this.webRequestResponseFactory = createWebRequestResponseFactory({
59
- maxBodySize: options.maxBodySize,
60
- multipart: options.multipart,
61
- rawBody: options.rawBody
71
+ maxBodySize: resolvedOptions.maxBodySize,
72
+ multipart: resolvedOptions.multipart,
73
+ rawBody: resolvedOptions.rawBody
62
74
  });
63
75
  }
64
76
  getServer() {
@@ -83,47 +95,75 @@ export class DenoHttpApplicationAdapter {
83
95
  return createShutdownResponse();
84
96
  }
85
97
  const release = this.trackInFlightRequest();
98
+ let releaseAfterResponse = true;
86
99
  try {
100
+ if (!this.dispatcher) {
101
+ const dispatch = startWebRequestDispatch({
102
+ dispatcher: this.dispatcher,
103
+ dispatcherNotReadyMessage: 'Deno adapter received a request before dispatcher binding completed.',
104
+ factory: this.webRequestResponseFactory,
105
+ request
106
+ });
107
+ releaseAfterResponse = false;
108
+ void dispatch.completion.finally(release).catch(() => {});
109
+ return await dispatch.response;
110
+ }
87
111
  if (this.websocketBinding && isWebSocketUpgradeRequest(request)) {
88
112
  const upgradeWebSocket = resolveUpgradeWebSocket(this.options.upgradeWebSocket);
89
113
  return await this.websocketBinding.fetch(request, {
90
114
  upgrade: upgradeRequest => upgradeWebSocket(upgradeRequest)
91
115
  });
92
116
  }
93
- return await dispatchWebRequest({
117
+ const dispatch = startWebRequestDispatch({
94
118
  dispatcher: this.dispatcher,
95
119
  dispatcherNotReadyMessage: 'Deno adapter received a request before dispatcher binding completed.',
96
120
  factory: this.webRequestResponseFactory,
97
121
  request
98
122
  });
123
+ releaseAfterResponse = false;
124
+ void dispatch.completion.finally(release).catch(() => {});
125
+ return await dispatch.response;
99
126
  } finally {
100
- release();
127
+ if (releaseAfterResponse) {
128
+ release();
129
+ }
101
130
  }
102
131
  }
103
132
  async listen(dispatcher) {
104
- this.dispatcher = dispatcher;
105
133
  if (this.server) {
106
134
  return;
107
135
  }
136
+ const previousAbortController = this.abortController;
137
+ const previousDispatcher = this.dispatcher;
138
+ const previousListenAddress = this.listenAddress;
108
139
  const abortController = new AbortController();
109
- const serve = resolveServe(this.options.serve);
110
140
  const listenReady = this.options.port === 0 ? createDeferred() : undefined;
111
- this.abortController = abortController;
112
- this.server = serve({
113
- cert: this.options.https?.cert,
114
- hostname: this.options.hostname,
115
- key: this.options.https?.key,
116
- onListen: localAddr => {
117
- this.listenAddress = localAddr;
118
- listenReady?.resolve();
119
- this.options.onListen?.(localAddr);
120
- },
121
- port: this.options.port,
122
- signal: abortController.signal
123
- }, async request => {
124
- return await this.handle(request);
125
- });
126
- await listenReady?.promise;
141
+ try {
142
+ const serve = resolveServe(this.options.serve);
143
+ this.dispatcher = dispatcher;
144
+ this.abortController = abortController;
145
+ this.server = serve({
146
+ cert: this.options.https?.cert,
147
+ hostname: this.options.hostname,
148
+ key: this.options.https?.key,
149
+ onListen: localAddr => {
150
+ this.listenAddress = localAddr;
151
+ listenReady?.resolve();
152
+ this.options.onListen?.(localAddr);
153
+ },
154
+ port: this.options.port,
155
+ signal: abortController.signal
156
+ }, async request => {
157
+ return await this.handle(request);
158
+ });
159
+ await listenReady?.promise;
160
+ } catch (error) {
161
+ abortController.abort();
162
+ this.abortController = previousAbortController;
163
+ this.dispatcher = previousDispatcher;
164
+ this.listenAddress = previousListenAddress;
165
+ throw error;
166
+ }
127
167
  }
128
168
  async close() {
129
169
  if (this.closeInFlight) {
@@ -185,12 +225,15 @@ export class DenoHttpApplicationAdapter {
185
225
  * @returns A Deno-backed `HttpApplicationAdapter`.
186
226
  */
187
227
  export function createDenoAdapter(options = {}) {
228
+ return new DenoHttpApplicationAdapter(options);
229
+ }
230
+ function normalizeDenoAdapterOptions(options) {
188
231
  validateNonNegativeIntegerOption('maxBodySize', options.maxBodySize);
189
- return new DenoHttpApplicationAdapter({
232
+ return {
190
233
  ...options,
191
234
  hostname: options.hostname ?? options.host ?? DEFAULT_HOSTNAME,
192
235
  port: resolveDenoPort(options.port)
193
- });
236
+ };
194
237
  }
195
238
 
196
239
  /**
@@ -247,7 +290,11 @@ function createDenoShutdownSignalRegistration(signals) {
247
290
  });
248
291
  }
249
292
  } catch (error) {
250
- removeDenoSignalBindings(denoGlobal, bindings);
293
+ try {
294
+ removeDenoSignalBindings(denoGlobal, bindings);
295
+ } catch (cleanupError) {
296
+ throw new AggregateError([error, cleanupError], 'Failed to register Deno shutdown signals and roll back registered listeners.');
297
+ }
251
298
  throw error;
252
299
  }
253
300
  return () => {
@@ -256,8 +303,19 @@ function createDenoShutdownSignalRegistration(signals) {
256
303
  };
257
304
  }
258
305
  function removeDenoSignalBindings(denoGlobal, bindings) {
306
+ const errors = [];
259
307
  for (const binding of bindings) {
260
- denoGlobal.removeSignalListener(binding.signal, binding.handler);
308
+ try {
309
+ denoGlobal.removeSignalListener(binding.signal, binding.handler);
310
+ } catch (error) {
311
+ errors.push(error);
312
+ }
313
+ }
314
+ if (errors.length === 1) {
315
+ throw errors[0];
316
+ }
317
+ if (errors.length > 1) {
318
+ throw new AggregateError(errors, 'Failed to remove Deno shutdown signal listeners.');
261
319
  }
262
320
  }
263
321
  function createListenTarget(hostname, port, usesHttps) {
@@ -277,14 +335,6 @@ function resolveDenoPort(value) {
277
335
  }
278
336
  return port;
279
337
  }
280
- function validateNonNegativeIntegerOption(name, value) {
281
- if (value === undefined) {
282
- return;
283
- }
284
- if (!Number.isInteger(value) || value < 0) {
285
- throw new Error(`Invalid ${name} value: ${String(value)}. Expected a non-negative integer.`);
286
- }
287
- }
288
338
  function formatHostForAuthority(hostname) {
289
339
  return hostname.includes(':') && !hostname.startsWith('[') ? `[${hostname}]` : hostname;
290
340
  }
@@ -313,15 +363,58 @@ function isWebSocketUpgradeRequest(request) {
313
363
  }
314
364
  function closeDenoServerWithDrain(server, abortController, waitForDrain) {
315
365
  return (async () => {
366
+ let closeFailure;
367
+ const shutdown = Promise.resolve(server.shutdown());
368
+ const drain = waitForDrain();
369
+ const gracefulClose = Promise.allSettled([shutdown, drain]).then(results => ({
370
+ kind: 'graceful',
371
+ results
372
+ }));
373
+ const forcedClose = abortController ? waitForAbort(abortController.signal).then(() => ({
374
+ kind: 'forced'
375
+ })) : undefined;
376
+ const closeResult = forcedClose ? await Promise.race([gracefulClose, forcedClose]) : await gracefulClose;
377
+ if (closeResult.kind === 'graceful') {
378
+ const rejected = closeResult.results.find(result => result.status === 'rejected');
379
+ if (rejected?.status === 'rejected') {
380
+ abortController?.abort();
381
+ closeFailure = {
382
+ error: rejected.reason
383
+ };
384
+ }
385
+ } else {
386
+ try {
387
+ await drain;
388
+ } catch (error) {
389
+ closeFailure = {
390
+ error
391
+ };
392
+ }
393
+ }
316
394
  try {
317
- await server.shutdown();
318
- await waitForDrain();
319
- } finally {
320
- abortController?.abort();
395
+ await server.finished;
396
+ } catch (error) {
397
+ if (!closeFailure) {
398
+ throw error;
399
+ }
400
+ }
401
+ if (closeFailure) {
402
+ throw closeFailure.error;
321
403
  }
322
- await server.finished;
323
404
  })();
324
405
  }
406
+ function waitForAbort(signal) {
407
+ if (signal.aborted) {
408
+ return Promise.resolve();
409
+ }
410
+ return new Promise(resolve => {
411
+ signal.addEventListener('abort', () => {
412
+ resolve();
413
+ }, {
414
+ once: true
415
+ });
416
+ });
417
+ }
325
418
  function createDeferred() {
326
419
  let resolve;
327
420
  let reject;
@@ -0,0 +1,26 @@
1
+ import type { Dispatcher } from '@fluojs/http/internal';
2
+ import type { MultipartOptions } from '@fluojs/runtime';
3
+ import type { DenoServeHandler } from './adapter.js';
4
+ /** Options for creating a Deno request handler for a host-owned `Deno.serve(...)` lifecycle. */
5
+ export interface CreateDenoFetchHandlerOptions {
6
+ /** Already bootstrapped dispatcher that receives translated fluo framework requests. */
7
+ readonly dispatcher: Dispatcher;
8
+ /** Maximum request body size enforced by the shared Web request parser. */
9
+ readonly maxBodySize?: number;
10
+ /** Multipart parsing limits enforced by the shared Web request parser. */
11
+ readonly multipart?: MultipartOptions;
12
+ /** Preserves byte-exact raw bodies for JSON and text requests when enabled. */
13
+ readonly rawBody?: boolean;
14
+ }
15
+ /**
16
+ * Create a Deno `Request` handler without starting or owning `Deno.serve(...)`.
17
+ *
18
+ * @remarks
19
+ * The surrounding host owns server startup, shutdown, signals, and websocket upgrades. The
20
+ * dispatcher must come from an already bootstrapped fluo application, such as `app.dispatcher`.
21
+ *
22
+ * @param options - Bootstrapped dispatcher and shared Web request parsing options.
23
+ * @returns A handler suitable for a host-owned `Deno.serve(handler)` call.
24
+ */
25
+ export declare function createDenoFetchHandler({ dispatcher, maxBodySize, multipart, rawBody, }: CreateDenoFetchHandlerOptions): DenoServeHandler;
26
+ //# sourceMappingURL=fetch-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-handler.d.ts","sourceRoot":"","sources":["../src/fetch-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,gGAAgG;AAChG,MAAM,WAAW,6BAA6B;IAC5C,wFAAwF;IACxF,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IACtC,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,WAAW,EACX,SAAS,EACT,OAAO,GACR,EAAE,6BAA6B,GAAG,gBAAgB,CAiBlD"}
@@ -0,0 +1,36 @@
1
+ import { createWebRequestResponseFactory, dispatchWebRequest } from '@fluojs/runtime/web';
2
+ import { validateNonNegativeIntegerOption } from './options.js';
3
+
4
+ /** Options for creating a Deno request handler for a host-owned `Deno.serve(...)` lifecycle. */
5
+
6
+ /**
7
+ * Create a Deno `Request` handler without starting or owning `Deno.serve(...)`.
8
+ *
9
+ * @remarks
10
+ * The surrounding host owns server startup, shutdown, signals, and websocket upgrades. The
11
+ * dispatcher must come from an already bootstrapped fluo application, such as `app.dispatcher`.
12
+ *
13
+ * @param options - Bootstrapped dispatcher and shared Web request parsing options.
14
+ * @returns A handler suitable for a host-owned `Deno.serve(handler)` call.
15
+ */
16
+ export function createDenoFetchHandler({
17
+ dispatcher,
18
+ maxBodySize,
19
+ multipart,
20
+ rawBody
21
+ }) {
22
+ validateNonNegativeIntegerOption('maxBodySize', maxBodySize);
23
+ const factory = createWebRequestResponseFactory({
24
+ maxBodySize,
25
+ multipart,
26
+ rawBody
27
+ });
28
+ return async function denoFetchHandler(request) {
29
+ return await dispatchWebRequest({
30
+ dispatcher,
31
+ dispatcherNotReadyMessage: 'Deno fetch handler received a request before dispatcher binding completed.',
32
+ factory,
33
+ request
34
+ });
35
+ };
36
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './adapter.js';
2
+ export * from './fetch-handler.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
- export * from './adapter.js';
1
+ export * from './adapter.js';
2
+ export * from './fetch-handler.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Validate an optional non-negative integer adapter option.
3
+ *
4
+ * @param name - Option name included in validation errors.
5
+ * @param value - Optional numeric value to validate.
6
+ */
7
+ export declare function validateNonNegativeIntegerOption(name: string, value: number | undefined): void;
8
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAQ9F"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Validate an optional non-negative integer adapter option.
3
+ *
4
+ * @param name - Option name included in validation errors.
5
+ * @param value - Optional numeric value to validate.
6
+ */
7
+ export function validateNonNegativeIntegerOption(name, value) {
8
+ if (value === undefined) {
9
+ return;
10
+ }
11
+ if (!Number.isInteger(value) || value < 0) {
12
+ throw new Error(`Invalid ${name} value: ${String(value)}. Expected a non-negative integer.`);
13
+ }
14
+ }
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "platform",
9
9
  "server"
10
10
  ],
11
- "version": "1.0.8",
11
+ "version": "2.0.0",
12
12
  "private": false,
13
13
  "license": "MIT",
14
14
  "repository": {
@@ -32,12 +32,12 @@
32
32
  "dist"
33
33
  ],
34
34
  "dependencies": {
35
- "@fluojs/http": "^1.1.2",
36
- "@fluojs/runtime": "^1.1.8"
35
+ "@fluojs/http": "^3.0.0",
36
+ "@fluojs/runtime": "^3.0.0"
37
37
  },
38
38
  "devDependencies": {
39
- "vitest": "^3.2.4",
40
- "@fluojs/testing": "^1.0.6"
39
+ "vitest": "^4.1.11",
40
+ "@fluojs/testing": "^3.0.0"
41
41
  },
42
42
  "scripts": {
43
43
  "prebuild": "node ../../tooling/scripts/clean-dist.mjs",