@steijnveer/fbr-plugin-io 0.0.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/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # @steijnveer/fbr-plugin-io
2
+
3
+ Socket.io integration plugin for [@steijnveer/file-based-router](https://github.com/steijnveer/file-based-router)
4
+
5
+ ## Overview
6
+
7
+ This plugin adds Socket.io support to your file-based-router application with automatic event handler discovery and registration. Define your Socket.io event handlers in files, and the plugin will automatically load and register them.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @steijnveer/fbr-plugin-io
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ### 1. Configure the plugin
18
+
19
+ ```typescript
20
+ // '/fbr.config.ts'
21
+ import ioPlugin from '@steijnveer/fbr-plugin-io';
22
+ import defineConfig from '@steijnveer/file-based-router/defineConfig';
23
+
24
+ export default defineConfig({
25
+ plugins: [
26
+ ioPlugin({
27
+ eventsDir: 'src\\events', // Directory containing event handlers
28
+ extensions: ['.ts', '.js'] // File extensions to load
29
+ })
30
+ ]
31
+ });
32
+ ```
33
+
34
+ ### 2. Create event handlers
35
+
36
+ Create event handler files in your events directory (default: `src/events`):
37
+
38
+ **src/events/message.ts**
39
+ ```typescript
40
+ import type { Socket } from '@steijnveer/fbr-plugin-io';
41
+
42
+ // Export named functions - function name becomes the event name
43
+ export function message(socket: Socket, data: { text: string }) {
44
+ log('Received message:', data.text);
45
+ socket.emit('message:response', { echo: data.text });
46
+ }
47
+ ```
48
+
49
+ **src/events/chat.ts**
50
+ ```typescript
51
+ import type { Socket } from '@steijnveer/fbr-plugin-io';
52
+
53
+ export function join(socket: Socket, data: { room: string }) {
54
+ socket.join(data.room);
55
+ socket.to(data.room).emit('user:joined', { id: socket.id });
56
+ }
57
+
58
+ export function leave(socket: Socket, data: { room: string }) {
59
+ socket.leave(data.room);
60
+ socket.to(data.room).emit('user:left', { id: socket.id });
61
+ }
62
+ ```
63
+
64
+ **src/events/index.ts** (for connection handlers)
65
+ ```typescript
66
+ import type { Socket } from '@steijnveer/fbr-plugin-io';
67
+
68
+ // Special 'connection' event handlers
69
+ export function connection(socket: Socket) {
70
+ log('User connected: ' + socket.id);
71
+ socket.emit('welcome', { message: 'Welcome to the server!' });
72
+ }
73
+ ```
74
+
75
+ ### 3. Access Socket.io server instance
76
+
77
+ The Socket.io server instance is available on your server object:
78
+
79
+ ```typescript
80
+ import type { Io } from '@steijnveer/fbr-plugin-io';
81
+
82
+ // Emit to all connected clients
83
+ server._io.emit('broadcast', { message: 'Hello everyone!' });
84
+
85
+ // Access specific rooms
86
+ server._io.to('room-name').emit('room:message', { text: 'Hello room!' });
87
+ ```
88
+
89
+ ## Configuration
90
+
91
+ ### Plugin Options
92
+
93
+ ```typescript
94
+ interface IoPluginConfig {
95
+ eventsDir?: string; // Directory containing event handlers (default: 'src\\events')
96
+ extensions?: string[]; // File extensions to load (default: ['.ts', '.js'])
97
+ }
98
+ ```
99
+
100
+ ## Event Handler Conventions
101
+
102
+ ### Event Naming
103
+
104
+ - **File name becomes event prefix**: `message.ts` → `message` event
105
+ - **Named exports**: Use function name as event name
106
+ - `export function join()` in `chat.ts` → `chat:join` event
107
+ - **Default exports**: Use file name as event name
108
+ - `export default function()` in `message.ts` → `message` event
109
+ - **index.ts**: Exports use direct function names
110
+ - `export function connection()` in `index.ts` → `connection` event
111
+
112
+ ### Event Handler Signature
113
+
114
+ ```typescript
115
+ type EventHandler = (socket: Socket, data: any) => void;
116
+ ```
117
+
118
+ - **socket**: The Socket.io socket instance for the connected client
119
+ - **data**: The data sent from the client (null if no data provided)
120
+
121
+ ### Special Events
122
+
123
+ - **connection**: Handlers named `connection` are executed when a client connects
124
+ - These handlers run after other event listeners are attached to the socket
125
+
126
+ ## Examples
127
+
128
+ ### Broadcasting to all clients
129
+
130
+ **src/events/admin.ts**
131
+ ```typescript
132
+ import type { Socket } from '@steijnveer/fbr-plugin-io';
133
+
134
+ export function announce(socket: Socket, data: { message: string }) {
135
+ // Broadcast to all clients including sender
136
+ socket.server.emit('announcement', { message: data.message });
137
+ }
138
+ ```
139
+
140
+ ### Room-based chat
141
+
142
+ **src/events/room.ts**
143
+ ```typescript
144
+ import type { Socket } from '@steijnveer/fbr-plugin-io';
145
+
146
+ export function join(socket: Socket, data: { roomId: string }) {
147
+ socket.join(data.roomId);
148
+ socket.to(data.roomId).emit('room:userJoined', {
149
+ userId: socket.id,
150
+ roomId: data.roomId
151
+ });
152
+ }
153
+
154
+ export function message(socket: Socket, data: { roomId: string, text: string }) {
155
+ socket.to(data.roomId).emit('room:message', {
156
+ userId: socket.id,
157
+ text: data.text,
158
+ timestamp: Date.now()
159
+ });
160
+ }
161
+ ```
162
+
163
+ ## TypeScript Support
164
+
165
+ The plugin includes full TypeScript support with type definitions:
166
+
167
+ ```typescript
168
+ import type {
169
+ Socket, // Socket.io socket instance with type safety
170
+ Io, // Socket.io server instance
171
+ EventsMap, // Event name to handler mapping
172
+ IoPluginConfig // Plugin configuration options
173
+ } from '@steijnveer/fbr-plugin-io';
174
+ ```
175
+
176
+ ## Client-Side Usage
177
+
178
+ ```html
179
+ <!DOCTYPE html>
180
+ <html>
181
+ <head>
182
+ <script src="/socket.io/socket.io.js"></script>
183
+ </head>
184
+ <body>
185
+ <script>
186
+ const socket = io();
187
+
188
+ // Listen for events
189
+ socket.on('welcome', (data) => {
190
+ console.log(data.message);
191
+ });
192
+
193
+ // Emit events (matches your handler in message.ts)
194
+ socket.emit('message', { text: 'Hello server!' });
195
+
196
+ // Listen for responses
197
+ socket.on('message:response', (data) => {
198
+ console.log('Echo:', data.echo);
199
+ });
200
+ </script>
201
+ </body>
202
+ </html>
203
+ ```
204
+
205
+ ## Debug Logging
206
+
207
+ The plugin includes built-in debug logging for:
208
+ - User connections/disconnections
209
+ - All incoming events with data
210
+ - All outgoing events with data
211
+
212
+ Logs include the socket ID for easy debugging.
213
+
214
+ ## License
215
+
216
+ MIT
@@ -0,0 +1,8 @@
1
+ import type { Server } from '@steijnveer/file-based-router';
2
+ import '@steijnveer/file-based-router/utils';
3
+ import type { IoPluginConfig } from './types';
4
+ declare function ioPlugin({ eventsDir, extensions }?: IoPluginConfig): (server: Server) => Promise<void>;
5
+ export default ioPlugin;
6
+ export type * from './types';
7
+ export { ioPlugin };
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,qCAAqC,CAAC;AAK7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAqB9C,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,GAAE,cAAmB,IAEhD,QAAQ,MAAM,mBAyB7B;AAGD,eAAe,QAAQ,CAAC;AACxB,mBAAmB,SAAS,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,54 @@
1
+ import '@steijnveer/file-based-router/utils';
2
+ import { readdirSync } from 'fs';
3
+ import { extname, join, resolve } from 'path';
4
+ import { Server as Io } from 'socket.io';
5
+ import { pathToFileURL } from 'url';
6
+ async function importEvents(eventsDir, extensions) {
7
+ const eventsDirPath = resolve(eventsDir);
8
+ const files = readdirSync(eventsDirPath, { withFileTypes: true })
9
+ .filter(entry => entry.isFile() && extensions.includes(extname(entry.name)));
10
+ const events = await Promise.all(files.map(async (file) => {
11
+ const fileName = file.name.replace(extname(file.name), '');
12
+ const module = await import(pathToFileURL(join(eventsDirPath, fileName)).href);
13
+ const eventHandlers = Object.entries(module)
14
+ .filter(([_, handler]) => typeof handler === 'function');
15
+ return fileName === 'index'
16
+ ? eventHandlers
17
+ : eventHandlers.map(([exportName, handler]) => [
18
+ exportName === 'default'
19
+ ? fileName
20
+ : `${fileName}:${exportName}`, handler
21
+ ]);
22
+ }));
23
+ return events.flat();
24
+ }
25
+ function ioPlugin({ eventsDir, extensions } = {}) {
26
+ const eventsPromise = importEvents(eventsDir ?? 'src\\events', extensions ?? ['.ts', '.js']);
27
+ return async (server) => {
28
+ server._io = new Io(server._httpServer);
29
+ const allEvents = await eventsPromise;
30
+ const events = allEvents
31
+ .filter(([eventName]) => eventName !== 'connection');
32
+ const connectionhandlers = allEvents
33
+ .filter(([eventName]) => eventName === 'connection')
34
+ .map(([_, handler]) => handler);
35
+ server._io.on('connection', (socket) => {
36
+ log(`a user connected: ${socket.id}`);
37
+ socket.on('disconnect', () => {
38
+ log(`(${socket.id}) user disconnected`);
39
+ });
40
+ socket.onAny((event, data) => {
41
+ log(`(${socket.id}) Received event: ${event} with args: ${JSON.stringify(data)}`);
42
+ });
43
+ socket.onAnyOutgoing((event, data) => {
44
+ log(`(${socket.id}) Emitting event: ${event} with args: ${JSON.stringify(data)}`);
45
+ });
46
+ for (const [eventName, handler] of events)
47
+ socket.on(eventName, (data) => handler(socket, data ?? null));
48
+ for (const handler of connectionhandlers)
49
+ handler(socket, null);
50
+ });
51
+ };
52
+ }
53
+ export default ioPlugin;
54
+ export { ioPlugin };
@@ -0,0 +1,16 @@
1
+ import '@steijnveer/file-based-router/utils';
2
+ import type { Server as IoServer, Socket as IoSocket } from 'socket.io';
3
+ type IoPluginConfig = {
4
+ eventsDir?: string;
5
+ extensions?: string[];
6
+ };
7
+ type EventsMap = Record<string, (data?: any) => void>;
8
+ type Io<SocketData = any> = IoServer<EventsMap, EventsMap, never, SocketData>;
9
+ type Socket<SocketData = any> = IoSocket<EventsMap, EventsMap, never, SocketData>;
10
+ declare module '@steijnveer/file-based-router' {
11
+ interface Server {
12
+ _io: Io;
13
+ }
14
+ }
15
+ export type { EventsMap, Io, IoPluginConfig, Socket };
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,WAAW,CAAC;AAExE,KAAK,cAAc,GAAG;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;AAEtD,KAAK,EAAE,CAAC,UAAU,GAAG,GAAG,IAAI,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;AAE9E,KAAK,MAAM,CAAC,UAAU,GAAG,GAAG,IAAI,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;AAElF,OAAO,QAAQ,+BAA+B,CAAC;IAC7C,UAAU,MAAM;QACd,GAAG,EAAE,EAAE,CAAC;KACT;CACF;AAGD,YAAY,EAAE,SAAS,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ import '@steijnveer/file-based-router/utils';
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "author": "Steijn Veer",
3
+ "dependencies": {
4
+ "@steijnveer/file-based-router": "^0.0.9",
5
+ "socket.io": "^4.8.3"
6
+ },
7
+ "description": "fbr plugin: Socket.io integration",
8
+ "devDependencies": {
9
+ "@types/express": "^5.0.6",
10
+ "@types/node": "^24.10.1",
11
+ "@types/socket.io": "^3.0.1",
12
+ "typescript": "~5.9.3"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "import": "./dist/index.js",
17
+ "types": "./dist/index.d.ts"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "keywords": [
25
+ "fbr",
26
+ "io",
27
+ "socket.io",
28
+ "file-based",
29
+ "plugin",
30
+ "typescript",
31
+ "websocket"
32
+ ],
33
+ "license": "MIT",
34
+ "main": "./dist/index.js",
35
+ "name": "@steijnveer/fbr-plugin-io",
36
+ "scripts": {
37
+ "build": "tsc",
38
+ "prepublishOnly": "npm run build"
39
+ },
40
+ "type": "module",
41
+ "types": "./dist/index.d.ts",
42
+ "version": "0.0.1"
43
+ }