@fluojs/websockets 1.0.7 → 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 (62) hide show
  1. package/README.ko.md +30 -10
  2. package/README.md +30 -10
  3. package/dist/bun/bun-service.d.ts +3 -1
  4. package/dist/bun/bun-service.d.ts.map +1 -1
  5. package/dist/bun/bun-service.js +75 -26
  6. package/dist/bun/bun-types.d.ts +15 -4
  7. package/dist/bun/bun-types.d.ts.map +1 -1
  8. package/dist/bun.d.ts +2 -1
  9. package/dist/bun.d.ts.map +1 -1
  10. package/dist/bun.js +2 -1
  11. package/dist/cloudflare-workers/cloudflare-workers-service.d.ts +4 -0
  12. package/dist/cloudflare-workers/cloudflare-workers-service.d.ts.map +1 -1
  13. package/dist/cloudflare-workers/cloudflare-workers-service.js +108 -33
  14. package/dist/cloudflare-workers/cloudflare-workers-types.d.ts +2 -2
  15. package/dist/cloudflare-workers/cloudflare-workers-types.d.ts.map +1 -1
  16. package/dist/cloudflare-workers.d.ts +2 -1
  17. package/dist/cloudflare-workers.d.ts.map +1 -1
  18. package/dist/cloudflare-workers.js +2 -1
  19. package/dist/decorators.d.ts +2 -2
  20. package/dist/decorators.d.ts.map +1 -1
  21. package/dist/decorators.js +4 -4
  22. package/dist/deno/deno-service.d.ts +4 -0
  23. package/dist/deno/deno-service.d.ts.map +1 -1
  24. package/dist/deno/deno-service.js +85 -16
  25. package/dist/deno/deno-types.d.ts +2 -2
  26. package/dist/deno/deno-types.d.ts.map +1 -1
  27. package/dist/deno.d.ts +2 -1
  28. package/dist/deno.d.ts.map +1 -1
  29. package/dist/deno.js +2 -1
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/internal/shared.d.ts +6 -3
  33. package/dist/internal/shared.d.ts.map +1 -1
  34. package/dist/internal/shared.js +26 -6
  35. package/dist/internal.d.ts +7 -0
  36. package/dist/internal.d.ts.map +1 -0
  37. package/dist/internal.js +6 -0
  38. package/dist/module.d.ts +1 -1
  39. package/dist/module.d.ts.map +1 -1
  40. package/dist/module.js +23 -3
  41. package/dist/node/node-module.d.ts.map +1 -1
  42. package/dist/node/node-module.js +6 -2
  43. package/dist/node/node-service-token.d.ts +20 -0
  44. package/dist/node/node-service-token.d.ts.map +1 -0
  45. package/dist/node/node-service-token.js +29 -0
  46. package/dist/node/node-service.d.ts +5 -3
  47. package/dist/node/node-service.d.ts.map +1 -1
  48. package/dist/node/node-service.js +26 -8
  49. package/dist/node/node-types.d.ts +2 -2
  50. package/dist/node/node-types.d.ts.map +1 -1
  51. package/dist/node/node.d.ts +1 -1
  52. package/dist/node/node.d.ts.map +1 -1
  53. package/dist/node/node.js +1 -1
  54. package/dist/node.d.ts +1 -0
  55. package/dist/node.d.ts.map +1 -1
  56. package/dist/node.js +2 -1
  57. package/dist/service.d.ts +1 -8
  58. package/dist/service.d.ts.map +1 -1
  59. package/dist/service.js +1 -8
  60. package/dist/types.d.ts +26 -2
  61. package/dist/types.d.ts.map +1 -1
  62. package/package.json +21 -12
package/README.ko.md CHANGED
@@ -21,7 +21,9 @@ fluo 런타임을 위한 데코레이터 기반 WebSocket 게이트웨이 작성
21
21
  npm install @fluojs/websockets
22
22
  ```
23
23
 
24
- Root Node.js entrypoint는 패키지가 직접 소유한 `ws` dependency를 사용합니다. 애플리케이션 코드에서 `ws`를 직접 사용하지 않는 한 별도 설치가 필요하지 않습니다.
24
+ Root Node.js module path`WebSocketModule.forRoot()`가 runtime에서 해석될 때만 패키지가 직접 소유한 `ws` dependency를 사용합니다. 애플리케이션 코드에서 `ws`를 직접 사용하지 않는 한 별도 설치가 필요하지 않습니다.
25
+
26
+ `@fluojs/websockets`은 `ws@^8.21.0`을 포함합니다. 업그레이드할 때 application lockfile을 갱신해 패치된 package-owned Node.js WebSocket runtime이 설치되도록 하세요. Bun, Deno, Cloudflare Workers subpath는 계속 각 runtime이 소유한 WebSocket 구현을 사용합니다.
25
27
 
26
28
  ## 사용 시점
27
29
 
@@ -102,16 +104,20 @@ WebSocketModule.forRoot({
102
104
  });
103
105
  ```
104
106
 
105
- 옵션을 생략하면 `@fluojs/websockets`는 동시 연결 수, inbound payload 크기, pending message buffer, shutdown cleanup에 bounded default를 적용합니다. 기본값은 `maxConnections: 1000`, `maxPayloadBytes: 1 MiB`, `buffer.maxPendingMessagesPerSocket: 256`, `shutdown.timeoutMs: 5000`, Node heartbeat interval `30s`, Node backpressure `maxBufferedAmountBytes: 1 MiB`와 drop behavior입니다. 또한 server-backed Node listener는 `heartbeat.enabled`를 명시적으로 `false`로 두지 않는 한 heartbeat timer를 활성화합니다. Node shutdown은 shutdown이 시작된 in-flight async upgrade를 거절하고, 애플리케이션 shutdown 시 추적 중인 websocket 클라이언트를 닫고, `shutdown.timeoutMs` 범위 안에서 `@OnDisconnect()` cleanup이 마무리될 수 있도록 bounded 기회를 제공합니다. 해결되지 않은 cleanup은 shutdown을 무기한 막지 않고 해당 timeout 안에서 로그로 남습니다. 공식 fetch-style runtime module(`@fluojs/websockets/bun`, `@fluojs/websockets/deno`, `@fluojs/websockets/cloudflare-workers`)은 `Request` typed upgrade guard를 노출하며, 애플리케이션 shutdown 중 동일한 bounded close와 disconnect cleanup 동작을 제공합니다.
107
+ 옵션을 생략하면 `@fluojs/websockets`는 동시 연결 수, inbound payload 크기, pending message buffer, shutdown cleanup에 bounded default를 적용합니다. 기본값은 `maxConnections: 1000`, `maxPayloadBytes: 1 MiB`, `buffer.maxPendingMessagesPerSocket: 256`, `shutdown.timeoutMs: 5000`, Node heartbeat interval `30s`, Node backpressure `maxBufferedAmountBytes: 1 MiB`와 drop behavior입니다. 또한 server-backed Node listener는 `heartbeat.enabled`를 명시적으로 `false`로 두지 않는 한 heartbeat timer를 활성화합니다. Node shutdown은 terminal admission gate를 설정하고 upgrade guard가 없는 경우까지 포함해 일치하는 upgrade를 `ws`에 넘기기 직전에 gate를 다시 확인합니다. 애플리케이션 shutdown 시 추적 중인 websocket 클라이언트를 닫고, `shutdown.timeoutMs` 범위 안에서 `@OnDisconnect()` cleanup이 마무리될 수 있도록 bounded 기회를 제공합니다. 해결되지 않은 cleanup은 shutdown을 무기한 막지 않고 해당 timeout 안에서 로그로 남습니다. 모든 runtime은 queue된 disconnect cleanup이 settle될 때까지 connection별 lifecycle state를 유지하므로, shutdown 직전에 client close가 queue한 `@OnDisconnect()`도 같은 bounded drain에 포함됩니다. 공식 fetch-style runtime module(`@fluojs/websockets/bun`, `@fluojs/websockets/deno`, `@fluojs/websockets/cloudflare-workers`)은 `Request` typed upgrade guard를 노출하며, 애플리케이션 shutdown 중 동일한 bounded close와 disconnect cleanup 동작을 제공합니다. Bun, Deno, Cloudflare Workers는 shutdown 중에도 adapter-owned binding을 유지하므로 host가 request routing 소유권을 보존하고 새 upgrade에는 runtime의 terminal response를 반환합니다. Terminal socket error 또는 room broadcast send 실패가 발생하면 fetch-style socket을 code `1011`로 닫고 `@OnDisconnect()` cleanup을 완료합니다. Cloudflare Workers 애플리케이션 shutdown 중 새 upgrade 시도는 HTTP dispatch로 넘어가지 않고 JSON `503` shutdown response로 거절됩니다.
106
108
 
107
- Root `@fluojs/websockets` / `@fluojs/websockets/node` guard는 Node의 `IncomingMessage`를 받습니다. Fetch-style subpath는 Web standard `Request`를 받으므로, 재사용 가능한 옵션 객체를 작성할 때는 subpath별 `WebSocketModuleOptions` 타입을 선택하세요.
109
+ Root `@fluojs/websockets` / `@fluojs/websockets/node` guard는 Node의 `IncomingMessage`를 받습니다. Fetch-style subpath는 Web standard `Request`를 받으므로, 재사용 가능한 옵션 객체를 작성할 때는 subpath별 `WebSocketModuleOptions` 타입을 선택하세요. Guard는 `true`, `undefined`, 또는 return 없음으로 upgrade를 허용하고, `false` 또는 `{ status, body? }` 형태의 `WebSocketUpgradeRejection`으로 거절하거나 `UnauthorizedException` 같은 `HttpException` 계열 오류를 throw할 수 있습니다. Throw된 HTTP exception은 socket이 accept되기 전에 동일한 pre-handshake rejection response로 변환됩니다.
108
110
 
109
111
  ### Room
110
- `WebSocketRoomService`를 사용하면 gateway 또는 application service가 adapter 내부에 접근하지 않고도 가벼운 room membership state를 유지할 수 있습니다. Runtime lifecycle service는 `joinRoom(socketId, room)`, `leaveRoom(socketId, room)`, `broadcastToRoom(room, event, data)`, `getRooms(socketId)`를 구현합니다. `broadcastToRoom(...)`은 현재 room에 있는 열린 socket에 `{ event, data }` 형태의 JSON frame을 보내며, 전송 전에 설정된 backpressure policy를 적용합니다.
112
+ `WebSocketRoomService`를 사용하면 gateway 또는 application service가 adapter 내부에 접근하지 않고도 가벼운 room membership state를 유지할 수 있습니다. Runtime lifecycle service는 `joinRoom(socketId, room)`, `leaveRoom(socketId, room)`, `broadcastToRoom(room, event, data)`, `getRooms(socketId)`를 구현합니다. `joinRoom(...)`은 `socketId`가 선택한 runtime lifecycle service에 등록된 현재 열린 socket을 가리킬 때만 membership을 추가합니다. 알 수 없거나 이미 닫힌 socket identifier는 무시하며 room index를 생성하지 않습니다. `broadcastToRoom(...)`은 현재 room에 있는 열린 socket에 `{ event, data }` 형태의 JSON frame을 보냅니다. Node.js 기반 adapter는 전송 전에 설정된 `backpressure` policy를 적용하지만, fetch-style runtime(`@fluojs/websockets/bun`, `@fluojs/websockets/deno`, `@fluojs/websockets/cloudflare-workers`)은 room broadcast에 backpressure policy를 적용하지 않습니다.
113
+
114
+ `WebSocketRoomService`는 runtime lifecycle service가 구현하는 type-only contract입니다. `@Inject(...)`로 lifecycle service token을 주입하고 constructor parameter를 `WebSocketRoomService`로 type 지정하세요. Root `@fluojs/websockets` entrypoint는 `WebSocketGatewayLifecycleService`를 DI token으로 노출하고, 명시적 `@fluojs/websockets/node` subpath는 `NodeWebSocketGatewayLifecycleService`를 노출합니다. 다른 runtime-specific subpath는 아래 runtime 표에 나열된 해당 `*WebSocketGatewayLifecycleService` token을 노출합니다.
111
115
 
112
116
  ```typescript
113
- import { WebSocketRoomService } from '@fluojs/websockets';
117
+ import { Inject } from '@fluojs/core';
118
+ import { WebSocketGatewayLifecycleService, type WebSocketRoomService } from '@fluojs/websockets';
114
119
 
120
+ @Inject(WebSocketGatewayLifecycleService)
115
121
  class OrderStatusPublisher {
116
122
  constructor(private readonly rooms: WebSocketRoomService) {}
117
123
 
@@ -125,6 +131,19 @@ class OrderStatusPublisher {
125
131
 
126
132
  Gateway `@OnMessage()` handler는 지원 런타임 전반에서 하나의 정규화된 payload contract를 받습니다. Text frame은 가능한 경우 JSON으로 파싱하고, 그렇지 않으면 string으로 전달합니다. Binary frame은 런타임이 Node `Buffer`/typed array, Bun `ArrayBuffer`/view, Deno `ArrayBuffer`/view/`Blob`, Cloudflare Workers `ArrayBuffer`/view/`Blob` 중 어떤 형태로 노출하더라도 UTF-8로 디코딩한 뒤 동일한 JSON/event dispatch 단계를 거칩니다. `limits.maxPayloadBytes` 검사는 모든 표현에 byte length를 사용하며, 허용된 socket에서 oversized payload가 들어오면 close code `1009`로 닫습니다.
127
133
 
134
+ Message handler는 `(payload, socket, request, socketId)`를 받습니다. 안정적인 `socketId`는 `WebSocketRoomService`가 받는 connection identity와 같으므로, handler가 adapter별 socket 내부를 조사하지 않고 현재 connection을 room에 추가할 수 있습니다.
135
+
136
+ 기본적으로 handler return value는 Node, Bun, Deno, Cloudflare Workers 전반에서 완료될 때까지 await된 뒤 무시됩니다. `socket.send(JSON.stringify({ event: 'pong', data }))`처럼 runtime socket argument를 통해 명시적으로 reply를 보낼 수 있습니다. Return 기반 reply를 opt-in하려면 `replies: { mode: 'event-envelope' }`를 설정하세요. 그러면 올바른 `{ event: string, data?: unknown }` return이 handler settle 후 serialize되어 전송됩니다. 올바르지 않은 return은 계속 무시되며, `replies`를 생략하면 기존 기본 동작이 유지됩니다.
137
+
138
+ ```typescript
139
+ WebSocketModule.forRoot({ replies: { mode: 'event-envelope' } });
140
+
141
+ @OnMessage('ping')
142
+ handlePing(payload, _socket, _request, socketId) {
143
+ return { event: 'pong', data: { payload, socketId } };
144
+ }
145
+ ```
146
+
128
147
  ## 공개 API 개요
129
148
 
130
149
  - `@WebSocketGateway(options)`: 클래스를 WebSocket 게이트웨이로 표시합니다.
@@ -132,18 +151,19 @@ Gateway `@OnMessage()` handler는 지원 런타임 전반에서 하나의 정규
132
151
  - `@OnMessage(event?)`: 인바운드 메시지 핸들러를 위한 데코레이터입니다.
133
152
  - `@OnDisconnect()`: 연결 해제 핸들러를 위한 데코레이터입니다.
134
153
  - `WebSocketModule`: WebSocket 통합을 위한 루트 모듈입니다.
135
- - `WebSocketModule.forRoot({ upgrade, limits, backpressure, buffer, heartbeat, shutdown })`: pre-upgrade guard bounded runtime default를 구성합니다.
136
- - `WebSocketGatewayLifecycleService`: 기본 Node.js 기반 lifecycle service token 위한 루트 alias입니다.
154
+ - `WebSocketModule.forRoot({ upgrade, limits, backpressure, buffer, heartbeat, replies, shutdown })`: pre-upgrade guard, bounded runtime default, optional event-envelope reply를 구성합니다.
155
+ - `WebSocketGatewayLifecycleService`: 기본 Node.js 기반 lifecycle service token `NodeWebSocketGatewayLifecycleService`의 루트 alias입니다. 직접 instantiate하는 concrete class가 아니라 DI token placeholder이며, `WebSocketModule.forRoot(...)`가 lazy Node implementation을 연결한 뒤 application container에서 resolve해야 합니다.
137
156
  - `WebSocketRoomService`: websocket room join, leave, broadcast, 조회를 위해 runtime lifecycle service가 구현하는 Room management contract입니다.
157
+ - Typed runtime seam: `WebSocketUpgradeContext`, `WebSocketUpgradeGuard`, `WebSocketUpgradeRejection`, `WebSocketGatewayDescriptor`, `WebSocketGatewayHandlerDescriptor`, 그리고 Node, Bun, Deno, Cloudflare Workers subpath의 runtime socket/binding type.
138
158
  - Metadata helper와 symbol: `defineWebSocketGatewayMetadata`, `getWebSocketGatewayMetadata`, `defineWebSocketHandlerMetadata`, `getWebSocketHandlerMetadata`, `getWebSocketHandlerMetadataEntries`, `webSocketGatewayMetadataSymbol`, `webSocketHandlerMetadataSymbol`.
139
159
 
140
160
  ## 런타임별 서브패스
141
161
 
142
- 기본 루트 Node.js alias 대신 런타임을 명시적으로 고정하고 싶다면 런타임별 서브패스를 사용하세요. 루트 `@fluojs/websockets` 진입점은 Node.js 기본 module lifecycle-service alias 유지합니다. Fetch-style 애플리케이션은 게이트웨이 데코레이터와 metadata helper를 선택한 런타임 서브패스에서 import 수 있으므로 authoring code가 루트 Node.js 기반 진입점을 로드할 필요가 없습니다.
162
+ 기본 루트 Node.js alias 대신 런타임을 명시적으로 고정하고 싶다면 런타임별 서브패스를 사용하세요. 루트 `@fluojs/websockets` 진입점은 Node.js 기본 module export name을 유지하고 `WebSocketGatewayLifecycleService`를 `NodeWebSocketGatewayLifecycleService`와 같은 DI token으로 alias하지만, 단순 root package import는 concrete Node implementation을 lazy runtime provider resolution 뒤에 둡니다. Fetch-style 애플리케이션은 선택한 런타임 서브패스에서 gateway decorator와 metadata helper를 import authoring code가 root Node.js-backed module boundary에 의존하지 않게 해야 합니다.
143
163
 
144
- Package manifest의 `engines.node >=20.0.0` 선언은 published package와 기본 Node.js entrypoint 기준입니다. Bun, Deno, Cloudflare Workers 지원은 아래 전용 fetch-style subpath를 통해 노출되며, 해당 subpath는 request/handler type을 web-standard로 유지하고 application code가 root Node.js lifecycle-service alias에 의존하지 않게 합니다.
164
+ Published package와 기본 Node.js entrypoint의 package-owned 지원 계약은 manifest의 `engines.node >=24.0.0 <27` 선언입니다. Node 20 Node 22 host를 Node.js `>=24.0.0 <27`로 올리세요. Node 24 미만과 Node 27 이상은 지원하지 않습니다. Bun, Deno, Cloudflare Workers 지원은 아래 전용 fetch-style subpath를 통해 노출되며, 해당 subpath는 request/handler type을 web-standard로 유지하고 application code가 root Node.js lifecycle-service alias에 의존하지 않게 합니다.
145
165
 
146
- 각 서브패스는 해당 `*WebSocketModule.forRoot(...)` 진입점, 일치하는 런타임 lifecycle service export, 그리고 공유 gateway authoring primitive인 `WebSocketGateway`, `OnConnect`, `OnMessage`, `OnDisconnect`, `defineWebSocketGatewayMetadata`, `getWebSocketGatewayMetadata`, `defineWebSocketHandlerMetadata`, `getWebSocketHandlerMetadata`, `getWebSocketHandlerMetadataEntries`, `webSocketGatewayMetadataSymbol`, `webSocketHandlerMetadataSymbol`을 제공합니다.
166
+ 각 서브패스는 해당 `*WebSocketModule.forRoot(...)` 진입점, 일치하는 런타임 lifecycle service export, 그리고 공유 gateway authoring primitive인 `WebSocketGateway`, `OnConnect`, `OnMessage`, `OnDisconnect`, `defineWebSocketGatewayMetadata`, `getWebSocketGatewayMetadata`, `defineWebSocketHandlerMetadata`, `getWebSocketHandlerMetadata`, `getWebSocketHandlerMetadataEntries`, `webSocketGatewayMetadataSymbol`, `webSocketHandlerMetadataSymbol`을 제공합니다. Bun 서브패스의 low-level binding은 `upgrade(...)`만 가진 `BunWebSocketUpgradeHost`를 받으며, adapter-owned listener shutdown과 raw HTTP fetch 제어는 `@fluojs/platform-bun`에 남습니다.
147
167
 
148
168
  | 런타임 | 서브패스 | 모듈 | Lifecycle service |
149
169
  | --- | --- | --- | --- |
package/README.md CHANGED
@@ -21,7 +21,9 @@ Decorator-based WebSocket gateway authoring for the fluo runtime.
21
21
  npm install @fluojs/websockets
22
22
  ```
23
23
 
24
- The root Node.js entrypoint uses the package-owned `ws` dependency. Applications do not need to install `ws` separately unless they use it directly in their own code.
24
+ The root Node.js module path uses the package-owned `ws` dependency only when `WebSocketModule.forRoot()` is resolved at runtime. Applications do not need to install `ws` separately unless they use it directly in their own code.
25
+
26
+ `@fluojs/websockets` includes `ws@^8.21.0`. Refresh the application lockfile when upgrading so the patched package-owned Node.js WebSocket runtime is installed. The Bun, Deno, and Cloudflare Workers subpaths continue to use their runtime-owned WebSocket implementations.
25
27
 
26
28
  ## When to Use
27
29
 
@@ -102,16 +104,20 @@ WebSocketModule.forRoot({
102
104
  });
103
105
  ```
104
106
 
105
- When omitted, `@fluojs/websockets` applies bounded defaults for concurrent connections, inbound payload size, pending message buffers, and shutdown cleanup. Default settings are `maxConnections: 1000`, `maxPayloadBytes: 1 MiB`, `buffer.maxPendingMessagesPerSocket: 256`, `shutdown.timeoutMs: 5000`, Node heartbeat interval `30s`, and Node backpressure `maxBufferedAmountBytes: 1 MiB` with drop behavior. Server-backed Node listeners enable heartbeat timers unless you explicitly set `heartbeat.enabled` to `false`. Node shutdown rejects in-flight async upgrades once shutdown begins, will close tracked websocket clients during application shutdown, and gives `@OnDisconnect()` cleanup a bounded chance to finish within `shutdown.timeoutMs`; unresolved cleanup is logged and bounded by that timeout instead of blocking shutdown indefinitely. The official fetch-style runtime modules (`@fluojs/websockets/bun`, `@fluojs/websockets/deno`, and `@fluojs/websockets/cloudflare-workers`) expose `Request`-typed upgrade guards and provide the same bounded close and disconnect cleanup behavior during application shutdown.
107
+ When omitted, `@fluojs/websockets` applies bounded defaults for concurrent connections, inbound payload size, pending message buffers, and shutdown cleanup. Default settings are `maxConnections: 1000`, `maxPayloadBytes: 1 MiB`, `buffer.maxPendingMessagesPerSocket: 256`, `shutdown.timeoutMs: 5000`, Node heartbeat interval `30s`, and Node backpressure `maxBufferedAmountBytes: 1 MiB` with drop behavior. Server-backed Node listeners enable heartbeat timers unless you explicitly set `heartbeat.enabled` to `false`. Node shutdown establishes a terminal admission gate, rechecks it immediately before handing a matching upgrade to `ws` even when no upgrade guard is configured, and will close tracked websocket clients during application shutdown. It gives `@OnDisconnect()` cleanup a bounded chance to finish within `shutdown.timeoutMs`; unresolved cleanup is logged and bounded by that timeout instead of blocking shutdown indefinitely. All runtimes retain per-connection lifecycle state until queued disconnect cleanup settles, so a client close that queues `@OnDisconnect()` immediately before shutdown still enters the same bounded drain. The official fetch-style runtime modules (`@fluojs/websockets/bun`, `@fluojs/websockets/deno`, and `@fluojs/websockets/cloudflare-workers`) expose `Request`-typed upgrade guards and provide the same bounded close and disconnect cleanup behavior during application shutdown. Bun, Deno, and Cloudflare Workers keep the adapter-owned binding installed during shutdown so the host retains request routing ownership while new upgrades receive the runtime's terminal response. Fetch-style sockets are closed with code `1011` and complete `@OnDisconnect()` cleanup after terminal socket errors or failed room-broadcast sends. During Cloudflare Workers application shutdown, new upgrade attempts receive a JSON `503` shutdown response instead of falling through to HTTP dispatch.
106
108
 
107
- The root `@fluojs/websockets` / `@fluojs/websockets/node` guard receives Node's `IncomingMessage`. Fetch-style subpaths receive a Web-standard `Request`, so choose the subpath-specific `WebSocketModuleOptions` type when authoring reusable option objects.
109
+ The root `@fluojs/websockets` / `@fluojs/websockets/node` guard receives Node's `IncomingMessage`. Fetch-style subpaths receive a Web-standard `Request`, so choose the subpath-specific `WebSocketModuleOptions` type when authoring reusable option objects. Guards may allow an upgrade with `true`, `undefined`, or no return value; reject with `false` or a `{ status, body? }` `WebSocketUpgradeRejection`; or throw an `HttpException`-like error such as `UnauthorizedException`. Thrown HTTP exceptions are converted to the same pre-handshake rejection response before any socket is accepted.
108
110
 
109
111
  ### Rooms
110
- `WebSocketRoomService` lets gateway or application services keep lightweight room membership state without reaching into adapter internals. Runtime lifecycle services implement `joinRoom(socketId, room)`, `leaveRoom(socketId, room)`, `broadcastToRoom(room, event, data)`, and `getRooms(socketId)`. `broadcastToRoom(...)` sends a JSON frame shaped as `{ event, data }` to currently open sockets in the room and applies the configured backpressure policy before sending.
112
+ `WebSocketRoomService` lets gateway or application services keep lightweight room membership state without reaching into adapter internals. Runtime lifecycle services implement `joinRoom(socketId, room)`, `leaveRoom(socketId, room)`, `broadcastToRoom(room, event, data)`, and `getRooms(socketId)`. `joinRoom(...)` adds membership only when `socketId` identifies a currently open socket registered by the selected runtime lifecycle service; unknown or already closed socket identifiers are ignored and do not create room indexes. `broadcastToRoom(...)` sends a JSON frame shaped as `{ event, data }` to currently open sockets in the room. The Node.js-backed adapter applies the configured `backpressure` policy before sending; the fetch-style runtimes (`@fluojs/websockets/bun`, `@fluojs/websockets/deno`, and `@fluojs/websockets/cloudflare-workers`) do not apply a backpressure policy to room broadcasts.
113
+
114
+ `WebSocketRoomService` is a type-only contract implemented by the runtime lifecycle service. Inject the lifecycle service token with `@Inject(...)` and type the constructor parameter as `WebSocketRoomService`. The root `@fluojs/websockets` entrypoint exposes `WebSocketGatewayLifecycleService` as the DI token, while the explicit `@fluojs/websockets/node` subpath exposes `NodeWebSocketGatewayLifecycleService`; the other runtime-specific subpaths expose the matching `*WebSocketGatewayLifecycleService` token listed in the runtime table below.
111
115
 
112
116
  ```typescript
113
- import { WebSocketRoomService } from '@fluojs/websockets';
117
+ import { Inject } from '@fluojs/core';
118
+ import { WebSocketGatewayLifecycleService, type WebSocketRoomService } from '@fluojs/websockets';
114
119
 
120
+ @Inject(WebSocketGatewayLifecycleService)
115
121
  class OrderStatusPublisher {
116
122
  constructor(private readonly rooms: WebSocketRoomService) {}
117
123
 
@@ -125,6 +131,19 @@ class OrderStatusPublisher {
125
131
 
126
132
  Gateway `@OnMessage()` handlers receive one normalized payload contract across supported runtimes. Text frames are parsed as JSON when possible and otherwise delivered as strings. Binary frames are decoded as UTF-8 before the same JSON/event dispatch step, whether the runtime surfaces them as Node `Buffer`/typed arrays, Bun `ArrayBuffer`/views, Deno `ArrayBuffer`/views/`Blob`, or Cloudflare Workers `ArrayBuffer`/views/`Blob`. The `limits.maxPayloadBytes` check uses byte length for every representation and closes oversized accepted sockets with close code `1009`.
127
133
 
134
+ Message handlers receive `(payload, socket, request, socketId)`. The stable `socketId` is the same connection identity accepted by `WebSocketRoomService`, so a handler can join its current connection to a room without adapter-specific socket inspection.
135
+
136
+ Handler return values are awaited for completion and then ignored across Node, Bun, Deno, and Cloudflare Workers by default. Send replies explicitly through the runtime socket argument, for example `socket.send(JSON.stringify({ event: 'pong', data }))`. To opt into return-based replies, configure `replies: { mode: 'event-envelope' }`; then a valid `{ event: string, data?: unknown }` return is serialized and sent after the handler settles. Invalid returns remain ignored, and omitting `replies` preserves the default behavior.
137
+
138
+ ```typescript
139
+ WebSocketModule.forRoot({ replies: { mode: 'event-envelope' } });
140
+
141
+ @OnMessage('ping')
142
+ handlePing(payload, _socket, _request, socketId) {
143
+ return { event: 'pong', data: { payload, socketId } };
144
+ }
145
+ ```
146
+
128
147
  ## Public API Overview
129
148
 
130
149
  - `@WebSocketGateway(options)`: Marks a class as a WebSocket gateway.
@@ -132,18 +151,19 @@ Gateway `@OnMessage()` handlers receive one normalized payload contract across s
132
151
  - `@OnMessage(event?)`: Decorator for inbound message handlers.
133
152
  - `@OnDisconnect()`: Decorator for disconnection handlers.
134
153
  - `WebSocketModule`: Root module for WebSocket integration.
135
- - `WebSocketModule.forRoot({ upgrade, limits, backpressure, buffer, heartbeat, shutdown })`: Configures pre-upgrade guards and bounded runtime defaults.
136
- - `WebSocketGatewayLifecycleService`: Root alias for the default Node.js-backed lifecycle service token.
154
+ - `WebSocketModule.forRoot({ upgrade, limits, backpressure, buffer, heartbeat, replies, shutdown })`: Configures pre-upgrade guards, bounded runtime defaults, and optional event-envelope replies.
155
+ - `WebSocketGatewayLifecycleService`: Root alias for `NodeWebSocketGatewayLifecycleService`, the default Node.js-backed lifecycle service token. It is a DI token placeholder, not a concrete class to instantiate directly; resolve it from the application container after `WebSocketModule.forRoot(...)` wires the lazy Node implementation.
137
156
  - `WebSocketRoomService`: Room management contract implemented by runtime lifecycle services for joining, leaving, broadcasting to, and inspecting websocket rooms.
157
+ - Typed runtime seams: `WebSocketUpgradeContext`, `WebSocketUpgradeGuard`, `WebSocketUpgradeRejection`, `WebSocketGatewayDescriptor`, and `WebSocketGatewayHandlerDescriptor`, plus runtime socket/binding types from the Node, Bun, Deno, and Cloudflare Workers subpaths.
138
158
  - Metadata helpers and symbols: `defineWebSocketGatewayMetadata`, `getWebSocketGatewayMetadata`, `defineWebSocketHandlerMetadata`, `getWebSocketHandlerMetadata`, `getWebSocketHandlerMetadataEntries`, `webSocketGatewayMetadataSymbol`, `webSocketHandlerMetadataSymbol`.
139
159
 
140
160
  ## Runtime-Specific Subpaths
141
161
 
142
- Use the runtime subpaths when you want an explicit runtime binding instead of the default root Node.js alias. The root `@fluojs/websockets` entrypoint preserves the Node.js default module and lifecycle-service aliases. Fetch-style applications can import gateway decorators and metadata helpers from their selected runtime subpath so authoring code does not need to load the root Node.js-backed entrypoint.
162
+ Use the runtime subpaths when you want an explicit runtime binding instead of the default root Node.js alias. The root `@fluojs/websockets` entrypoint preserves the Node.js default module export name and aliases `WebSocketGatewayLifecycleService` to the same DI token as `NodeWebSocketGatewayLifecycleService`, but plain root-package imports keep the concrete Node implementation behind lazy runtime provider resolution. Fetch-style applications should still import gateway decorators and metadata helpers from their selected runtime subpath so authoring code does not rely on the root Node.js-backed module boundary.
143
163
 
144
- The package manifest declares `engines.node >=20.0.0` for the published package and default Node.js entrypoint. Bun, Deno, and Cloudflare Workers support is exposed through the dedicated fetch-style subpaths listed below; those subpaths keep request/handler types web-standard and avoid the root Node.js lifecycle-service alias in application code.
164
+ The package manifest declares `engines.node >=24.0.0 <27` for the published package and default Node.js entrypoint, under its package-owned support contract. Upgrade Node 20 and Node 22 hosts to Node.js `>=24.0.0 <27`; Node versions below 24 and Node 27+ are unsupported. Bun, Deno, and Cloudflare Workers support is exposed through the dedicated fetch-style subpaths listed below; those subpaths keep request/handler types web-standard and avoid the root Node.js lifecycle-service alias in application code.
145
165
 
146
- Each subpath exposes its `*WebSocketModule.forRoot(...)` entrypoint, the matching runtime lifecycle service export, and the shared gateway authoring primitives: `WebSocketGateway`, `OnConnect`, `OnMessage`, `OnDisconnect`, `defineWebSocketGatewayMetadata`, `getWebSocketGatewayMetadata`, `defineWebSocketHandlerMetadata`, `getWebSocketHandlerMetadata`, `getWebSocketHandlerMetadataEntries`, `webSocketGatewayMetadataSymbol`, and `webSocketHandlerMetadataSymbol`.
166
+ Each subpath exposes its `*WebSocketModule.forRoot(...)` entrypoint, the matching runtime lifecycle service export, and the shared gateway authoring primitives: `WebSocketGateway`, `OnConnect`, `OnMessage`, `OnDisconnect`, `defineWebSocketGatewayMetadata`, `getWebSocketGatewayMetadata`, `defineWebSocketHandlerMetadata`, `getWebSocketHandlerMetadata`, `getWebSocketHandlerMetadataEntries`, `webSocketGatewayMetadataSymbol`, and `webSocketHandlerMetadataSymbol`. The Bun subpath's low-level binding receives a `BunWebSocketUpgradeHost` with only `upgrade(...)`; adapter-owned listener shutdown and raw HTTP fetch control stay with `@fluojs/platform-bun`.
147
167
 
148
168
  | Runtime | Subpath | Module | Lifecycle service |
149
169
  | --- | --- | --- | --- |
@@ -48,7 +48,6 @@ export declare class BunWebSocketGatewayLifecycleService implements OnApplicatio
48
48
  private runConnectHandlers;
49
49
  private finalizeConnectionBinding;
50
50
  private replayBufferedConnectionEvents;
51
- private resolveBackpressureLimit;
52
51
  private resolveUpgradeRejection;
53
52
  private closeOversizedPayload;
54
53
  private resolveMaxConnectionCount;
@@ -63,11 +62,14 @@ export declare class BunWebSocketGatewayLifecycleService implements OnApplicatio
63
62
  private trackPendingUpgradeOperation;
64
63
  private awaitPendingUpgradeOperations;
65
64
  private closeActiveSockets;
65
+ private scheduleSocketStateCleanup;
66
66
  private awaitHandlerQueueDrain;
67
67
  joinRoom(socketId: string, room: string): void;
68
68
  leaveRoom(socketId: string, room: string): void;
69
69
  broadcastToRoom(room: string, event: string, data: unknown): void;
70
70
  getRooms(socketId: string): ReadonlySet<string>;
71
+ private closeSocketAfterTerminalFailure;
72
+ private unregisterSocketWithDeferredStateCleanup;
71
73
  private unregisterSocket;
72
74
  }
73
75
  //# sourceMappingURL=bun-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bun-service.d.ts","sourceRoot":"","sources":["../../src/bun/bun-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bun-service.d.ts","sourceRoot":"","sources":["../../src/bun/bun-service.ts"],"names":[],"mappings":"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"}
@@ -90,14 +90,12 @@ class BunWebSocketGatewayLifecycleService {
90
90
  return {
91
91
  fetch: (request, server) => this.trackPendingUpgradeOperation(this.handleUpgradeRequest(request, server, descriptorsByPath)),
92
92
  websocket: {
93
- backpressureLimit: this.resolveBackpressureLimit(),
94
93
  close: (socket, code, reason) => {
95
- this.unregisterSocket(socket.data.state.socketId);
94
+ this.unregisterSocketWithDeferredStateCleanup(socket.data.state);
96
95
  this.handleSocketClose(socket, code, reason);
97
96
  },
98
- closeOnBackpressureLimit: this.moduleOptions.backpressure?.policy === 'close',
99
97
  error: (socket, error) => {
100
- this.unregisterSocket(socket.data.state.socketId);
98
+ this.closeSocketAfterTerminalFailure(socket.data.state, socket, 'Socket error');
101
99
  this.logger.error('WebSocket gateway socket emitted an error.', error, LIFECYCLE_LOG_CONTEXT);
102
100
  },
103
101
  idleTimeout: this.resolveIdleTimeoutSeconds(),
@@ -107,7 +105,7 @@ class BunWebSocketGatewayLifecycleService {
107
105
  },
108
106
  open: socket => {
109
107
  void this.trackPendingUpgradeOperation(this.bindConnectionHandlers(socket)).catch(error => {
110
- this.unregisterSocket(socket.data.state.socketId);
108
+ this.unregisterSocketWithDeferredStateCleanup(socket.data.state);
111
109
  this.logger.error('WebSocket gateway open lifecycle failed.', error, LIFECYCLE_LOG_CONTEXT);
112
110
  socket.close(1011, 'Internal server error');
113
111
  });
@@ -213,11 +211,13 @@ class BunWebSocketGatewayLifecycleService {
213
211
  bufferedDisconnect: undefined,
214
212
  bufferedMessages: [],
215
213
  bufferedMessagesStartIndex: 0,
214
+ cleanupScheduled: false,
216
215
  connectLifecycleSettled: false,
217
216
  connectLifecyclePromise: connectLifecycle.promise,
218
217
  descriptors,
219
218
  disconnectLifecycleSettled: false,
220
219
  disconnectLifecyclePromise: disconnectLifecycle.promise,
220
+ disconnectDispatchScheduled: false,
221
221
  enqueuedMessageCount: 0,
222
222
  handlerQueue: Promise.resolve(),
223
223
  handlersReady: false,
@@ -231,7 +231,8 @@ class BunWebSocketGatewayLifecycleService {
231
231
  resolveConnectLifecycle: connectLifecycle.resolve,
232
232
  resolveDisconnectLifecycle: disconnectLifecycle.resolve,
233
233
  resolved: [],
234
- socketId: crypto.randomUUID()
234
+ socketId: crypto.randomUUID(),
235
+ terminalFailureHandled: false
235
236
  };
236
237
  }
237
238
  settleOpenRegistration(state) {
@@ -334,13 +335,16 @@ class BunWebSocketGatewayLifecycleService {
334
335
  this.logger.warn(`WebSocket connection ${state.socketId} dropped the oldest pending message due to buffer limit (${String(limit)}).`, LIFECYCLE_LOG_CONTEXT);
335
336
  }
336
337
  enqueueMessageDispatch(state, socket, message) {
338
+ if (state.terminalFailureHandled) {
339
+ return;
340
+ }
337
341
  const limit = isFinitePositiveInteger(this.moduleOptions.buffer?.maxPendingMessagesPerSocket) ? this.moduleOptions.buffer.maxPendingMessagesPerSocket : DEFAULT_MAX_PENDING_MESSAGES_PER_SOCKET;
338
342
  const policy = this.moduleOptions.buffer?.overflowPolicy ?? 'drop-oldest';
339
343
  if (this.getQueuedMessageCount(state) >= limit) {
340
344
  if (policy === 'close') {
341
345
  socket.close(1013, 'Ready-state message queue limit exceeded');
342
346
  this.clearQueuedMessages(state);
343
- this.unregisterSocket(state.socketId);
347
+ this.unregisterSocketWithDeferredStateCleanup(state);
344
348
  this.logger.warn(`WebSocket connection ${state.socketId} exceeded ready-state message queue limit (${String(limit)}). Connection closed.`, LIFECYCLE_LOG_CONTEXT);
345
349
  return;
346
350
  }
@@ -374,11 +378,25 @@ class BunWebSocketGatewayLifecycleService {
374
378
  if (nextMessage === undefined) {
375
379
  continue;
376
380
  }
377
- await dispatchGatewayMessage(state.resolved, socket, state.request, nextMessage, this.logger, LIFECYCLE_LOG_CONTEXT);
381
+ const outcome = await dispatchGatewayMessage(state.resolved, socket, state.request, nextMessage, state.socketId, this.moduleOptions.replies?.mode, message => {
382
+ if (socket.send(message) !== 0) {
383
+ return true;
384
+ }
385
+ this.closeSocketAfterTerminalFailure(state, socket, 'Send failed');
386
+ return false;
387
+ }, this.logger, LIFECYCLE_LOG_CONTEXT);
388
+ if (outcome === 'terminal') {
389
+ this.clearQueuedMessages(state);
390
+ return;
391
+ }
378
392
  }
379
393
  this.clearQueuedMessages(state);
380
394
  }
381
395
  enqueueDisconnectDispatch(state, socket, disconnectEvent) {
396
+ if (state.disconnectDispatchScheduled) {
397
+ return;
398
+ }
399
+ state.disconnectDispatchScheduled = true;
382
400
  state.handlerQueue = state.handlerQueue.then(async () => {
383
401
  await dispatchGatewayDisconnect(state.resolved, socket, disconnectEvent.code, disconnectEvent.reason, state.socketId, this.logger, LIFECYCLE_LOG_CONTEXT);
384
402
  }).catch(error => {
@@ -426,13 +444,6 @@ class BunWebSocketGatewayLifecycleService {
426
444
  }
427
445
  this.clearBufferedMessages(state);
428
446
  }
429
- resolveBackpressureLimit() {
430
- const configured = this.moduleOptions.backpressure?.maxBufferedAmountBytes;
431
- if (!isFinitePositiveInteger(configured)) {
432
- return 1_048_576;
433
- }
434
- return configured;
435
- }
436
447
  async resolveUpgradeRejection(request, path) {
437
448
  if (this.isShuttingDown) {
438
449
  return {
@@ -543,10 +554,6 @@ class BunWebSocketGatewayLifecycleService {
543
554
  }
544
555
  async runShutdownLifecycle() {
545
556
  this.isShuttingDown = true;
546
- if (hasBunWebSocketBindingHost(this.adapter)) {
547
- const bunAdapter = this.adapter;
548
- bunAdapter.configureWebSocketBinding(undefined);
549
- }
550
557
  const shutdownTimeoutMs = this.resolveShutdownTimeoutMs();
551
558
  await this.awaitPendingUpgradeOperations(shutdownTimeoutMs);
552
559
  await this.closeActiveSockets(shutdownTimeoutMs);
@@ -600,10 +607,7 @@ class BunWebSocketGatewayLifecycleService {
600
607
  }
601
608
  async closeActiveSockets(timeoutMs) {
602
609
  const activeSockets = [...this.socketRegistry.entries()];
603
- if (activeSockets.length === 0) {
604
- return;
605
- }
606
- const activeStates = activeSockets.map(([socketId]) => this.socketStates.get(socketId)).filter(state => state !== undefined);
610
+ const activeStates = [...this.socketStates.values()];
607
611
  for (const [, socket] of activeSockets) {
608
612
  if (socket.readyState === 1) {
609
613
  socket.close(1001, 'Server shutting down');
@@ -611,6 +615,15 @@ class BunWebSocketGatewayLifecycleService {
611
615
  }
612
616
  await this.awaitHandlerQueueDrain(activeStates, timeoutMs);
613
617
  }
618
+ scheduleSocketStateCleanup(state) {
619
+ if (state.cleanupScheduled) {
620
+ return;
621
+ }
622
+ state.cleanupScheduled = true;
623
+ void Promise.all([state.connectLifecyclePromise, state.disconnectLifecyclePromise]).finally(() => {
624
+ this.socketStates.delete(state.socketId);
625
+ });
626
+ }
614
627
  async awaitHandlerQueueDrain(states, timeoutMs) {
615
628
  if (states.length === 0) {
616
629
  return;
@@ -647,6 +660,10 @@ class BunWebSocketGatewayLifecycleService {
647
660
  });
648
661
  }
649
662
  joinRoom(socketId, room) {
663
+ const socket = this.socketRegistry.get(socketId);
664
+ if (!socket || socket.readyState !== 1) {
665
+ return;
666
+ }
650
667
  let rooms = this.socketRooms.get(socketId);
651
668
  if (!rooms) {
652
669
  rooms = new Set();
@@ -688,7 +705,13 @@ class BunWebSocketGatewayLifecycleService {
688
705
  }
689
706
  const result = socket.send(message);
690
707
  if (result === 0) {
691
- this.unregisterSocket(socketId);
708
+ const state = this.socketStates.get(socketId);
709
+ if (state) {
710
+ this.closeSocketAfterTerminalFailure(state, socket, 'Send failed');
711
+ } else {
712
+ socket.close(1011, 'Send failed');
713
+ this.unregisterSocket(socketId);
714
+ }
692
715
  this.logger.warn(`WebSocket connection ${socketId} dropped a room broadcast because the socket was unavailable.`, LIFECYCLE_LOG_CONTEXT);
693
716
  }
694
717
  }
@@ -700,9 +723,35 @@ class BunWebSocketGatewayLifecycleService {
700
723
  }
701
724
  return new Set(rooms);
702
725
  }
703
- unregisterSocket(socketId) {
726
+ closeSocketAfterTerminalFailure(state, socket, reason) {
727
+ if (state.terminalFailureHandled) {
728
+ return;
729
+ }
730
+ state.terminalFailureHandled = true;
731
+ this.clearQueuedMessages(state);
732
+ this.unregisterSocketWithDeferredStateCleanup(state);
733
+ socket.close(1011, reason);
734
+ const disconnectEvent = {
735
+ code: 1011,
736
+ reason
737
+ };
738
+ if (!state.handlersReady) {
739
+ state.bufferedDisconnect ??= disconnectEvent;
740
+ return;
741
+ }
742
+ this.enqueueDisconnectDispatch(state, socket, disconnectEvent);
743
+ }
744
+ unregisterSocketWithDeferredStateCleanup(state) {
745
+ this.unregisterSocket(state.socketId, {
746
+ deleteState: false
747
+ });
748
+ this.scheduleSocketStateCleanup(state);
749
+ }
750
+ unregisterSocket(socketId, options = {}) {
704
751
  this.socketRegistry.delete(socketId);
705
- this.socketStates.delete(socketId);
752
+ if (options.deleteState !== false) {
753
+ this.socketStates.delete(socketId);
754
+ }
706
755
  const rooms = this.socketRooms.get(socketId);
707
756
  if (rooms) {
708
757
  for (const room of rooms) {
@@ -1,4 +1,4 @@
1
- import type { WebSocketModuleOptions as SharedWebSocketModuleOptions, WebSocketUpgradeContext, WebSocketUpgradeGuard as SharedWebSocketUpgradeGuard, WebSocketUpgradeRejection } from '../types.js';
1
+ import type { WebSocketModuleOptions as SharedWebSocketModuleOptions, WebSocketUpgradeGuard as SharedWebSocketUpgradeGuard, WebSocketUpgradeContext, WebSocketUpgradeRejection } from '../types.js';
2
2
  /**
3
3
  * Defines the bun web socket message type.
4
4
  */
@@ -20,7 +20,9 @@ export interface BunServerWebSocket<TData = unknown> {
20
20
  unsubscribe(topic: string): void;
21
21
  }
22
22
  /**
23
- * Describes the bun server like contract.
23
+ * Describes the legacy complete Bun server handle used by host doubles.
24
+ *
25
+ * @deprecated Websocket bindings receive {@link BunWebSocketUpgradeHost}. Keep listener lifecycle and raw fetch ownership in `@fluojs/platform-bun`.
24
26
  */
25
27
  export interface BunServerLike {
26
28
  fetch?(request: Request): Response | Promise<Response> | undefined | Promise<Response | undefined>;
@@ -33,6 +35,15 @@ export interface BunServerLike {
33
35
  }): boolean;
34
36
  url?: URL;
35
37
  }
38
+ /**
39
+ * Describes the upgrade-only Bun host exposed to websocket bindings.
40
+ */
41
+ export interface BunWebSocketUpgradeHost {
42
+ upgrade<TData = unknown>(request: Request, options?: {
43
+ data?: TData;
44
+ headers?: HeadersInit;
45
+ }): boolean;
46
+ }
36
47
  /**
37
48
  * Describes the bun web socket handler contract.
38
49
  */
@@ -58,7 +69,7 @@ export interface BunWebSocketHandler<TData = unknown> {
58
69
  * Describes the bun web socket binding contract.
59
70
  */
60
71
  export interface BunWebSocketBinding<TData = unknown> {
61
- fetch(request: Request, server: BunServerLike): Response | Promise<Response> | undefined | Promise<Response | undefined>;
72
+ fetch(request: Request, server: BunWebSocketUpgradeHost): Response | Promise<Response> | undefined | Promise<Response | undefined>;
62
73
  websocket: BunWebSocketHandler<TData>;
63
74
  }
64
75
  /**
@@ -70,7 +81,7 @@ export interface BunWebSocketBindingHost {
70
81
  /**
71
82
  * Defines the typed on message handler type.
72
83
  */
73
- export type TypedOnMessageHandler<TEvents extends Record<string, unknown>, K extends keyof TEvents> = (payload: TEvents[K], socket: BunServerWebSocket, request: Request) => void | Promise<void>;
84
+ export type TypedOnMessageHandler<TEvents extends Record<string, unknown>, K extends keyof TEvents> = import('../types.js').TypedOnMessageHandler<TEvents, K, BunServerWebSocket, Request>;
74
85
  /**
75
86
  * Describes the web socket gateway context contract.
76
87
  */
@@ -1 +1 @@
1
- {"version":3,"file":"bun-types.d.ts","sourceRoot":"","sources":["../../src/bun/bun-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bun-types.d.ts","sourceRoot":"","sources":["../../src/bun/bun-types.ts"],"names":[],"mappings":"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"}
package/dist/bun.d.ts CHANGED
@@ -1,4 +1,5 @@
1
+ export * from './bun/bun.js';
1
2
  export * from './decorators.js';
2
3
  export * from './metadata.js';
3
- export * from './bun/bun.js';
4
+ export type { WebSocketEventEnvelope, WebSocketReplyMode } from './types.js';
4
5
  //# sourceMappingURL=bun.d.ts.map
package/dist/bun.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../src/bun.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../src/bun.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
package/dist/bun.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './bun/bun.js';
1
2
  export * from './decorators.js';
2
3
  export * from './metadata.js';
3
- export * from './bun/bun.js';
4
+ export {};
@@ -61,11 +61,15 @@ export declare class CloudflareWorkersWebSocketGatewayLifecycleService implement
61
61
  private trackPendingUpgradeOperation;
62
62
  private awaitPendingUpgradeOperations;
63
63
  private closeActiveSockets;
64
+ private scheduleSocketStateCleanup;
64
65
  private awaitHandlerQueueDrain;
65
66
  joinRoom(socketId: string, room: string): void;
66
67
  leaveRoom(socketId: string, room: string): void;
67
68
  broadcastToRoom(room: string, event: string, data: unknown): void;
68
69
  getRooms(socketId: string): ReadonlySet<string>;
70
+ private closeSocketAfterTerminalFailure;
71
+ private unregisterSocketWithDeferredStateCleanup;
72
+ private unregisterTrackedSocketWithDeferredStateCleanup;
69
73
  private unregisterSocket;
70
74
  }
71
75
  //# sourceMappingURL=cloudflare-workers-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare-workers-service.d.ts","sourceRoot":"","sources":["../../src/cloudflare-workers/cloudflare-workers-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cloudflare-workers-service.d.ts","sourceRoot":"","sources":["../../src/cloudflare-workers/cloudflare-workers-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAgBzI,OAAO,KAAK,EAA8B,oBAAoB,EAA6B,MAAM,aAAa,CAAC;AAC/G,OAAO,KAAK,EAIV,sBAAsB,EACvB,MAAM,+BAA+B,CAAC;AAoEvC;;GAEG;AACH,qBACa,iDACX,YAAW,sBAAsB,EAAE,qBAAqB,EAAE,eAAe,EAAE,oBAAoB;IAY7F,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAdhC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA4B;IACrE,OAAO,CAAC,0BAA0B,CAAK;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgD;IAC/E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6C;gBAGvD,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,sBAAsB,EAC/B,aAAa,EAAE,sBAAsB;IAGlD,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,OAAO,CAAC,aAAa;YAUP,oBAAoB;IA4DlC,OAAO,CAAC,sBAAsB;YAmBhB,sBAAsB;IA8BpC,OAAO,CAAC,4BAA4B;IAiCpC,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,yBAAyB;IASjC,OAAO,CAAC,yBAAyB;IAyCjC,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,4BAA4B;IASpC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,qBAAqB;IA0C7B,OAAO,CAAC,sBAAsB;YA4DhB,iBAAiB;YA4CjB,gBAAgB;IAU9B,OAAO,CAAC,yBAAyB;YA8BnB,yBAAyB;YAczB,kBAAkB;YAgBlB,yBAAyB;YAUzB,8BAA8B;IAqB5C,OAAO,CAAC,YAAY;YAUN,uBAAuB;IAmDrC,OAAO,CAAC,qBAAqB;IAmB7B,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,8BAA8B;IAItC,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,yBAAyB;IAMjC,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,wBAAwB;YAUlB,QAAQ;YAUR,oBAAoB;IAelC,OAAO,CAAC,4BAA4B;YAyBtB,6BAA6B;YA4C7B,kBAAkB;IAahC,OAAO,CAAC,0BAA0B;YAWpB,sBAAsB;IAkDpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAyB9C,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAc/C,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAkCjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;IAU/C,OAAO,CAAC,+BAA+B;IAuBvC,OAAO,CAAC,wCAAwC;IAKhD,OAAO,CAAC,+CAA+C;IAWvD,OAAO,CAAC,gBAAgB;CAuBzB"}