@fluojs/socket.io 1.0.0-beta.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fluo contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.ko.md ADDED
@@ -0,0 +1,124 @@
1
+ # @fluojs/socket.io
2
+
3
+ <p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
4
+
5
+ fluo 런타임용 Socket.IO v4 게이트웨이 어댑터입니다.
6
+
7
+ ## 목차
8
+
9
+ - [설치](#설치)
10
+ - [사용 시점](#사용-시점)
11
+ - [빠른 시작](#빠른-시작)
12
+ - [주요 패턴](#주요-패턴)
13
+ - [공개 API 개요](#공개-api-개요)
14
+ - [지원 플랫폼](#지원-플랫폼)
15
+ - [예제 소스](#예제-소스)
16
+
17
+ ## 설치
18
+
19
+ ```bash
20
+ npm install @fluojs/core @fluojs/socket.io @fluojs/websockets socket.io
21
+ ```
22
+
23
+ ## 사용 시점
24
+
25
+ Socket.IO가 제공하는 room, namespace, broadcast, 자동 재연결 같은 고수준 실시간 기능이 필요할 때 사용합니다. 이 패키지는 raw websocket 대신 Socket.IO v4 서버를 fluo의 `@WebSocketGateway` 기반 모델에 연결합니다.
26
+
27
+ ## 빠른 시작
28
+
29
+ ```ts
30
+ import { Inject, Module } from '@fluojs/core';
31
+ import { SOCKETIO_ROOM_SERVICE, SocketIoModule, type SocketIoRoomService } from '@fluojs/socket.io';
32
+ import { OnMessage, WebSocketGateway } from '@fluojs/websockets';
33
+
34
+ @Inject(SOCKETIO_ROOM_SERVICE)
35
+ @WebSocketGateway({ path: '/chat' })
36
+ class ChatGateway {
37
+ constructor(private readonly rooms: SocketIoRoomService) {}
38
+
39
+ @OnMessage('ping')
40
+ handlePing(payload: unknown) {
41
+ this.rooms.broadcastToRoom('chat:lobby', 'pong', payload);
42
+ }
43
+ }
44
+
45
+ @Module({
46
+ imports: [SocketIoModule.forRoot()],
47
+ providers: [ChatGateway],
48
+ })
49
+ export class AppModule {}
50
+ ```
51
+
52
+ ## 주요 패턴
53
+
54
+ ### Room 관리
55
+
56
+ ```ts
57
+ this.rooms.joinRoom(socket.id, 'room:123');
58
+ this.rooms.broadcastToRoom('room:123', 'event', data);
59
+ ```
60
+
61
+ ### Raw Socket.IO 서버 접근
62
+
63
+ ```ts
64
+ import { SOCKETIO_SERVER } from '@fluojs/socket.io';
65
+ import type { Server } from 'socket.io';
66
+
67
+ @Inject(SOCKETIO_SERVER)
68
+ class MyService {
69
+ constructor(private readonly io: Server) {}
70
+ }
71
+ ```
72
+
73
+ ### auth guard, 안전한 CORS 기본값, bounded payload
74
+ `SocketIoModule.forRoot(...)`로 namespace/message 인증을 명시하고, CORS를 deny-by-default로 유지하며, 인바운드 Engine.IO payload 크기를 제한할 수 있습니다.
75
+
76
+ ```ts
77
+ SocketIoModule.forRoot({
78
+ auth: {
79
+ connection({ socket }) {
80
+ return socket.handshake.auth.token === 'demo-token'
81
+ ? true
82
+ : { message: 'Authentication required.' };
83
+ },
84
+ message({ payload }) {
85
+ return payload === 'allowed'
86
+ ? true
87
+ : { message: 'Forbidden event.' };
88
+ },
89
+ },
90
+ cors: {
91
+ origin: ['https://app.example.com'],
92
+ },
93
+ engine: {
94
+ maxHttpBufferSize: 65_536,
95
+ },
96
+ });
97
+ ```
98
+
99
+ 이제 `cors`를 생략하면 `@fluojs/socket.io`는 `origin: false`를 기본값으로 사용하므로 cross-origin 노출은 명시적 opt-in이 필요합니다. `engine.maxHttpBufferSize`를 생략하면 어댑터가 1 MiB Engine.IO payload 상한을 적용합니다.
100
+
101
+ ### 모듈 등록
102
+ `SocketIoModule.forRoot(...)`로 Socket.IO를 등록합니다.
103
+
104
+ Socket.IO 등록은 소유 모듈의 import 경로에서 구성하여 namespace/message guard, CORS, Engine.IO 옵션을 한 곳에서 관리합니다.
105
+
106
+ ## 공개 API 개요
107
+
108
+ - `SocketIoModule.forRoot(options)`
109
+ - `SocketIoModule.forRoot({ auth, cors, engine, ... })`
110
+ - `SOCKETIO_SERVER`
111
+ - `SOCKETIO_ROOM_SERVICE`
112
+
113
+ ## 지원 플랫폼
114
+
115
+ | 플랫폼 | 지원 여부 | 비고 |
116
+ | --- | --- | --- |
117
+ | Node.js (Raw/Express/Fastify) | ✅ 전체 지원 | server-backed mode |
118
+ | Bun | ✅ 전체 지원 | `@socket.io/bun-engine` 기반 |
119
+ | Deno | ❌ 미지원 | 현재 지원하지 않음 |
120
+ | Workers | ❌ 미지원 | 현재 지원하지 않음 |
121
+
122
+ ## 예제 소스
123
+
124
+ - `packages/socket.io/src/module.test.ts`
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # @fluojs/socket.io
2
+
3
+ <p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
4
+
5
+ Socket.IO v4 gateway adapter for the fluo runtime.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Installation](#installation)
10
+ - [When to Use](#when-to-use)
11
+ - [Quick Start](#quick-start)
12
+ - [Common Patterns](#common-patterns)
13
+ - [Public API Overview](#public-api-overview)
14
+ - [Supported Platforms](#supported-platforms)
15
+ - [Example Sources](#example-sources)
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @fluojs/core @fluojs/socket.io @fluojs/websockets socket.io
21
+ ```
22
+
23
+ ## When to Use
24
+
25
+ Use this package when you need advanced real-time features like rooms, namespaces, broadcasting, and automatic reconnection provided by [Socket.IO](https://socket.io/). This adapter integrates Socket.IO v4 into fluo's decorator-based architecture, sharing the same `@WebSocketGateway` core as raw websockets.
26
+
27
+ ## Quick Start
28
+
29
+ ```typescript
30
+ import { SocketIoModule, SOCKETIO_ROOM_SERVICE, type SocketIoRoomService } from '@fluojs/socket.io';
31
+ import { WebSocketGateway, OnMessage } from '@fluojs/websockets';
32
+ import { Inject, Module } from '@fluojs/core';
33
+
34
+ @Inject(SOCKETIO_ROOM_SERVICE)
35
+ @WebSocketGateway({ path: '/chat' })
36
+ class ChatGateway {
37
+ constructor(private readonly rooms: SocketIoRoomService) {}
38
+
39
+ @OnMessage('ping')
40
+ handlePing(payload: unknown) {
41
+ this.rooms.broadcastToRoom('chat:lobby', 'pong', payload);
42
+ }
43
+ }
44
+
45
+ @Module({
46
+ imports: [SocketIoModule.forRoot()],
47
+ providers: [ChatGateway],
48
+ })
49
+ export class AppModule {}
50
+ ```
51
+
52
+ ## Common Patterns
53
+
54
+ ### Room Management
55
+ The `SocketIoRoomService` provides a high-level API for managing client rooms and broadcasting.
56
+
57
+ ```typescript
58
+ this.rooms.joinRoom(socket.id, 'room:123');
59
+ this.rooms.broadcastToRoom('room:123', 'event', data);
60
+ ```
61
+
62
+ ### Accessing the Raw Server
63
+ You can inject the underlying Socket.IO `Server` instance for low-level control.
64
+
65
+ ```typescript
66
+ import { SOCKETIO_SERVER } from '@fluojs/socket.io';
67
+ import type { Server } from 'socket.io';
68
+
69
+ @Inject(SOCKETIO_SERVER)
70
+ class MyService {
71
+ constructor(private readonly io: Server) {}
72
+ }
73
+ ```
74
+
75
+ ### Auth guards, safe CORS defaults, and bounded payloads
76
+ Use `SocketIoModule.forRoot(...)` to require explicit namespace/message auth, keep CORS in a deny-by-default posture, and cap inbound Engine.IO payload size.
77
+
78
+ ```typescript
79
+ SocketIoModule.forRoot({
80
+ auth: {
81
+ connection({ socket }) {
82
+ return socket.handshake.auth.token === 'demo-token'
83
+ ? true
84
+ : { message: 'Authentication required.' };
85
+ },
86
+ message({ payload }) {
87
+ return payload === 'allowed'
88
+ ? true
89
+ : { message: 'Forbidden event.' };
90
+ },
91
+ },
92
+ cors: {
93
+ origin: ['https://app.example.com'],
94
+ },
95
+ engine: {
96
+ maxHttpBufferSize: 65_536,
97
+ },
98
+ });
99
+ ```
100
+
101
+ When `cors` is omitted, `@fluojs/socket.io` now defaults to `origin: false` so cross-origin exposure stays opt-in. When `engine.maxHttpBufferSize` is omitted, the adapter applies a bounded 1 MiB Engine.IO payload limit.
102
+
103
+ ### Module registration
104
+ Register Socket.IO with `SocketIoModule.forRoot(...)`.
105
+
106
+ Register Socket.IO through module imports in the owning module so namespace/message guards, CORS, and Engine.IO options stay configured in one place.
107
+
108
+ ## Public API Overview
109
+
110
+ - `SocketIoModule.forRoot(options)`: Main module for Socket.IO integration.
111
+ - `SocketIoModule.forRoot({ auth, cors, engine, ... })`: Configures namespace/message guards plus explicit CORS and Engine.IO payload bounds.
112
+ - `SOCKETIO_SERVER`: Token to inject the raw Socket.IO `Server`.
113
+ - `SOCKETIO_ROOM_SERVICE`: Token to inject the `SocketIoRoomService`.
114
+
115
+ ## Supported Platforms
116
+
117
+ | Platform | Support | Note |
118
+ | --- | --- | --- |
119
+ | Node.js (Raw/Express/Fastify) | ✅ Full | Server-backed mode |
120
+ | Bun | ✅ Full | Via `@socket.io/bun-engine` |
121
+ | Deno | ❌ None | Not currently supported |
122
+ | Workers | ❌ None | Not currently supported |
123
+
124
+ ## Example Sources
125
+
126
+ - `packages/socket.io/src/module.test.ts`
@@ -0,0 +1,118 @@
1
+ import type { Container } from '@fluojs/di';
2
+ import type { HttpApplicationAdapter } from '@fluojs/http';
3
+ import type { ApplicationLogger, CompiledModule, OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy } from '@fluojs/runtime';
4
+ import { Server } from 'socket.io';
5
+ import type { SocketIoModuleOptions, SocketIoRoomService } from './types.js';
6
+ /**
7
+ * Lifecycle service that boots Socket.IO gateways, exposes the raw server, and implements room helpers.
8
+ *
9
+ * @remarks
10
+ * This service preserves the README-level gateway contracts for namespace discovery, buffered pre-ready events,
11
+ * Bun engine hosting, and graceful shutdown behavior.
12
+ */
13
+ export declare class SocketIoLifecycleService implements OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy, SocketIoRoomService {
14
+ private readonly runtimeContainer;
15
+ private readonly compiledModules;
16
+ private readonly logger;
17
+ private readonly adapter;
18
+ private readonly moduleOptions;
19
+ private attachments;
20
+ private bunEngine;
21
+ private io;
22
+ private readonly namespaceContext;
23
+ private readonly socketRegistry;
24
+ private shutdownPromise;
25
+ private wired;
26
+ constructor(runtimeContainer: Container, compiledModules: readonly CompiledModule[], logger: ApplicationLogger, adapter: HttpApplicationAdapter, moduleOptions: SocketIoModuleOptions);
27
+ /**
28
+ * Returns the managed Socket.IO server instance, creating and binding it on first access.
29
+ *
30
+ * @returns The shared Socket.IO `Server` used by this adapter instance.
31
+ * @throws {Error} When the selected realtime capability cannot expose the server contract Socket.IO requires.
32
+ */
33
+ getServer(): Server;
34
+ /**
35
+ * Discovers gateway classes and binds their handlers to the resolved namespaces once per application lifecycle.
36
+ */
37
+ onApplicationBootstrap(): Promise<void>;
38
+ /**
39
+ * Shuts down Socket.IO resources during application shutdown.
40
+ */
41
+ onApplicationShutdown(): Promise<void>;
42
+ /**
43
+ * Shuts down Socket.IO resources when the containing module is destroyed.
44
+ */
45
+ onModuleDestroy(): Promise<void>;
46
+ /**
47
+ * Adds a socket to one room, using the current gateway namespace or an explicit namespace override.
48
+ *
49
+ * @param socketId Socket identifier to move into the room.
50
+ * @param room Room identifier to join.
51
+ * @param namespacePath Optional namespace path required when the helper runs outside gateway handler context.
52
+ */
53
+ joinRoom(socketId: string, room: string, namespacePath?: string): void;
54
+ /**
55
+ * Removes a socket from one room, using the current gateway namespace or an explicit namespace override.
56
+ *
57
+ * @param socketId Socket identifier to remove from the room.
58
+ * @param room Room identifier to leave.
59
+ * @param namespacePath Optional namespace path required when the helper runs outside gateway handler context.
60
+ */
61
+ leaveRoom(socketId: string, room: string, namespacePath?: string): void;
62
+ /**
63
+ * Emits one event payload to every socket currently joined to a room.
64
+ *
65
+ * @param room Room identifier that should receive the event.
66
+ * @param event Socket.IO event name emitted to the room.
67
+ * @param data Payload delivered with the event.
68
+ * @param namespacePath Optional namespace path required when the helper runs outside gateway handler context.
69
+ */
70
+ broadcastToRoom(room: string, event: string, data: unknown, namespacePath?: string): void;
71
+ /**
72
+ * Returns the current room set tracked for one connected socket.
73
+ *
74
+ * @param socketId Socket identifier to inspect.
75
+ * @returns A snapshot of the rooms currently joined by that socket.
76
+ */
77
+ getRooms(socketId: string): ReadonlySet<string>;
78
+ private createServerOptions;
79
+ private createBunEngineOptions;
80
+ private resolveCorsOptions;
81
+ private resolveMaxHttpBufferSize;
82
+ private installBunSocketIoBinding;
83
+ private createBunSocketIoBinding;
84
+ private tryResolvePathname;
85
+ private matchesSocketIoEnginePath;
86
+ private assertNoServerBackedGatewayOptIn;
87
+ private prepareNamespaceAttachments;
88
+ private resolveNamespace;
89
+ private resolveContextNamespace;
90
+ private resolveRequiredNamespace;
91
+ private resolveSocket;
92
+ private bindNamespaceHandlers;
93
+ private runConnectionGuard;
94
+ private bindConnectionHandlers;
95
+ private createConnectionHandlerState;
96
+ private maxPendingMessagesPerSocket;
97
+ private attachConnectionListeners;
98
+ private replayBufferedConnectionEvents;
99
+ private resolveConnectionGateways;
100
+ private runConnectHandlers;
101
+ private handleMessage;
102
+ private resolveMessageGuardRejection;
103
+ private reportRejectedMessage;
104
+ private selectMessageHandlers;
105
+ private handleDisconnect;
106
+ private runHandlers;
107
+ private invokeGatewayMethod;
108
+ private resolveGatewayInstance;
109
+ private discoverGatewayDescriptors;
110
+ private shouldSkipGatewayCandidate;
111
+ private createGatewayDescriptor;
112
+ private discoveryCandidates;
113
+ private resolveShutdownTimeoutMs;
114
+ private shutdown;
115
+ private runShutdownLifecycle;
116
+ private closeServerWithTimeout;
117
+ }
118
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}