@forinda/kickjs-ws 1.3.2 → 1.4.1-alpha.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.
@@ -0,0 +1,75 @@
1
+ import 'reflect-metadata';
2
+ /**
3
+ * Mark a class as a WebSocket controller with a namespace path.
4
+ * Registers the class in the DI container and the WS controller registry.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * @WsController('/chat')
9
+ * export class ChatController {
10
+ * @OnConnect()
11
+ * handleConnect(ctx: WsContext) { }
12
+ *
13
+ * @OnMessage('send')
14
+ * handleSend(ctx: WsContext) { }
15
+ * }
16
+ * ```
17
+ */
18
+ export declare function WsController(namespace?: string): ClassDecorator;
19
+ /**
20
+ * Handle new WebSocket connections.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * @OnConnect()
25
+ * handleConnect(ctx: WsContext) {
26
+ * console.log(`Client ${ctx.id} connected`)
27
+ * }
28
+ * ```
29
+ */
30
+ export declare const OnConnect: () => MethodDecorator;
31
+ /**
32
+ * Handle WebSocket disconnections.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * @OnDisconnect()
37
+ * handleDisconnect(ctx: WsContext) {
38
+ * console.log(`Client ${ctx.id} disconnected`)
39
+ * }
40
+ * ```
41
+ */
42
+ export declare const OnDisconnect: () => MethodDecorator;
43
+ /**
44
+ * Handle WebSocket errors.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * @OnError()
49
+ * handleError(ctx: WsContext) {
50
+ * console.error('WS error:', ctx.data)
51
+ * }
52
+ * ```
53
+ */
54
+ export declare const OnError: () => MethodDecorator;
55
+ /**
56
+ * Handle a specific WebSocket message event.
57
+ * Use '*' as a catch-all for unmatched events.
58
+ *
59
+ * Messages must be JSON: `{ "event": "chat:send", "data": { ... } }`
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * @OnMessage('chat:send')
64
+ * handleChatSend(ctx: WsContext) {
65
+ * ctx.broadcast('chat:receive', ctx.data)
66
+ * }
67
+ *
68
+ * @OnMessage('*')
69
+ * handleUnknown(ctx: WsContext) {
70
+ * ctx.send('error', { message: `Unknown event: ${ctx.event}` })
71
+ * }
72
+ * ```
73
+ */
74
+ export declare function OnMessage(event: string): MethodDecorator;
75
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAA;AAIzB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,cAAc,CAM/D;AAiBD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,QAzBT,eAyB+C,CAAA;AAE5D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,QAtCZ,eAsCqD,CAAA;AAElE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,OAAO,QAnDP,eAmD2C,CAAA;AAExD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAWxD"}
package/dist/index.d.ts CHANGED
@@ -1,234 +1,6 @@
1
- import { AppAdapter, Ref, Container } from '@forinda/kickjs-core';
2
- import { WebSocket, WebSocketServer } from 'ws';
3
-
4
- declare const WS_METADATA: {
5
- readonly WS_CONTROLLER: symbol;
6
- readonly WS_HANDLERS: symbol;
7
- };
8
- type WsHandlerType = 'connect' | 'disconnect' | 'message' | 'error';
9
- interface WsHandlerDefinition {
10
- type: WsHandlerType;
11
- /** Event name — only for 'message' type */
12
- event?: string;
13
- /** Method name on the controller class */
14
- handlerName: string;
15
- }
16
- interface WsAdapterOptions {
17
- /** Base path for WebSocket upgrade (default: '/ws') */
18
- path?: string;
19
- /** Heartbeat ping interval in ms (default: 30000). Set to 0 to disable. */
20
- heartbeatInterval?: number;
21
- /** Maximum message payload size in bytes */
22
- maxPayload?: number;
23
- }
24
-
25
- /**
26
- * WebSocket adapter for KickJS. Attaches to the HTTP server and routes
27
- * WebSocket connections to @WsController classes based on namespace paths.
28
- *
29
- * @example
30
- * ```ts
31
- * import { WsAdapter } from '@forinda/kickjs-ws'
32
- *
33
- * bootstrap({
34
- * modules: [ChatModule],
35
- * adapters: [
36
- * new WsAdapter({ path: '/ws' }),
37
- * ],
38
- * })
39
- * ```
40
- *
41
- * Clients connect to: `ws://localhost:3000/ws/chat`
42
- * Messages are JSON: `{ "event": "send", "data": { "text": "hello" } }`
43
- */
44
- declare class WsAdapter implements AppAdapter {
45
- readonly name = "WsAdapter";
46
- private basePath;
47
- private heartbeatInterval;
48
- private maxPayload;
49
- private wss;
50
- private container;
51
- private namespaces;
52
- private roomManager;
53
- private heartbeatTimer;
54
- /** Total WebSocket connections ever opened */
55
- readonly totalConnections: Ref<number>;
56
- /** Currently active connections */
57
- readonly activeConnections: Ref<number>;
58
- /** Total messages received */
59
- readonly messagesReceived: Ref<number>;
60
- /** Total messages sent */
61
- readonly messagesSent: Ref<number>;
62
- /** Total errors */
63
- readonly wsErrors: Ref<number>;
64
- constructor(options?: WsAdapterOptions);
65
- /** Get a snapshot of WebSocket stats for DevTools */
66
- getStats(): {
67
- totalConnections: number;
68
- activeConnections: number;
69
- messagesReceived: number;
70
- messagesSent: number;
71
- errors: number;
72
- namespaces: Record<string, {
73
- connections: number;
74
- handlers: number;
75
- }>;
76
- rooms: Record<string, number>;
77
- };
78
- beforeStart(_app: any, container: Container): void;
79
- afterStart(server: any, _container: Container): void;
80
- shutdown(): void;
81
- private handleConnection;
82
- private invokeHandlers;
83
- private safeInvoke;
84
- }
85
-
86
- /**
87
- * Manages WebSocket room membership and broadcasting.
88
- * Standalone from ws/socket.io — can be swapped for socket.io's built-in rooms.
89
- */
90
- declare class RoomManager {
91
- /** socketId → set of room names */
92
- private socketRooms;
93
- /** room name → set of { socketId, socket } */
94
- private roomSockets;
95
- join(socketId: string, socket: WebSocket, room: string): void;
96
- leave(socketId: string, room: string): void;
97
- /** Remove socket from all rooms (called on disconnect) */
98
- leaveAll(socketId: string): void;
99
- getRooms(socketId: string): string[];
100
- getSockets(room: string): Map<string, WebSocket>;
101
- /** Get all rooms with their member counts */
102
- getAllRooms(): Record<string, number>;
103
- /** Broadcast to all sockets in a room, optionally excluding one */
104
- broadcast(room: string, event: string, data: any, excludeId?: string): void;
105
- }
106
-
107
- /**
108
- * Context object passed to WebSocket handler methods.
109
- * Analogous to RequestContext for HTTP controllers.
110
- *
111
- * @example
112
- * ```ts
113
- * @OnMessage('chat:send')
114
- * handleSend(ctx: WsContext) {
115
- * console.log(ctx.data) // parsed message payload
116
- * ctx.send('chat:ack', { ok: true })
117
- * ctx.broadcast('chat:receive', ctx.data)
118
- * ctx.join('room-1')
119
- * ctx.to('room-1').send('chat:receive', ctx.data)
120
- * }
121
- * ```
122
- */
123
- declare class WsContext {
124
- readonly socket: WebSocket;
125
- readonly server: WebSocketServer;
126
- private readonly roomManager;
127
- private readonly namespaceSockets;
128
- /** Unique connection ID */
129
- readonly id: string;
130
- /** Parsed message payload (set for @OnMessage handlers) */
131
- data: any;
132
- /** Event name from the message envelope (set for @OnMessage handlers) */
133
- event: string;
134
- /** The namespace this connection belongs to */
135
- readonly namespace: string;
136
- private metadata;
137
- constructor(socket: WebSocket, server: WebSocketServer, roomManager: RoomManager, namespaceSockets: Map<string, WebSocket>, id: string, namespace: string);
138
- /** Get a metadata value */
139
- get<T = any>(key: string): T | undefined;
140
- /** Set a metadata value (persists for the lifetime of the connection) */
141
- set(key: string, value: any): void;
142
- /** Send a message to this socket */
143
- send(event: string, data: any): void;
144
- /** Send to all sockets in the same namespace except this one */
145
- broadcast(event: string, data: any): void;
146
- /** Send to all sockets in the same namespace including this one */
147
- broadcastAll(event: string, data: any): void;
148
- /** Join a room */
149
- join(room: string): void;
150
- /** Leave a room */
151
- leave(room: string): void;
152
- /** Get all rooms this socket is in */
153
- rooms(): string[];
154
- /** Send to all sockets in a room */
155
- to(room: string): {
156
- send(event: string, data: any): void;
157
- };
158
- }
159
-
160
- /**
161
- * Mark a class as a WebSocket controller with a namespace path.
162
- * Registers the class in the DI container and the WS controller registry.
163
- *
164
- * @example
165
- * ```ts
166
- * @WsController('/chat')
167
- * export class ChatController {
168
- * @OnConnect()
169
- * handleConnect(ctx: WsContext) { }
170
- *
171
- * @OnMessage('send')
172
- * handleSend(ctx: WsContext) { }
173
- * }
174
- * ```
175
- */
176
- declare function WsController(namespace?: string): ClassDecorator;
177
- /**
178
- * Handle new WebSocket connections.
179
- *
180
- * @example
181
- * ```ts
182
- * @OnConnect()
183
- * handleConnect(ctx: WsContext) {
184
- * console.log(`Client ${ctx.id} connected`)
185
- * }
186
- * ```
187
- */
188
- declare const OnConnect: () => MethodDecorator;
189
- /**
190
- * Handle WebSocket disconnections.
191
- *
192
- * @example
193
- * ```ts
194
- * @OnDisconnect()
195
- * handleDisconnect(ctx: WsContext) {
196
- * console.log(`Client ${ctx.id} disconnected`)
197
- * }
198
- * ```
199
- */
200
- declare const OnDisconnect: () => MethodDecorator;
201
- /**
202
- * Handle WebSocket errors.
203
- *
204
- * @example
205
- * ```ts
206
- * @OnError()
207
- * handleError(ctx: WsContext) {
208
- * console.error('WS error:', ctx.data)
209
- * }
210
- * ```
211
- */
212
- declare const OnError: () => MethodDecorator;
213
- /**
214
- * Handle a specific WebSocket message event.
215
- * Use '*' as a catch-all for unmatched events.
216
- *
217
- * Messages must be JSON: `{ "event": "chat:send", "data": { ... } }`
218
- *
219
- * @example
220
- * ```ts
221
- * @OnMessage('chat:send')
222
- * handleChatSend(ctx: WsContext) {
223
- * ctx.broadcast('chat:receive', ctx.data)
224
- * }
225
- *
226
- * @OnMessage('*')
227
- * handleUnknown(ctx: WsContext) {
228
- * ctx.send('error', { message: `Unknown event: ${ctx.event}` })
229
- * }
230
- * ```
231
- */
232
- declare function OnMessage(event: string): MethodDecorator;
233
-
234
- export { OnConnect, OnDisconnect, OnError, OnMessage, RoomManager, WS_METADATA, WsAdapter, type WsAdapterOptions, WsContext, WsController, type WsHandlerDefinition, type WsHandlerType };
1
+ export { WsAdapter } from './ws-adapter';
2
+ export { WsContext } from './ws-context';
3
+ export { WsController, OnConnect, OnDisconnect, OnMessage, OnError } from './decorators';
4
+ export { RoomManager } from './room-manager';
5
+ export { WS_METADATA, type WsAdapterOptions, type WsHandlerDefinition, type WsHandlerType, } from './interfaces';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAGxC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAGxC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAGxF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAG5C,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA"}
package/dist/index.js CHANGED
@@ -1,3 +1,261 @@
1
- var A=Object.defineProperty;var r=(i,e)=>A(i,"name",{value:e,configurable:!0});import{randomUUID as x}from"crypto";import{WebSocketServer as C}from"ws";import{createLogger as N,ref as g}from"@forinda/kickjs-core";var l={WS_CONTROLLER:Symbol("kick:ws:controller"),WS_HANDLERS:Symbol("kick:ws:handlers")},v=new Set;var d=class{static{r(this,"WsContext")}socket;server;roomManager;namespaceSockets;id;data;event;namespace;metadata=new Map;constructor(e,t,s,o,a,n){this.socket=e,this.server=t,this.roomManager=s,this.namespaceSockets=o,this.id=a,this.namespace=n,this.data=null,this.event=""}get(e){return this.metadata.get(e)}set(e,t){this.metadata.set(e,t)}send(e,t){this.socket.readyState===this.socket.OPEN&&this.socket.send(JSON.stringify({event:e,data:t}))}broadcast(e,t){let s=JSON.stringify({event:e,data:t});for(let[o,a]of this.namespaceSockets)o!==this.id&&a.readyState===a.OPEN&&a.send(s)}broadcastAll(e,t){let s=JSON.stringify({event:e,data:t});for(let[,o]of this.namespaceSockets)o.readyState===o.OPEN&&o.send(s)}join(e){this.roomManager.join(this.id,this.socket,e)}leave(e){this.roomManager.leave(this.id,e)}rooms(){return this.roomManager.getRooms(this.id)}to(e){return{send:r((t,s)=>{this.roomManager.broadcast(e,t,s)},"send")}}};var f=class{static{r(this,"RoomManager")}socketRooms=new Map;roomSockets=new Map;join(e,t,s){this.socketRooms.has(e)||this.socketRooms.set(e,new Set),this.socketRooms.get(e).add(s),this.roomSockets.has(s)||this.roomSockets.set(s,new Map),this.roomSockets.get(s).set(e,t)}leave(e,t){this.socketRooms.get(e)?.delete(t),this.roomSockets.get(t)?.delete(e),this.roomSockets.get(t)?.size===0&&this.roomSockets.delete(t)}leaveAll(e){let t=this.socketRooms.get(e);if(t)for(let s of t)this.roomSockets.get(s)?.delete(e),this.roomSockets.get(s)?.size===0&&this.roomSockets.delete(s);this.socketRooms.delete(e)}getRooms(e){return Array.from(this.socketRooms.get(e)??[])}getSockets(e){return this.roomSockets.get(e)??new Map}getAllRooms(){let e={};for(let[t,s]of this.roomSockets)e[t]=s.size;return e}broadcast(e,t,s,o){let a=this.roomSockets.get(e);if(!a)return;let n=JSON.stringify({event:t,data:s});for(let[h,c]of a)h!==o&&c.readyState===c.OPEN&&c.send(n)}};var u=N("WsAdapter"),k=class{static{r(this,"WsAdapter")}name="WsAdapter";basePath;heartbeatInterval;maxPayload;wss=null;container=null;namespaces=new Map;roomManager=new f;heartbeatTimer=null;totalConnections;activeConnections;messagesReceived;messagesSent;wsErrors;constructor(e={}){this.basePath=e.path??"/ws",this.heartbeatInterval=e.heartbeatInterval??3e4,this.maxPayload=e.maxPayload,this.totalConnections=g(0),this.activeConnections=g(0),this.messagesReceived=g(0),this.messagesSent=g(0),this.wsErrors=g(0)}getStats(){let e={};for(let[t,s]of this.namespaces)e[t]={connections:s.sockets.size,handlers:s.handlers.length};return{totalConnections:this.totalConnections.value,activeConnections:this.activeConnections.value,messagesReceived:this.messagesReceived.value,messagesSent:this.messagesSent.value,errors:this.wsErrors.value,namespaces:e,rooms:this.roomManager.getAllRooms()}}beforeStart(e,t){this.container=t;for(let s of v){let o=Reflect.getMetadata(l.WS_CONTROLLER,s);if(o===void 0)continue;let a=Reflect.getMetadata(l.WS_HANDLERS,s)||[],n=this.basePath+(o==="/"?"":o);this.namespaces.set(n,{namespace:o,controllerClass:s,handlers:a,sockets:new Map,contexts:new Map}),u.info(`Registered WS namespace: ${n} (${s.name})`)}}afterStart(e,t){this.wss=new C({noServer:!0,maxPayload:this.maxPayload}),e.on("upgrade",(o,a,n)=>{let c=(o.url||"/").split("?")[0],p=this.namespaces.get(c);if(!p){a.write(`HTTP/1.1 404 Not Found\r
1
+ import { randomUUID as k } from "node:crypto";
2
+ import { WebSocketServer as u } from "ws";
3
+ import { Service as p, createLogger as R, ref as h } from "@forinda/kickjs-core";
4
+ import "reflect-metadata";
5
+ var i = {
6
+ WS_CONTROLLER: /* @__PURE__ */ Symbol("kick:ws:controller"),
7
+ WS_HANDLERS: /* @__PURE__ */ Symbol("kick:ws:handlers")
8
+ }, S = /* @__PURE__ */ new Set(), M = class {
9
+ id;
10
+ data;
11
+ event;
12
+ namespace;
13
+ metadata = /* @__PURE__ */ new Map();
14
+ constructor(e, t, s, a, o, n) {
15
+ this.socket = e, this.server = t, this.roomManager = s, this.namespaceSockets = a, this.id = o, this.namespace = n, this.data = null, this.event = "";
16
+ }
17
+ get(e) {
18
+ return this.metadata.get(e);
19
+ }
20
+ set(e, t) {
21
+ this.metadata.set(e, t);
22
+ }
23
+ send(e, t) {
24
+ this.socket.readyState === this.socket.OPEN && this.socket.send(JSON.stringify({
25
+ event: e,
26
+ data: t
27
+ }));
28
+ }
29
+ broadcast(e, t) {
30
+ const s = JSON.stringify({
31
+ event: e,
32
+ data: t
33
+ });
34
+ for (const [a, o] of this.namespaceSockets) a !== this.id && o.readyState === o.OPEN && o.send(s);
35
+ }
36
+ broadcastAll(e, t) {
37
+ const s = JSON.stringify({
38
+ event: e,
39
+ data: t
40
+ });
41
+ for (const [, a] of this.namespaceSockets) a.readyState === a.OPEN && a.send(s);
42
+ }
43
+ join(e) {
44
+ this.roomManager.join(this.id, this.socket, e);
45
+ }
46
+ leave(e) {
47
+ this.roomManager.leave(this.id, e);
48
+ }
49
+ rooms() {
50
+ return this.roomManager.getRooms(this.id);
51
+ }
52
+ to(e) {
53
+ return { send: (t, s) => {
54
+ this.roomManager.broadcast(e, t, s);
55
+ } };
56
+ }
57
+ }, N = class {
58
+ socketRooms = /* @__PURE__ */ new Map();
59
+ roomSockets = /* @__PURE__ */ new Map();
60
+ join(e, t, s) {
61
+ this.socketRooms.has(e) || this.socketRooms.set(e, /* @__PURE__ */ new Set()), this.socketRooms.get(e).add(s), this.roomSockets.has(s) || this.roomSockets.set(s, /* @__PURE__ */ new Map()), this.roomSockets.get(s).set(e, t);
62
+ }
63
+ leave(e, t) {
64
+ this.socketRooms.get(e)?.delete(t), this.roomSockets.get(t)?.delete(e), this.roomSockets.get(t)?.size === 0 && this.roomSockets.delete(t);
65
+ }
66
+ leaveAll(e) {
67
+ const t = this.socketRooms.get(e);
68
+ if (t) for (const s of t)
69
+ this.roomSockets.get(s)?.delete(e), this.roomSockets.get(s)?.size === 0 && this.roomSockets.delete(s);
70
+ this.socketRooms.delete(e);
71
+ }
72
+ getRooms(e) {
73
+ return Array.from(this.socketRooms.get(e) ?? []);
74
+ }
75
+ getSockets(e) {
76
+ return this.roomSockets.get(e) ?? /* @__PURE__ */ new Map();
77
+ }
78
+ getAllRooms() {
79
+ const e = {};
80
+ for (const [t, s] of this.roomSockets) e[t] = s.size;
81
+ return e;
82
+ }
83
+ broadcast(e, t, s, a) {
84
+ const o = this.roomSockets.get(e);
85
+ if (!o) return;
86
+ const n = JSON.stringify({
87
+ event: t,
88
+ data: s
89
+ });
90
+ for (const [c, r] of o) c !== a && r.readyState === r.OPEN && r.send(n);
91
+ }
92
+ }, d = R("WsAdapter"), A = class {
93
+ name = "WsAdapter";
94
+ basePath;
95
+ heartbeatInterval;
96
+ maxPayload;
97
+ wss = null;
98
+ container = null;
99
+ namespaces = /* @__PURE__ */ new Map();
100
+ roomManager = new N();
101
+ heartbeatTimer = null;
102
+ totalConnections;
103
+ activeConnections;
104
+ messagesReceived;
105
+ messagesSent;
106
+ wsErrors;
107
+ constructor(e = {}) {
108
+ this.basePath = e.path ?? "/ws", this.heartbeatInterval = e.heartbeatInterval ?? 3e4, this.maxPayload = e.maxPayload, this.totalConnections = h(0), this.activeConnections = h(0), this.messagesReceived = h(0), this.messagesSent = h(0), this.wsErrors = h(0);
109
+ }
110
+ getStats() {
111
+ const e = {};
112
+ for (const [t, s] of this.namespaces) e[t] = {
113
+ connections: s.sockets.size,
114
+ handlers: s.handlers.length
115
+ };
116
+ return {
117
+ totalConnections: this.totalConnections.value,
118
+ activeConnections: this.activeConnections.value,
119
+ messagesReceived: this.messagesReceived.value,
120
+ messagesSent: this.messagesSent.value,
121
+ errors: this.wsErrors.value,
122
+ namespaces: e,
123
+ rooms: this.roomManager.getAllRooms()
124
+ };
125
+ }
126
+ beforeStart(e, t) {
127
+ this.container = t;
128
+ for (const s of S) {
129
+ const a = Reflect.getMetadata(i.WS_CONTROLLER, s);
130
+ if (a === void 0) continue;
131
+ const o = Reflect.getMetadata(i.WS_HANDLERS, s) || [], n = this.basePath + (a === "/" ? "" : a);
132
+ this.namespaces.set(n, {
133
+ namespace: a,
134
+ controllerClass: s,
135
+ handlers: o,
136
+ sockets: /* @__PURE__ */ new Map(),
137
+ contexts: /* @__PURE__ */ new Map()
138
+ }), d.info(`Registered WS namespace: ${n} (${s.name})`);
139
+ }
140
+ }
141
+ afterStart(e, t) {
142
+ this.wss = new u({
143
+ noServer: !0,
144
+ maxPayload: this.maxPayload
145
+ }), e.on("upgrade", (a, o, n) => {
146
+ const c = (a.url || "/").split("?")[0], r = this.namespaces.get(c);
147
+ if (!r) {
148
+ o.write(`HTTP/1.1 404 Not Found\r
2
149
  \r
3
- `),a.destroy();return}this.wss.handleUpgrade(o,a,n,S=>{this.handleConnection(S,p)})}),this.heartbeatInterval>0&&(this.heartbeatTimer=setInterval(()=>{for(let[,o]of this.namespaces)for(let[,a]of o.sockets){if(a.__alive===!1){a.terminate();continue}a.__alive=!1,a.ping()}},this.heartbeatInterval));let s=Array.from(this.namespaces.values()).reduce((o,a)=>o+a.handlers.length,0);u.info(`WebSocket ready \u2014 ${this.namespaces.size} namespace(s), ${s} handler(s)`)}shutdown(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer);for(let[,e]of this.namespaces){for(let[,t]of e.sockets)t.close(1001,"Server shutting down");e.sockets.clear(),e.contexts.clear()}this.wss?.close()}handleConnection(e,t){let s=x();e.__alive=!0,t.sockets.set(s,e),this.totalConnections.value++,this.activeConnections.value++;let o=new d(e,this.wss,this.roomManager,t.sockets,s,t.namespace);t.contexts.set(s,o);let a=this.container.resolve(t.controllerClass);e.on("pong",()=>{e.__alive=!0}),this.invokeHandlers(a,t.handlers,"connect",o),e.on("message",n=>{this.messagesReceived.value++;try{let h=JSON.parse(n.toString()),c=h.event,p=h.data;if(!c||typeof c!="string"){o.send("error",{message:'Invalid message format: missing "event" field'});return}o.event=c,o.data=p;let S=t.handlers.find(m=>m.type==="message"&&m.event===c);if(S)this.safeInvoke(a,S.handlerName,o);else{let m=t.handlers.find(M=>M.type==="message"&&M.event==="*");m&&this.safeInvoke(a,m.handlerName,o)}}catch{o.data={message:"Invalid JSON"},this.invokeHandlers(a,t.handlers,"error",o)}}),e.on("close",()=>{this.activeConnections.value--,this.invokeHandlers(a,t.handlers,"disconnect",o),this.roomManager.leaveAll(s),t.sockets.delete(s),t.contexts.delete(s)}),e.on("error",n=>{this.wsErrors.value++,o.data={message:n.message,name:n.name},this.invokeHandlers(a,t.handlers,"error",o)})}invokeHandlers(e,t,s,o){for(let a of t)a.type===s&&this.safeInvoke(e,a.handlerName,o)}safeInvoke(e,t,s){try{let o=e[t](s);o instanceof Promise&&o.catch(a=>{u.error({err:a},`WS handler error in ${t}`)})}catch(o){u.error({err:o},`WS handler error in ${t}`)}}};import"reflect-metadata";import{Service as w}from"@forinda/kickjs-core";function O(i){return e=>{w()(e),Reflect.defineMetadata(l.WS_CONTROLLER,i||"/",e),v.add(e)}}r(O,"WsController");function R(i,e){return()=>(t,s)=>{let o=Reflect.getMetadata(l.WS_HANDLERS,t.constructor)||[];o.push({type:i,event:e,handlerName:s}),Reflect.defineMetadata(l.WS_HANDLERS,o,t.constructor)}}r(R,"createWsHandlerDecorator");var W=R("connect"),E=R("disconnect"),_=R("error");function b(i){return(e,t)=>{let s=Reflect.getMetadata(l.WS_HANDLERS,e.constructor)||[];s.push({type:"message",event:i,handlerName:t}),Reflect.defineMetadata(l.WS_HANDLERS,s,e.constructor)}}r(b,"OnMessage");export{W as OnConnect,E as OnDisconnect,_ as OnError,b as OnMessage,f as RoomManager,l as WS_METADATA,k as WsAdapter,d as WsContext,O as WsController};
150
+ `), o.destroy();
151
+ return;
152
+ }
153
+ this.wss.handleUpgrade(a, o, n, (m) => {
154
+ this.handleConnection(m, r);
155
+ });
156
+ }), this.heartbeatInterval > 0 && (this.heartbeatTimer = setInterval(() => {
157
+ for (const [, a] of this.namespaces) for (const [, o] of a.sockets) {
158
+ if (o.__alive === !1) {
159
+ o.terminate();
160
+ continue;
161
+ }
162
+ o.__alive = !1, o.ping();
163
+ }
164
+ }, this.heartbeatInterval));
165
+ const s = Array.from(this.namespaces.values()).reduce((a, o) => a + o.handlers.length, 0);
166
+ d.info(`WebSocket ready — ${this.namespaces.size} namespace(s), ${s} handler(s)`);
167
+ }
168
+ shutdown() {
169
+ this.heartbeatTimer && clearInterval(this.heartbeatTimer);
170
+ for (const [, e] of this.namespaces) {
171
+ for (const [, t] of e.sockets) t.close(1001, "Server shutting down");
172
+ e.sockets.clear(), e.contexts.clear();
173
+ }
174
+ this.wss?.close();
175
+ }
176
+ handleConnection(e, t) {
177
+ const s = k();
178
+ e.__alive = !0, t.sockets.set(s, e), this.totalConnections.value++, this.activeConnections.value++;
179
+ const a = new M(e, this.wss, this.roomManager, t.sockets, s, t.namespace);
180
+ t.contexts.set(s, a);
181
+ const o = this.container.resolve(t.controllerClass);
182
+ e.on("pong", () => {
183
+ e.__alive = !0;
184
+ }), this.invokeHandlers(o, t.handlers, "connect", a), e.on("message", (n) => {
185
+ this.messagesReceived.value++;
186
+ try {
187
+ const c = JSON.parse(n.toString()), r = c.event, m = c.data;
188
+ if (!r || typeof r != "string") {
189
+ a.send("error", { message: 'Invalid message format: missing "event" field' });
190
+ return;
191
+ }
192
+ a.event = r, a.data = m;
193
+ const v = t.handlers.find((l) => l.type === "message" && l.event === r);
194
+ if (v) this.safeInvoke(o, v.handlerName, a);
195
+ else {
196
+ const l = t.handlers.find((g) => g.type === "message" && g.event === "*");
197
+ l && this.safeInvoke(o, l.handlerName, a);
198
+ }
199
+ } catch {
200
+ a.data = { message: "Invalid JSON" }, this.invokeHandlers(o, t.handlers, "error", a);
201
+ }
202
+ }), e.on("close", () => {
203
+ this.activeConnections.value--, this.invokeHandlers(o, t.handlers, "disconnect", a), this.roomManager.leaveAll(s), t.sockets.delete(s), t.contexts.delete(s);
204
+ }), e.on("error", (n) => {
205
+ this.wsErrors.value++, a.data = {
206
+ message: n.message,
207
+ name: n.name
208
+ }, this.invokeHandlers(o, t.handlers, "error", a);
209
+ });
210
+ }
211
+ invokeHandlers(e, t, s, a) {
212
+ for (const o of t) o.type === s && this.safeInvoke(e, o.handlerName, a);
213
+ }
214
+ safeInvoke(e, t, s) {
215
+ try {
216
+ const a = e[t](s);
217
+ a instanceof Promise && a.catch((o) => {
218
+ d.error({ err: o }, `WS handler error in ${t}`);
219
+ });
220
+ } catch (a) {
221
+ d.error({ err: a }, `WS handler error in ${t}`);
222
+ }
223
+ }
224
+ };
225
+ function O(e) {
226
+ return (t) => {
227
+ p()(t), Reflect.defineMetadata(i.WS_CONTROLLER, e || "/", t), S.add(t);
228
+ };
229
+ }
230
+ function f(e, t) {
231
+ return () => (s, a) => {
232
+ const o = Reflect.getMetadata(i.WS_HANDLERS, s.constructor) || [];
233
+ o.push({
234
+ type: e,
235
+ event: t,
236
+ handlerName: a
237
+ }), Reflect.defineMetadata(i.WS_HANDLERS, o, s.constructor);
238
+ };
239
+ }
240
+ var _ = f("connect"), y = f("disconnect"), E = f("error");
241
+ function P(e) {
242
+ return (t, s) => {
243
+ const a = Reflect.getMetadata(i.WS_HANDLERS, t.constructor) || [];
244
+ a.push({
245
+ type: "message",
246
+ event: e,
247
+ handlerName: s
248
+ }), Reflect.defineMetadata(i.WS_HANDLERS, a, t.constructor);
249
+ };
250
+ }
251
+ export {
252
+ _ as OnConnect,
253
+ y as OnDisconnect,
254
+ E as OnError,
255
+ P as OnMessage,
256
+ N as RoomManager,
257
+ i as WS_METADATA,
258
+ A as WsAdapter,
259
+ M as WsContext,
260
+ O as WsController
261
+ };
@@ -0,0 +1,25 @@
1
+ type Constructor = new (...args: any[]) => any;
2
+ export declare const WS_METADATA: {
3
+ readonly WS_CONTROLLER: symbol;
4
+ readonly WS_HANDLERS: symbol;
5
+ };
6
+ export type WsHandlerType = 'connect' | 'disconnect' | 'message' | 'error';
7
+ export interface WsHandlerDefinition {
8
+ type: WsHandlerType;
9
+ /** Event name — only for 'message' type */
10
+ event?: string;
11
+ /** Method name on the controller class */
12
+ handlerName: string;
13
+ }
14
+ export interface WsAdapterOptions {
15
+ /** Base path for WebSocket upgrade (default: '/ws') */
16
+ path?: string;
17
+ /** Heartbeat ping interval in ms (default: 30000). Set to 0 to disable. */
18
+ heartbeatInterval?: number;
19
+ /** Maximum message payload size in bytes */
20
+ maxPayload?: number;
21
+ }
22
+ /** Registry of all @WsController classes — populated at decorator time */
23
+ export declare const wsControllerRegistry: Set<Constructor>;
24
+ export {};
25
+ //# sourceMappingURL=interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA,KAAK,WAAW,GAAG,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;AAE9C,eAAO,MAAM,WAAW;;;CAGd,CAAA;AAEV,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAA;AAE1E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,aAAa,CAAA;IACnB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,kBAAyB,CAAA"}
@@ -0,0 +1,22 @@
1
+ import type { WebSocket } from 'ws';
2
+ /**
3
+ * Manages WebSocket room membership and broadcasting.
4
+ * Standalone from ws/socket.io — can be swapped for socket.io's built-in rooms.
5
+ */
6
+ export declare class RoomManager {
7
+ /** socketId → set of room names */
8
+ private socketRooms;
9
+ /** room name → set of { socketId, socket } */
10
+ private roomSockets;
11
+ join(socketId: string, socket: WebSocket, room: string): void;
12
+ leave(socketId: string, room: string): void;
13
+ /** Remove socket from all rooms (called on disconnect) */
14
+ leaveAll(socketId: string): void;
15
+ getRooms(socketId: string): string[];
16
+ getSockets(room: string): Map<string, WebSocket>;
17
+ /** Get all rooms with their member counts */
18
+ getAllRooms(): Record<string, number>;
19
+ /** Broadcast to all sockets in a room, optionally excluding one */
20
+ broadcast(room: string, event: string, data: any, excludeId?: string): void;
21
+ }
22
+ //# sourceMappingURL=room-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-manager.d.ts","sourceRoot":"","sources":["../src/room-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAEnC;;;GAGG;AACH,qBAAa,WAAW;IACtB,mCAAmC;IACnC,OAAO,CAAC,WAAW,CAAiC;IACpD,8CAA8C;IAC9C,OAAO,CAAC,WAAW,CAA4C;IAE/D,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAY7D,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAU3C,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAahC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE;IAIpC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC;IAIhD,6CAA6C;IAC7C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQrC,mEAAmE;IACnE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;CAW5E"}
@@ -0,0 +1,63 @@
1
+ import { type AppAdapter, type Container, type Ref } from '@forinda/kickjs-core';
2
+ import { type WsAdapterOptions } from './interfaces';
3
+ /**
4
+ * WebSocket adapter for KickJS. Attaches to the HTTP server and routes
5
+ * WebSocket connections to @WsController classes based on namespace paths.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { WsAdapter } from '@forinda/kickjs-ws'
10
+ *
11
+ * bootstrap({
12
+ * modules: [ChatModule],
13
+ * adapters: [
14
+ * new WsAdapter({ path: '/ws' }),
15
+ * ],
16
+ * })
17
+ * ```
18
+ *
19
+ * Clients connect to: `ws://localhost:3000/ws/chat`
20
+ * Messages are JSON: `{ "event": "send", "data": { "text": "hello" } }`
21
+ */
22
+ export declare class WsAdapter implements AppAdapter {
23
+ readonly name = "WsAdapter";
24
+ private basePath;
25
+ private heartbeatInterval;
26
+ private maxPayload;
27
+ private wss;
28
+ private container;
29
+ private namespaces;
30
+ private roomManager;
31
+ private heartbeatTimer;
32
+ /** Total WebSocket connections ever opened */
33
+ readonly totalConnections: Ref<number>;
34
+ /** Currently active connections */
35
+ readonly activeConnections: Ref<number>;
36
+ /** Total messages received */
37
+ readonly messagesReceived: Ref<number>;
38
+ /** Total messages sent */
39
+ readonly messagesSent: Ref<number>;
40
+ /** Total errors */
41
+ readonly wsErrors: Ref<number>;
42
+ constructor(options?: WsAdapterOptions);
43
+ /** Get a snapshot of WebSocket stats for DevTools */
44
+ getStats(): {
45
+ totalConnections: number;
46
+ activeConnections: number;
47
+ messagesReceived: number;
48
+ messagesSent: number;
49
+ errors: number;
50
+ namespaces: Record<string, {
51
+ connections: number;
52
+ handlers: number;
53
+ }>;
54
+ rooms: Record<string, number>;
55
+ };
56
+ beforeStart(_app: any, container: Container): void;
57
+ afterStart(server: any, _container: Container): void;
58
+ shutdown(): void;
59
+ private handleConnection;
60
+ private invokeHandlers;
61
+ private safeInvoke;
62
+ }
63
+ //# sourceMappingURL=ws-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-adapter.d.ts","sourceRoot":"","sources":["../src/ws-adapter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAqB,KAAK,GAAG,EAAE,MAAM,sBAAsB,CAAA;AACnG,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAA;AAcrB;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,SAAU,YAAW,UAAU;IAC1C,QAAQ,CAAC,IAAI,eAAc;IAE3B,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,cAAc,CAA8C;IAGpE,8CAA8C;IAC9C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACtC,mCAAmC;IACnC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACvC,8BAA8B;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACtC,0BAA0B;IAC1B,QAAQ,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAClC,mBAAmB;IACnB,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;gBAElB,OAAO,GAAE,gBAAqB;IAY1C,qDAAqD;IACrD,QAAQ;;;;;;;yBAC8C,MAAM;sBAAY,MAAM;;;;IAkB9E,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IA4BlD,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,SAAS,GAAG,IAAI;IA+CpD,QAAQ,IAAI,IAAI;IAmBhB,OAAO,CAAC,gBAAgB;IAgFxB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,UAAU;CAYnB"}
@@ -0,0 +1,55 @@
1
+ import type { WebSocket, WebSocketServer } from 'ws';
2
+ import type { RoomManager } from './room-manager';
3
+ /**
4
+ * Context object passed to WebSocket handler methods.
5
+ * Analogous to RequestContext for HTTP controllers.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * @OnMessage('chat:send')
10
+ * handleSend(ctx: WsContext) {
11
+ * console.log(ctx.data) // parsed message payload
12
+ * ctx.send('chat:ack', { ok: true })
13
+ * ctx.broadcast('chat:receive', ctx.data)
14
+ * ctx.join('room-1')
15
+ * ctx.to('room-1').send('chat:receive', ctx.data)
16
+ * }
17
+ * ```
18
+ */
19
+ export declare class WsContext {
20
+ readonly socket: WebSocket;
21
+ readonly server: WebSocketServer;
22
+ private readonly roomManager;
23
+ private readonly namespaceSockets;
24
+ /** Unique connection ID */
25
+ readonly id: string;
26
+ /** Parsed message payload (set for @OnMessage handlers) */
27
+ data: any;
28
+ /** Event name from the message envelope (set for @OnMessage handlers) */
29
+ event: string;
30
+ /** The namespace this connection belongs to */
31
+ readonly namespace: string;
32
+ private metadata;
33
+ constructor(socket: WebSocket, server: WebSocketServer, roomManager: RoomManager, namespaceSockets: Map<string, WebSocket>, id: string, namespace: string);
34
+ /** Get a metadata value */
35
+ get<T = any>(key: string): T | undefined;
36
+ /** Set a metadata value (persists for the lifetime of the connection) */
37
+ set(key: string, value: any): void;
38
+ /** Send a message to this socket */
39
+ send(event: string, data: any): void;
40
+ /** Send to all sockets in the same namespace except this one */
41
+ broadcast(event: string, data: any): void;
42
+ /** Send to all sockets in the same namespace including this one */
43
+ broadcastAll(event: string, data: any): void;
44
+ /** Join a room */
45
+ join(room: string): void;
46
+ /** Leave a room */
47
+ leave(room: string): void;
48
+ /** Get all rooms this socket is in */
49
+ rooms(): string[];
50
+ /** Send to all sockets in a room */
51
+ to(room: string): {
52
+ send(event: string, data: any): void;
53
+ };
54
+ }
55
+ //# sourceMappingURL=ws-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-context.d.ts","sourceRoot":"","sources":["../src/ws-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,IAAI,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,SAAS;IAalB,QAAQ,CAAC,MAAM,EAAE,SAAS;IAC1B,QAAQ,CAAC,MAAM,EAAE,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAfnC,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,2DAA2D;IAC3D,IAAI,EAAE,GAAG,CAAA;IACT,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAE1B,OAAO,CAAC,QAAQ,CAAyB;gBAG9B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACf,WAAW,EAAE,WAAW,EACxB,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EACzD,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM;IAQnB,2BAA2B;IAC3B,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAIxC,yEAAyE;IACzE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAIlC,oCAAoC;IACpC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAMpC,gEAAgE;IAChE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IASzC,mEAAmE;IACnE,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAS5C,kBAAkB;IAClB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIxB,mBAAmB;IACnB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB,sCAAsC;IACtC,KAAK,IAAI,MAAM,EAAE;IAIjB,oCAAoC;IACpC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI,CAAA;KAAE;CAO3D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forinda/kickjs-ws",
3
- "version": "1.3.2",
3
+ "version": "1.4.1-alpha.0",
4
4
  "description": "WebSocket support with decorators, namespaces, rooms, and DI integration for KickJS",
5
5
  "keywords": [
6
6
  "kickjs",
@@ -34,7 +34,8 @@
34
34
  "@forinda/kickjs-queue",
35
35
  "@forinda/kickjs-multi-tenant",
36
36
  "@forinda/kickjs-devtools",
37
- "@forinda/kickjs-notifications"
37
+ "@forinda/kickjs-notifications",
38
+ "vite"
38
39
  ],
39
40
  "type": "module",
40
41
  "main": "dist/index.js",
@@ -50,16 +51,15 @@
50
51
  ],
51
52
  "dependencies": {
52
53
  "reflect-metadata": "^0.2.2",
53
- "ws": "^8.18.0",
54
- "@forinda/kickjs-core": "1.3.2"
54
+ "ws": "^8.20.0",
55
+ "@forinda/kickjs-core": "1.4.1-alpha.0"
55
56
  },
56
57
  "devDependencies": {
57
58
  "@swc/core": "^1.7.28",
58
- "@types/node": "^24.5.2",
59
+ "@types/node": "^25.0.0",
59
60
  "@types/ws": "^8.18.0",
60
- "tsup": "^8.5.0",
61
61
  "typescript": "^5.9.2",
62
- "vitest": "^3.2.4"
62
+ "vitest": "^4.1.1"
63
63
  },
64
64
  "publishConfig": {
65
65
  "access": "public"
@@ -79,8 +79,9 @@
79
79
  "url": "https://github.com/forinda/kick-js/issues"
80
80
  },
81
81
  "scripts": {
82
- "build": "tsup",
83
- "dev": "tsup --watch",
82
+ "build": "vite build && pnpm build:types",
83
+ "build:types": "tsc -p tsconfig.build.json",
84
+ "dev": "vite build --watch",
84
85
  "test": "vitest run",
85
86
  "test:watch": "vitest",
86
87
  "typecheck": "tsc --noEmit",