@beespoke/micro-server-websocket-bun 0.0.0-stage → 2.0.5
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 +79 -2
- package/dist/BaseSocketHandler.d.ts +13 -0
- package/dist/BaseSocketType.d.ts +5 -0
- package/dist/PluginContext.d.ts +13 -0
- package/dist/WebSocketServer.d.ts +36 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +224 -0
- package/dist/plugin/index.d.ts +10 -0
- package/dist/plugin/index.js +49 -0
- package/package.json +35 -4
package/README.md
CHANGED
|
@@ -1,3 +1,80 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @beespoke/micro-server-websocket-bun
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Bun-native WebSocket server, handler discovery, and application lifecycle integration. The WebSocket listener requires Bun 1.3.14+ and uses its own port, separate from core's HTTP server.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bun add @beespoke/micro-server @beespoke/micro-server-websocket-bun
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configure
|
|
12
|
+
|
|
13
|
+
Save `micro-server.config.ts`:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { defineConfig } from '@beespoke/micro-server';
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
plugins: ['@beespoke/micro-server-websocket-bun'],
|
|
20
|
+
pluginOptions: {
|
|
21
|
+
'@beespoke/micro-server-websocket-bun': {
|
|
22
|
+
port: 5001,
|
|
23
|
+
folder: './sockets',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Create `sockets/echo.ts`:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { BaseSocketHandler } from '@beespoke/micro-server-websocket-bun';
|
|
33
|
+
import type { ServerWebSocket } from 'bun';
|
|
34
|
+
|
|
35
|
+
export default class EchoSocket extends BaseSocketHandler {
|
|
36
|
+
async connect(_socket: ServerWebSocket<any>) {}
|
|
37
|
+
async disconnect(_socket: ServerWebSocket<any>, _code: number, _reason: string) {}
|
|
38
|
+
|
|
39
|
+
async accept(socket: ServerWebSocket<any>, message: string) {
|
|
40
|
+
socket.send(message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Start the application
|
|
46
|
+
|
|
47
|
+
Save the configuration as `micro-server.config.ts` next to your application's `package.json`, then create `index.ts`:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { ApplicationServer } from '@beespoke/micro-server';
|
|
51
|
+
|
|
52
|
+
await ApplicationServer.bootstrap();
|
|
53
|
+
for (const signal of ['SIGINT', 'SIGTERM'] as const) {
|
|
54
|
+
process.once(signal, () => {
|
|
55
|
+
void ApplicationServer.close().catch(error => {
|
|
56
|
+
console.error(error);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Run `bun index.ts` and connect a WebSocket client to `ws://localhost:5001/echo`. Install `@types/bun` as a development dependency when typechecking application handler files.
|
|
64
|
+
|
|
65
|
+
## Options and handlers
|
|
66
|
+
|
|
67
|
+
`port` defaults to 5001. `folder` defaults to `sockets`; `dir` can select the discovery root. Relative paths resolve from the app configuration directory. Discovered files default-export handler classes implementing `connect`, `accept`, and `disconnect`.
|
|
68
|
+
|
|
69
|
+
For explicit registration, supply `handlers: [{ path: 'echo', handler: new EchoSocket() }]` instead of relying on files. Handler paths and effective channels must be unique. `BaseSocketHandler.channel` supports application channel selection; a default channel follows the registered path. `getInstance()` exposes the handler's active socket when available.
|
|
70
|
+
|
|
71
|
+
This listener does not automatically inherit HTTP middleware or JWT route authentication from core. Applications own WebSocket upgrade/access policy. Plugin shutdown closes its listener and sockets. The root exports `WebSocketServer` as a named/default export, `BaseSocketHandler`, and WebSocket data/options types. Standalone applications own initialization and shutdown.
|
|
72
|
+
|
|
73
|
+
## TypeScript
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
// plugin-types.d.ts
|
|
77
|
+
import type {} from '@beespoke/micro-server-websocket-bun/plugin';
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This import enables typed plugin options without importing the adapter at runtime. Application handler files also need Bun types. A Node application may import package metadata/types, but starting the native WebSocket server requires Bun.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Server, ServerWebSocket } from 'bun';
|
|
2
|
+
import type { WebSocketData } from './BaseSocketType.js';
|
|
3
|
+
export default class BaseSocketHandler {
|
|
4
|
+
static channel: string;
|
|
5
|
+
static server: Server<WebSocketData>;
|
|
6
|
+
static websocket: ServerWebSocket<any>;
|
|
7
|
+
upgrade(req: Request, server: Server<WebSocketData>): Promise<boolean | void>;
|
|
8
|
+
accept(ws: ServerWebSocket<any>, message: string): Promise<void>;
|
|
9
|
+
connect(ws: ServerWebSocket<any>): Promise<void>;
|
|
10
|
+
disconnect(ws: ServerWebSocket<any>, code: number, reason: string): Promise<void>;
|
|
11
|
+
getInstance(): ServerWebSocket<any>;
|
|
12
|
+
static parse(url: String): any;
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Structural subset of the core context; standalone clients need no HTTP dependency. */
|
|
2
|
+
export interface Logger {
|
|
3
|
+
info(...args: unknown[]): void;
|
|
4
|
+
warn(...args: unknown[]): void;
|
|
5
|
+
error(...args: unknown[]): void;
|
|
6
|
+
debug(...args: unknown[]): void;
|
|
7
|
+
}
|
|
8
|
+
export interface PluginContext {
|
|
9
|
+
readonly logger: Logger;
|
|
10
|
+
readonly configDir: string;
|
|
11
|
+
onStart(hook: () => void | Promise<void>): void;
|
|
12
|
+
onClose(hook: () => void | Promise<void>): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ServerWebSocket } from 'bun';
|
|
2
|
+
import type { PluginContext } from './PluginContext.js';
|
|
3
|
+
import BaseSocketHandler from './BaseSocketHandler.js';
|
|
4
|
+
export type SocketHandlerRegistration = {
|
|
5
|
+
path: string;
|
|
6
|
+
handler: BaseSocketHandler;
|
|
7
|
+
};
|
|
8
|
+
export interface WebSocketServerOptions {
|
|
9
|
+
port?: number;
|
|
10
|
+
/** Relative directories resolve against PluginContext.configDir during setup. */
|
|
11
|
+
dir?: string;
|
|
12
|
+
folder?: string;
|
|
13
|
+
/** Explicit handlers bypass directory discovery. */
|
|
14
|
+
handlers?: SocketHandlerRegistration[];
|
|
15
|
+
}
|
|
16
|
+
export default class WebSocketServer {
|
|
17
|
+
private readonly options;
|
|
18
|
+
readonly name = "websocket-bun";
|
|
19
|
+
static socketHandlers: SocketHandlerRegistration[];
|
|
20
|
+
private static server?;
|
|
21
|
+
private static initializing?;
|
|
22
|
+
private static closing?;
|
|
23
|
+
private static readonly pendingHooks;
|
|
24
|
+
private static hookErrors;
|
|
25
|
+
private static stopping;
|
|
26
|
+
private static runHook;
|
|
27
|
+
constructor(options?: WebSocketServerOptions);
|
|
28
|
+
setup(context: PluginContext): Promise<void>;
|
|
29
|
+
/** Await discovery before serving. The leading arguments match the existing API. */
|
|
30
|
+
static init(port?: number, dir?: string, folder?: string): Promise<SocketHandlerRegistration[]>;
|
|
31
|
+
private static start;
|
|
32
|
+
static getSocketHandlers(): SocketHandlerRegistration[];
|
|
33
|
+
static getHandler(path: string): ServerWebSocket<any> | null;
|
|
34
|
+
private static stopServer;
|
|
35
|
+
static close(): Promise<void>;
|
|
36
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default, default as WebSocketServer } from './WebSocketServer.js';
|
|
2
|
+
export type { WebSocketServerOptions, SocketHandlerRegistration } from './WebSocketServer.js';
|
|
3
|
+
export { default as BaseSocketHandler } from './BaseSocketHandler.js';
|
|
4
|
+
export type { WebSocketData } from './BaseSocketType.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// src/WebSocketServer.ts
|
|
2
|
+
import { readdir } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
function assertBun() {
|
|
6
|
+
if (typeof Bun === "undefined" || typeof Bun.serve !== "function") {
|
|
7
|
+
throw new Error("@beespoke/micro-server-websocket-bun requires the Bun runtime to initialize WebSockets");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class WebSocketServer {
|
|
12
|
+
options;
|
|
13
|
+
name = "websocket-bun";
|
|
14
|
+
static socketHandlers = [];
|
|
15
|
+
static server;
|
|
16
|
+
static initializing;
|
|
17
|
+
static closing;
|
|
18
|
+
static pendingHooks = new Set;
|
|
19
|
+
static hookErrors = [];
|
|
20
|
+
static stopping = false;
|
|
21
|
+
static runHook(hook) {
|
|
22
|
+
const task = Promise.resolve().then(hook).catch((error) => {
|
|
23
|
+
this.hookErrors.push(error);
|
|
24
|
+
}).finally(() => {
|
|
25
|
+
this.pendingHooks.delete(task);
|
|
26
|
+
});
|
|
27
|
+
this.pendingHooks.add(task);
|
|
28
|
+
return task;
|
|
29
|
+
}
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
}
|
|
33
|
+
async setup(context) {
|
|
34
|
+
assertBun();
|
|
35
|
+
await WebSocketServer.start(this.options.port ?? 5001, resolve(context.configDir, this.options.dir ?? "."), this.options.folder ?? "sockets", this.options.handlers, context.logger, (server) => context.onClose(() => WebSocketServer.server === server ? WebSocketServer.close() : undefined));
|
|
36
|
+
}
|
|
37
|
+
static async init(port = 5001, dir = process.cwd(), folder = "sockets") {
|
|
38
|
+
return this.start(port, dir, folder);
|
|
39
|
+
}
|
|
40
|
+
static async start(port, dir, folder, explicitHandlers, logger = console, onAcquire) {
|
|
41
|
+
assertBun();
|
|
42
|
+
if (this.server || this.initializing || this.closing)
|
|
43
|
+
throw new Error("A WebSocketServer is already active or changing state");
|
|
44
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535)
|
|
45
|
+
throw new Error("WebSocket port must be an integer between 0 and 65535");
|
|
46
|
+
this.initializing = (async () => {
|
|
47
|
+
const handlers = [];
|
|
48
|
+
if (explicitHandlers) {
|
|
49
|
+
handlers.push(...explicitHandlers);
|
|
50
|
+
} else {
|
|
51
|
+
const fullDirectory = resolve(dir, folder);
|
|
52
|
+
const files = (await readdir(fullDirectory)).filter((file) => /\.(?:ts|mts|js|mjs)$/.test(file) && !/\.d\.(?:ts|mts)$/.test(file)).sort();
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
const Handler = (await import(pathToFileURL(resolve(fullDirectory, file)).href)).default;
|
|
55
|
+
if (typeof Handler !== "function")
|
|
56
|
+
throw new Error(`Socket handler ${file} must default-export a class`);
|
|
57
|
+
handlers.push({ path: file.replace(/\.(?:ts|mts|js|mjs)$/, ""), handler: new Handler });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const channels = new Map;
|
|
61
|
+
const routes = new Set;
|
|
62
|
+
for (const record of handlers) {
|
|
63
|
+
record.path = record.path.replace(/^\/+|\/+$/g, "");
|
|
64
|
+
if (routes.has(record.path))
|
|
65
|
+
throw new Error(`Duplicate WebSocket handler path: ${record.path}`);
|
|
66
|
+
routes.add(record.path);
|
|
67
|
+
const Handler = record.handler.constructor;
|
|
68
|
+
const channel = Handler.channel === "default" ? record.path : Handler.channel;
|
|
69
|
+
if (channels.has(channel))
|
|
70
|
+
throw new Error(`Duplicate WebSocket channel: ${channel}`);
|
|
71
|
+
Handler.channel = channel;
|
|
72
|
+
channels.set(channel, record.handler);
|
|
73
|
+
}
|
|
74
|
+
let server;
|
|
75
|
+
server = Bun.serve({
|
|
76
|
+
port,
|
|
77
|
+
async fetch(req, server2) {
|
|
78
|
+
const pathname = new URL(req.url).pathname.replace(/^\/+|\/+$/g, "");
|
|
79
|
+
const record = handlers.find((record2) => record2.path === pathname);
|
|
80
|
+
if (!record)
|
|
81
|
+
return new Response("WebSocket handler not found", { status: 404 });
|
|
82
|
+
const upgraded = await record.handler.upgrade(req, server2);
|
|
83
|
+
if (upgraded === false)
|
|
84
|
+
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
85
|
+
return;
|
|
86
|
+
},
|
|
87
|
+
websocket: {
|
|
88
|
+
message(ws, message) {
|
|
89
|
+
if (WebSocketServer.closing || WebSocketServer.stopping) {
|
|
90
|
+
ws.close(1001, "Server shutting down");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const handler = channels.get(ws.data.channel ?? "");
|
|
94
|
+
if (handler)
|
|
95
|
+
return WebSocketServer.runHook(() => handler.accept(ws, typeof message === "string" ? message : message.toString()));
|
|
96
|
+
},
|
|
97
|
+
open(ws) {
|
|
98
|
+
if (WebSocketServer.closing || WebSocketServer.stopping) {
|
|
99
|
+
ws.close(1001, "Server shutting down");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const handler = channels.get(ws.data.channel ?? "");
|
|
103
|
+
if (handler) {
|
|
104
|
+
handler.constructor.websocket = ws;
|
|
105
|
+
return WebSocketServer.runHook(() => handler.connect(ws));
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
close(ws, code, reason) {
|
|
109
|
+
const handler = channels.get(ws.data.channel ?? "");
|
|
110
|
+
if (!handler)
|
|
111
|
+
return;
|
|
112
|
+
return WebSocketServer.runHook(async () => {
|
|
113
|
+
try {
|
|
114
|
+
await handler.disconnect(ws, code, reason);
|
|
115
|
+
} finally {
|
|
116
|
+
const Handler = handler.constructor;
|
|
117
|
+
if (Handler.websocket === ws)
|
|
118
|
+
Handler.websocket = undefined;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
this.server = server;
|
|
125
|
+
this.socketHandlers = handlers;
|
|
126
|
+
try {
|
|
127
|
+
onAcquire?.(server);
|
|
128
|
+
for (const { handler } of handlers)
|
|
129
|
+
handler.constructor.server = server;
|
|
130
|
+
logger.info(`Web Socket Server ready at: http://127.0.0.1:${server.port}`);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
await this.stopServer();
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
return handlers;
|
|
136
|
+
})();
|
|
137
|
+
try {
|
|
138
|
+
return await this.initializing;
|
|
139
|
+
} finally {
|
|
140
|
+
this.initializing = undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
static getSocketHandlers() {
|
|
144
|
+
return this.socketHandlers;
|
|
145
|
+
}
|
|
146
|
+
static getHandler(path) {
|
|
147
|
+
return this.socketHandlers.find((socket) => socket.path === path)?.handler.getInstance() ?? null;
|
|
148
|
+
}
|
|
149
|
+
static async stopServer() {
|
|
150
|
+
const server = this.server;
|
|
151
|
+
this.stopping = true;
|
|
152
|
+
const errors = [];
|
|
153
|
+
try {
|
|
154
|
+
try {
|
|
155
|
+
if (server)
|
|
156
|
+
await server.stop(true);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
errors.push(error);
|
|
159
|
+
}
|
|
160
|
+
while (this.pendingHooks.size)
|
|
161
|
+
await Promise.all([...this.pendingHooks]);
|
|
162
|
+
errors.push(...this.hookErrors.splice(0));
|
|
163
|
+
if (errors.length)
|
|
164
|
+
throw new AggregateError(errors, "Failed to close WebSocket server");
|
|
165
|
+
} finally {
|
|
166
|
+
for (const { handler } of this.socketHandlers) {
|
|
167
|
+
const Handler = handler.constructor;
|
|
168
|
+
Handler.server = undefined;
|
|
169
|
+
Handler.websocket = undefined;
|
|
170
|
+
}
|
|
171
|
+
this.socketHandlers = [];
|
|
172
|
+
this.server = undefined;
|
|
173
|
+
this.stopping = false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
static async close() {
|
|
177
|
+
if (this.closing)
|
|
178
|
+
return this.closing;
|
|
179
|
+
this.closing = (async () => {
|
|
180
|
+
try {
|
|
181
|
+
await this.initializing;
|
|
182
|
+
} catch {}
|
|
183
|
+
await this.stopServer();
|
|
184
|
+
})();
|
|
185
|
+
try {
|
|
186
|
+
await this.closing;
|
|
187
|
+
} finally {
|
|
188
|
+
this.closing = undefined;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// src/BaseSocketHandler.ts
|
|
193
|
+
class BaseSocketHandler {
|
|
194
|
+
static channel = "default";
|
|
195
|
+
static server;
|
|
196
|
+
static websocket;
|
|
197
|
+
async upgrade(req, server) {
|
|
198
|
+
const Handler = this.constructor;
|
|
199
|
+
return server.upgrade(req, { data: { channel: Handler.channel } });
|
|
200
|
+
}
|
|
201
|
+
async accept(ws, message) {
|
|
202
|
+
throw new Error("accept(ws:ServerWebSocket<any>, message:string) - not yet implemented!");
|
|
203
|
+
}
|
|
204
|
+
async connect(ws) {
|
|
205
|
+
throw new Error("connect(ws:ServerWebSocket<any>) - not yet implemented!");
|
|
206
|
+
}
|
|
207
|
+
async disconnect(ws, code, reason) {
|
|
208
|
+
throw new Error("disconnect(ws:ServerWebSocket<any>, code:number, reason:string) - not yet implemented!");
|
|
209
|
+
}
|
|
210
|
+
getInstance() {
|
|
211
|
+
const Handler = this.constructor;
|
|
212
|
+
if (!Handler.websocket)
|
|
213
|
+
throw new Error("No active WebSocket for this handler");
|
|
214
|
+
return Handler.websocket;
|
|
215
|
+
}
|
|
216
|
+
static parse(url) {
|
|
217
|
+
return Object.fromEntries(new URL(String(url)).searchParams);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
export {
|
|
221
|
+
WebSocketServer as default,
|
|
222
|
+
WebSocketServer,
|
|
223
|
+
BaseSocketHandler
|
|
224
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PluginDefinition } from '@beespoke/micro-server';
|
|
2
|
+
import { type WebSocketServerOptions } from '../index.js';
|
|
3
|
+
export type WebSocketPluginOptions = WebSocketServerOptions;
|
|
4
|
+
declare module '@beespoke/micro-server' {
|
|
5
|
+
interface PluginOptionsRegistry {
|
|
6
|
+
'@beespoke/micro-server-websocket-bun': WebSocketPluginOptions;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
declare const definition: PluginDefinition<WebSocketPluginOptions>;
|
|
10
|
+
export default definition;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// src/plugin/index.ts
|
|
2
|
+
import { WebSocketServer, BaseSocketHandler } from "../index.js";
|
|
3
|
+
var definition = {
|
|
4
|
+
name: "@beespoke/micro-server-websocket-bun",
|
|
5
|
+
validate(options) {
|
|
6
|
+
if (!options || typeof options !== "object" || Array.isArray(options))
|
|
7
|
+
throw new Error("WebSocket options must be an object");
|
|
8
|
+
const values = options;
|
|
9
|
+
const keys = ["port", "dir", "folder", "handlers"];
|
|
10
|
+
if (Object.keys(values).some((key) => !keys.includes(key)))
|
|
11
|
+
throw new Error("WebSocket options contain an unknown field");
|
|
12
|
+
if (values.port !== undefined && (!Number.isInteger(values.port) || values.port < 0 || values.port > 65535))
|
|
13
|
+
throw new Error("WebSocket port must be an integer between 0 and 65535");
|
|
14
|
+
for (const key of ["dir", "folder"]) {
|
|
15
|
+
if (values[key] !== undefined && (typeof values[key] !== "string" || !values[key].trim()))
|
|
16
|
+
throw new Error(`WebSocket ${key} must be a nonempty string`);
|
|
17
|
+
}
|
|
18
|
+
if (values.handlers !== undefined) {
|
|
19
|
+
if (!Array.isArray(values.handlers))
|
|
20
|
+
throw new Error("WebSocket handlers must be an array");
|
|
21
|
+
const paths = new Set;
|
|
22
|
+
const channels = new Set;
|
|
23
|
+
for (const entry of values.handlers) {
|
|
24
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry) || Object.keys(entry).some((key) => !["path", "handler"].includes(key)))
|
|
25
|
+
throw new Error("WebSocket handlers entries must contain path and handler");
|
|
26
|
+
if (typeof entry.path !== "string")
|
|
27
|
+
throw new Error("WebSocket handlers.path must be a string");
|
|
28
|
+
if (!(entry.handler instanceof BaseSocketHandler))
|
|
29
|
+
throw new Error("WebSocket handlers.handler must be a BaseSocketHandler");
|
|
30
|
+
const path = entry.path.replace(/^\/+|\/+$/g, "");
|
|
31
|
+
if (paths.has(path))
|
|
32
|
+
throw new Error("WebSocket handlers.path must be unique");
|
|
33
|
+
paths.add(path);
|
|
34
|
+
const Handler = entry.handler.constructor;
|
|
35
|
+
const channel = Handler.channel === "default" ? path : Handler.channel;
|
|
36
|
+
if (channels.has(channel))
|
|
37
|
+
throw new Error("WebSocket handlers channels must be unique");
|
|
38
|
+
channels.add(channel);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
create(options) {
|
|
43
|
+
return new WebSocketServer(options);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var plugin_default = definition;
|
|
47
|
+
export {
|
|
48
|
+
plugin_default as default
|
|
49
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beespoke/micro-server-websocket-bun",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "2.0.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./plugin": {
|
|
14
|
+
"types": "./dist/plugin/index.d.ts",
|
|
15
|
+
"import": "./dist/plugin/index.js",
|
|
16
|
+
"default": "./dist/plugin/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "bun ../../scripts/build-package.ts"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@types/bun": "1.3.14",
|
|
27
|
+
"@types/node": "^24.0.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@beespoke/micro-server": "^2.0.5"
|
|
31
|
+
},
|
|
32
|
+
"peerDependenciesMeta": {
|
|
33
|
+
"@beespoke/micro-server": {
|
|
34
|
+
"optional": true
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|