@moostjs/event-ws 0.6.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.
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/dist/index.cjs +445 -0
- package/dist/index.d.ts +225 -0
- package/dist/index.mjs +298 -0
- package/package.json +62 -0
- package/scripts/setup-skills.js +78 -0
- package/skills/moostjs-event-ws/SKILL.md +42 -0
- package/skills/moostjs-event-ws/core.md +157 -0
- package/skills/moostjs-event-ws/handlers.md +162 -0
- package/skills/moostjs-event-ws/protocol.md +181 -0
- package/skills/moostjs-event-ws/request-data.md +185 -0
- package/skills/moostjs-event-ws/rooms.md +196 -0
- package/skills/moostjs-event-ws/routing.md +115 -0
- package/skills/moostjs-event-ws/testing.md +209 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import * as moost from 'moost';
|
|
2
|
+
import { TMoostAdapter, Moost, TConsoleBase, TMoostAdapterOptions } from 'moost';
|
|
3
|
+
export { Controller, Description, Intercept, Param, TInterceptorPriority, defineAfterInterceptor, defineBeforeInterceptor, defineInterceptor } from 'moost';
|
|
4
|
+
import { WooksWs, TWooksWsOptions } from '@wooksjs/event-ws';
|
|
5
|
+
export { TTestWsConnectionContext, TTestWsMessageContext, TWooksWsOptions, WooksWs, WsBroadcastOptions, WsBroadcastTransport, WsClientMessage, WsConnection, WsError, WsPushMessage, WsReplyMessage, WsRoomManager, WsServerAdapter, WsSocket, currentConnection, prepareTestWsConnectionContext, prepareTestWsMessageContext, useWsConnection, useWsMessage, useWsRooms, useWsServer, wsConnectionKind, wsMessageKind } from '@wooksjs/event-ws';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ## Define WebSocket Message Handler
|
|
9
|
+
* ### @MethodDecorator
|
|
10
|
+
*
|
|
11
|
+
* Registers a handler for routed WebSocket messages.
|
|
12
|
+
* The event and path correspond to the WsClientMessage protocol fields.
|
|
13
|
+
*
|
|
14
|
+
* @param event - message event type (e.g. "message", "rpc", "subscribe")
|
|
15
|
+
* @param path - route path with optional params (e.g. "/chat/rooms/:roomId")
|
|
16
|
+
*/
|
|
17
|
+
declare function Message(event: string, path?: string): MethodDecorator;
|
|
18
|
+
/**
|
|
19
|
+
* ## Define WebSocket Connection Handler
|
|
20
|
+
* ### @MethodDecorator
|
|
21
|
+
*
|
|
22
|
+
* Registers a handler that runs when a new WebSocket connection is established.
|
|
23
|
+
* Runs inside the connection context. Throwing or rejecting closes the connection.
|
|
24
|
+
*/
|
|
25
|
+
declare function Connect(): MethodDecorator;
|
|
26
|
+
/**
|
|
27
|
+
* ## Define WebSocket Disconnection Handler
|
|
28
|
+
* ### @MethodDecorator
|
|
29
|
+
*
|
|
30
|
+
* Registers a handler that runs when a WebSocket connection closes.
|
|
31
|
+
*/
|
|
32
|
+
declare function Disconnect(): MethodDecorator;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the parsed WebSocket message payload.
|
|
36
|
+
* Only available in message handlers (@Message).
|
|
37
|
+
* @decorator
|
|
38
|
+
* @paramType T
|
|
39
|
+
*/
|
|
40
|
+
declare function MessageData(): ParameterDecorator & PropertyDecorator;
|
|
41
|
+
/**
|
|
42
|
+
* Get the raw WebSocket message (Buffer or string before parsing).
|
|
43
|
+
* Only available in message handlers (@Message).
|
|
44
|
+
* @decorator
|
|
45
|
+
* @paramType Buffer | string
|
|
46
|
+
*/
|
|
47
|
+
declare function RawMessage(): ParameterDecorator & PropertyDecorator;
|
|
48
|
+
/**
|
|
49
|
+
* Get the WebSocket message correlation ID.
|
|
50
|
+
* Only available in message handlers (@Message).
|
|
51
|
+
* @decorator
|
|
52
|
+
* @paramType string | number | undefined
|
|
53
|
+
*/
|
|
54
|
+
declare function MessageId(): ParameterDecorator & PropertyDecorator;
|
|
55
|
+
/**
|
|
56
|
+
* Get the WebSocket message event type.
|
|
57
|
+
* Only available in message handlers (@Message).
|
|
58
|
+
* @decorator
|
|
59
|
+
* @paramType string
|
|
60
|
+
*/
|
|
61
|
+
declare function MessageType(): ParameterDecorator & PropertyDecorator;
|
|
62
|
+
/**
|
|
63
|
+
* Get the WebSocket message path.
|
|
64
|
+
* Only available in message handlers (@Message).
|
|
65
|
+
* @decorator
|
|
66
|
+
* @paramType string
|
|
67
|
+
*/
|
|
68
|
+
declare function MessagePath(): ParameterDecorator & PropertyDecorator;
|
|
69
|
+
/**
|
|
70
|
+
* Get the WebSocket connection ID (UUID).
|
|
71
|
+
* Available in all WS handlers (@Message, @Connect, @Disconnect).
|
|
72
|
+
* @decorator
|
|
73
|
+
* @paramType string
|
|
74
|
+
*/
|
|
75
|
+
declare function ConnectionId(): ParameterDecorator & PropertyDecorator;
|
|
76
|
+
|
|
77
|
+
/** Handler metadata for routed WebSocket message events. */
|
|
78
|
+
interface TWsMessageHandlerMeta {
|
|
79
|
+
event: string;
|
|
80
|
+
path: string;
|
|
81
|
+
}
|
|
82
|
+
/** Handler metadata for WebSocket connection events. */
|
|
83
|
+
interface TWsConnectHandlerMeta {
|
|
84
|
+
}
|
|
85
|
+
/** Union of all WebSocket handler metadata types. */
|
|
86
|
+
type TWsHandlerMeta = TWsMessageHandlerMeta | TWsConnectHandlerMeta;
|
|
87
|
+
/** Configuration options for the MoostWs adapter. */
|
|
88
|
+
interface TMoostWsOpts {
|
|
89
|
+
/**
|
|
90
|
+
* WooksWs options or instance
|
|
91
|
+
*/
|
|
92
|
+
wooksWs?: WooksWs | TWooksWsOptions;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* ## Moost WebSocket Adapter
|
|
96
|
+
*
|
|
97
|
+
* Moost Adapter for WebSocket events, wrapping @wooksjs/event-ws.
|
|
98
|
+
* Supports standalone and HTTP-integrated modes.
|
|
99
|
+
*
|
|
100
|
+
* ### HTTP-integrated mode (recommended)
|
|
101
|
+
* ```ts
|
|
102
|
+
* | import { MoostHttp, Upgrade } from '@moostjs/event-http'
|
|
103
|
+
* | import { MoostWs, Message, MessageData } from '@moostjs/event-ws'
|
|
104
|
+
* | import { Moost, Param, Controller, Injectable } from 'moost'
|
|
105
|
+
* |
|
|
106
|
+
* | @Controller()
|
|
107
|
+
* | class AppController {
|
|
108
|
+
* | constructor(private ws: WooksWs) {}
|
|
109
|
+
* |
|
|
110
|
+
* | @Upgrade('/ws')
|
|
111
|
+
* | handleUpgrade() { return this.ws.upgrade() }
|
|
112
|
+
* | }
|
|
113
|
+
* |
|
|
114
|
+
* | @Controller('chat')
|
|
115
|
+
* | class ChatController {
|
|
116
|
+
* | @Message('message', 'rooms/:roomId')
|
|
117
|
+
* | onMessage(@Param('roomId') roomId: string, @MessageData() data: unknown) {
|
|
118
|
+
* | return { received: true, roomId }
|
|
119
|
+
* | }
|
|
120
|
+
* | }
|
|
121
|
+
* |
|
|
122
|
+
* | const app = new Moost()
|
|
123
|
+
* | const http = new MoostHttp()
|
|
124
|
+
* | const ws = new MoostWs({ httpApp: http.getHttpApp() })
|
|
125
|
+
* | app.adapter(http)
|
|
126
|
+
* | app.adapter(ws)
|
|
127
|
+
* | app.registerControllers(AppController, ChatController)
|
|
128
|
+
* | http.listen(3000)
|
|
129
|
+
* | app.init()
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare class MoostWs implements TMoostAdapter<TWsHandlerMeta> {
|
|
133
|
+
protected opts?: (TMoostWsOpts & {
|
|
134
|
+
/**
|
|
135
|
+
* WooksHttp instance or an adapter with getHttpApp() for HTTP-integrated mode.
|
|
136
|
+
* When provided, the WS adapter shares the HTTP server.
|
|
137
|
+
* Use @Upgrade() decorator on a handler method to register the upgrade route.
|
|
138
|
+
*/
|
|
139
|
+
httpApp?: {
|
|
140
|
+
getHttpApp(): unknown;
|
|
141
|
+
} | object;
|
|
142
|
+
}) | undefined;
|
|
143
|
+
readonly name = "ws";
|
|
144
|
+
protected wsApp: WooksWs;
|
|
145
|
+
constructor(opts?: (TMoostWsOpts & {
|
|
146
|
+
/**
|
|
147
|
+
* WooksHttp instance or an adapter with getHttpApp() for HTTP-integrated mode.
|
|
148
|
+
* When provided, the WS adapter shares the HTTP server.
|
|
149
|
+
* Use @Upgrade() decorator on a handler method to register the upgrade route.
|
|
150
|
+
*/
|
|
151
|
+
httpApp?: {
|
|
152
|
+
getHttpApp(): unknown;
|
|
153
|
+
} | object;
|
|
154
|
+
}) | undefined);
|
|
155
|
+
getWsApp(): WooksWs;
|
|
156
|
+
/**
|
|
157
|
+
* Start a standalone WebSocket server (without HTTP integration).
|
|
158
|
+
*/
|
|
159
|
+
listen(port: number, hostname?: string): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Stop the server, close all connections, clean up heartbeat.
|
|
162
|
+
*/
|
|
163
|
+
close(): void;
|
|
164
|
+
protected moost?: Moost;
|
|
165
|
+
onInit(moost: Moost): void;
|
|
166
|
+
getProvideRegistry(): moost.TProvideRegistry;
|
|
167
|
+
getLogger(): TConsoleBase;
|
|
168
|
+
bindHandler<T extends object = object>(opts: TMoostAdapterOptions<TWsHandlerMeta, T>): void;
|
|
169
|
+
protected bindMessageHandler<T extends object>(opts: TMoostAdapterOptions<TWsHandlerMeta, T>, handler: TWsMessageHandlerMeta & {
|
|
170
|
+
type: string;
|
|
171
|
+
}): void;
|
|
172
|
+
protected bindConnectHandler<T extends object>(opts: TMoostAdapterOptions<TWsHandlerMeta, T>): void;
|
|
173
|
+
protected bindDisconnectHandler<T extends object>(opts: TMoostAdapterOptions<TWsHandlerMeta, T>): void;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
interface TWsAppOptions {
|
|
177
|
+
/**
|
|
178
|
+
* WooksWs configuration options
|
|
179
|
+
*/
|
|
180
|
+
ws?: TWooksWsOptions;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Quick WS App factory class.
|
|
184
|
+
*
|
|
185
|
+
* Use this class to quickly build a standalone WebSocket application
|
|
186
|
+
* with controllers. It extends the Moost class and wraps MoostWs initialization.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```typescript
|
|
190
|
+
* import { WsApp } from '@moostjs/event-ws'
|
|
191
|
+
* import { ChatController } from './chat.controller.ts'
|
|
192
|
+
*
|
|
193
|
+
* new WsApp()
|
|
194
|
+
* .controllers(ChatController)
|
|
195
|
+
* .start(3000)
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
declare class WsApp extends Moost {
|
|
199
|
+
protected _wsOpts?: TWsAppOptions;
|
|
200
|
+
protected _wsAdapter?: MoostWs;
|
|
201
|
+
/**
|
|
202
|
+
* Registers one or more WS controllers.
|
|
203
|
+
*
|
|
204
|
+
* (Shortcut for `registerControllers` method.)
|
|
205
|
+
*/
|
|
206
|
+
controllers(...controllers: (object | Function | [string, object | Function])[]): this;
|
|
207
|
+
/**
|
|
208
|
+
* Configures the WS options.
|
|
209
|
+
*/
|
|
210
|
+
useWsOptions(wsOpts: TWsAppOptions): this;
|
|
211
|
+
/**
|
|
212
|
+
* Returns the underlying MoostWs adapter instance.
|
|
213
|
+
*/
|
|
214
|
+
getWsAdapter(): MoostWs | undefined;
|
|
215
|
+
/**
|
|
216
|
+
* Starts the standalone WebSocket application.
|
|
217
|
+
*
|
|
218
|
+
* @param port - Port to listen on
|
|
219
|
+
* @param hostname - Optional hostname
|
|
220
|
+
*/
|
|
221
|
+
start(port: number, hostname?: string): Promise<void>;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export { Connect, ConnectionId, Disconnect, Message, MessageData, MessageId, MessagePath, MessageType, MoostWs, RawMessage, WsApp };
|
|
225
|
+
export type { TMoostWsOpts, TWsConnectHandlerMeta, TWsHandlerMeta, TWsMessageHandlerMeta };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { Controller, Description, Intercept, Moost, Param, Resolve, defineAfterInterceptor, defineBeforeInterceptor, defineInterceptor, defineMoostEventHandler, getMoostMate } from "moost";
|
|
2
|
+
import { WooksWs, WooksWs as WooksWs$1, WsError, WsRoomManager, createWsApp, currentConnection, prepareTestWsConnectionContext, prepareTestWsMessageContext, useWsConnection, useWsConnection as useWsConnection$1, useWsMessage, useWsMessage as useWsMessage$1, useWsRooms, useWsServer, wsConnectionKind, wsMessageKind } from "@wooksjs/event-ws";
|
|
3
|
+
import { createProvideRegistry } from "@prostojs/infact";
|
|
4
|
+
|
|
5
|
+
//#region packages/event-ws/src/decorators/ws-method.decorator.ts
|
|
6
|
+
/**
|
|
7
|
+
* ## Define WebSocket Message Handler
|
|
8
|
+
* ### @MethodDecorator
|
|
9
|
+
*
|
|
10
|
+
* Registers a handler for routed WebSocket messages.
|
|
11
|
+
* The event and path correspond to the WsClientMessage protocol fields.
|
|
12
|
+
*
|
|
13
|
+
* @param event - message event type (e.g. "message", "rpc", "subscribe")
|
|
14
|
+
* @param path - route path with optional params (e.g. "/chat/rooms/:roomId")
|
|
15
|
+
*/ function Message(event, path) {
|
|
16
|
+
return getMoostMate().decorate("handlers", {
|
|
17
|
+
event,
|
|
18
|
+
path,
|
|
19
|
+
type: "WS_MESSAGE"
|
|
20
|
+
}, true);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* ## Define WebSocket Connection Handler
|
|
24
|
+
* ### @MethodDecorator
|
|
25
|
+
*
|
|
26
|
+
* Registers a handler that runs when a new WebSocket connection is established.
|
|
27
|
+
* Runs inside the connection context. Throwing or rejecting closes the connection.
|
|
28
|
+
*/ function Connect() {
|
|
29
|
+
return getMoostMate().decorate("handlers", { type: "WS_CONNECT" }, true);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* ## Define WebSocket Disconnection Handler
|
|
33
|
+
* ### @MethodDecorator
|
|
34
|
+
*
|
|
35
|
+
* Registers a handler that runs when a WebSocket connection closes.
|
|
36
|
+
*/ function Disconnect() {
|
|
37
|
+
return getMoostMate().decorate("handlers", { type: "WS_DISCONNECT" }, true);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region packages/event-ws/src/decorators/resolve.decorator.ts
|
|
42
|
+
/**
|
|
43
|
+
* Get the parsed WebSocket message payload.
|
|
44
|
+
* Only available in message handlers (@Message).
|
|
45
|
+
* @decorator
|
|
46
|
+
* @paramType T
|
|
47
|
+
*/ function MessageData() {
|
|
48
|
+
return Resolve(() => useWsMessage$1().data, "ws_data");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the raw WebSocket message (Buffer or string before parsing).
|
|
52
|
+
* Only available in message handlers (@Message).
|
|
53
|
+
* @decorator
|
|
54
|
+
* @paramType Buffer | string
|
|
55
|
+
*/ function RawMessage() {
|
|
56
|
+
return Resolve(() => useWsMessage$1().raw, "ws_raw");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the WebSocket message correlation ID.
|
|
60
|
+
* Only available in message handlers (@Message).
|
|
61
|
+
* @decorator
|
|
62
|
+
* @paramType string | number | undefined
|
|
63
|
+
*/ function MessageId() {
|
|
64
|
+
return Resolve(() => useWsMessage$1().id, "ws_message_id");
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the WebSocket message event type.
|
|
68
|
+
* Only available in message handlers (@Message).
|
|
69
|
+
* @decorator
|
|
70
|
+
* @paramType string
|
|
71
|
+
*/ function MessageType() {
|
|
72
|
+
return Resolve(() => useWsMessage$1().event, "ws_event");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the WebSocket message path.
|
|
76
|
+
* Only available in message handlers (@Message).
|
|
77
|
+
* @decorator
|
|
78
|
+
* @paramType string
|
|
79
|
+
*/ function MessagePath() {
|
|
80
|
+
return Resolve(() => useWsMessage$1().path, "ws_path");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the WebSocket connection ID (UUID).
|
|
84
|
+
* Available in all WS handlers (@Message, @Connect, @Disconnect).
|
|
85
|
+
* @decorator
|
|
86
|
+
* @paramType string
|
|
87
|
+
*/ function ConnectionId() {
|
|
88
|
+
return Resolve(() => useWsConnection$1().id, "ws_connection_id");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region packages/event-ws/src/event-ws.ts
|
|
93
|
+
function _define_property$1(obj, key, value) {
|
|
94
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
95
|
+
value,
|
|
96
|
+
enumerable: true,
|
|
97
|
+
configurable: true,
|
|
98
|
+
writable: true
|
|
99
|
+
});
|
|
100
|
+
else obj[key] = value;
|
|
101
|
+
return obj;
|
|
102
|
+
}
|
|
103
|
+
const LOGGER_TITLE = "moost-ws";
|
|
104
|
+
/**
|
|
105
|
+
* ## Moost WebSocket Adapter
|
|
106
|
+
*
|
|
107
|
+
* Moost Adapter for WebSocket events, wrapping @wooksjs/event-ws.
|
|
108
|
+
* Supports standalone and HTTP-integrated modes.
|
|
109
|
+
*
|
|
110
|
+
* ### HTTP-integrated mode (recommended)
|
|
111
|
+
* ```ts
|
|
112
|
+
* | import { MoostHttp, Upgrade } from '@moostjs/event-http'
|
|
113
|
+
* | import { MoostWs, Message, MessageData } from '@moostjs/event-ws'
|
|
114
|
+
* | import { Moost, Param, Controller, Injectable } from 'moost'
|
|
115
|
+
* |
|
|
116
|
+
* | @Controller()
|
|
117
|
+
* | class AppController {
|
|
118
|
+
* | constructor(private ws: WooksWs) {}
|
|
119
|
+
* |
|
|
120
|
+
* | @Upgrade('/ws')
|
|
121
|
+
* | handleUpgrade() { return this.ws.upgrade() }
|
|
122
|
+
* | }
|
|
123
|
+
* |
|
|
124
|
+
* | @Controller('chat')
|
|
125
|
+
* | class ChatController {
|
|
126
|
+
* | @Message('message', 'rooms/:roomId')
|
|
127
|
+
* | onMessage(@Param('roomId') roomId: string, @MessageData() data: unknown) {
|
|
128
|
+
* | return { received: true, roomId }
|
|
129
|
+
* | }
|
|
130
|
+
* | }
|
|
131
|
+
* |
|
|
132
|
+
* | const app = new Moost()
|
|
133
|
+
* | const http = new MoostHttp()
|
|
134
|
+
* | const ws = new MoostWs({ httpApp: http.getHttpApp() })
|
|
135
|
+
* | app.adapter(http)
|
|
136
|
+
* | app.adapter(ws)
|
|
137
|
+
* | app.registerControllers(AppController, ChatController)
|
|
138
|
+
* | http.listen(3000)
|
|
139
|
+
* | app.init()
|
|
140
|
+
* ```
|
|
141
|
+
*/ var MoostWs = class MoostWs {
|
|
142
|
+
getWsApp() {
|
|
143
|
+
return this.wsApp;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Start a standalone WebSocket server (without HTTP integration).
|
|
147
|
+
*/ listen(port, hostname) {
|
|
148
|
+
return this.wsApp.listen(port, hostname);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Stop the server, close all connections, clean up heartbeat.
|
|
152
|
+
*/ close() {
|
|
153
|
+
return this.wsApp.close();
|
|
154
|
+
}
|
|
155
|
+
onInit(moost) {
|
|
156
|
+
this.moost = moost;
|
|
157
|
+
}
|
|
158
|
+
getProvideRegistry() {
|
|
159
|
+
return createProvideRegistry([MoostWs, () => this], ["MoostWs", () => this], [WooksWs$1, () => this.getWsApp()], ["WooksWs", () => this.getWsApp()]);
|
|
160
|
+
}
|
|
161
|
+
getLogger() {
|
|
162
|
+
return this.wsApp.getLogger("[moost-ws]");
|
|
163
|
+
}
|
|
164
|
+
bindHandler(opts) {
|
|
165
|
+
for (const handler of opts.handlers) if (handler.type === "WS_MESSAGE") this.bindMessageHandler(opts, handler);
|
|
166
|
+
else if (handler.type === "WS_CONNECT") this.bindConnectHandler(opts);
|
|
167
|
+
else if (handler.type === "WS_DISCONNECT") this.bindDisconnectHandler(opts);
|
|
168
|
+
}
|
|
169
|
+
bindMessageHandler(opts, handler) {
|
|
170
|
+
const event = handler.event;
|
|
171
|
+
const path = typeof handler.path === "string" ? handler.path : typeof opts.method === "string" ? opts.method : "";
|
|
172
|
+
const targetPath = `${`${opts.prefix || ""}/${path}`.replaceAll(/\/\/+/g, "/")}`;
|
|
173
|
+
const fn = defineMoostEventHandler({
|
|
174
|
+
contextType: "WS_MESSAGE",
|
|
175
|
+
loggerTitle: LOGGER_TITLE,
|
|
176
|
+
getIterceptorHandler: opts.getIterceptorHandler,
|
|
177
|
+
getControllerInstance: opts.getInstance,
|
|
178
|
+
controllerMethod: opts.method,
|
|
179
|
+
controllerName: opts.controllerName,
|
|
180
|
+
resolveArgs: opts.resolveArgs,
|
|
181
|
+
targetPath,
|
|
182
|
+
handlerType: handler.type
|
|
183
|
+
});
|
|
184
|
+
this.wsApp.onMessage(event, targetPath, fn);
|
|
185
|
+
opts.logHandler(`[36m(ws:${event})[32m${targetPath}`);
|
|
186
|
+
opts.register(handler, targetPath, []);
|
|
187
|
+
}
|
|
188
|
+
bindConnectHandler(opts) {
|
|
189
|
+
const fn = defineMoostEventHandler({
|
|
190
|
+
contextType: "WS_CONNECT",
|
|
191
|
+
loggerTitle: LOGGER_TITLE,
|
|
192
|
+
getIterceptorHandler: opts.getIterceptorHandler,
|
|
193
|
+
getControllerInstance: opts.getInstance,
|
|
194
|
+
controllerMethod: opts.method,
|
|
195
|
+
resolveArgs: opts.resolveArgs,
|
|
196
|
+
targetPath: "__ws_connect__",
|
|
197
|
+
handlerType: "WS_CONNECT"
|
|
198
|
+
});
|
|
199
|
+
this.wsApp.onConnect(fn);
|
|
200
|
+
opts.logHandler(`[36m(ws:connect)`);
|
|
201
|
+
opts.register({ type: "WS_CONNECT" }, "__ws_connect__", []);
|
|
202
|
+
}
|
|
203
|
+
bindDisconnectHandler(opts) {
|
|
204
|
+
const fn = defineMoostEventHandler({
|
|
205
|
+
contextType: "WS_DISCONNECT",
|
|
206
|
+
loggerTitle: LOGGER_TITLE,
|
|
207
|
+
getIterceptorHandler: opts.getIterceptorHandler,
|
|
208
|
+
getControllerInstance: opts.getInstance,
|
|
209
|
+
controllerMethod: opts.method,
|
|
210
|
+
resolveArgs: opts.resolveArgs,
|
|
211
|
+
targetPath: "__ws_disconnect__",
|
|
212
|
+
handlerType: "WS_DISCONNECT"
|
|
213
|
+
});
|
|
214
|
+
this.wsApp.onDisconnect(fn);
|
|
215
|
+
opts.logHandler(`[36m(ws:disconnect)`);
|
|
216
|
+
opts.register({ type: "WS_DISCONNECT" }, "__ws_disconnect__", []);
|
|
217
|
+
}
|
|
218
|
+
constructor(opts) {
|
|
219
|
+
_define_property$1(this, "opts", void 0);
|
|
220
|
+
_define_property$1(this, "name", void 0);
|
|
221
|
+
_define_property$1(this, "wsApp", void 0);
|
|
222
|
+
_define_property$1(this, "moost", void 0);
|
|
223
|
+
this.opts = opts;
|
|
224
|
+
this.name = "ws";
|
|
225
|
+
const wsOpts = opts?.wooksWs;
|
|
226
|
+
if (wsOpts && wsOpts instanceof WooksWs$1) this.wsApp = wsOpts;
|
|
227
|
+
else {
|
|
228
|
+
const httpApp = opts?.httpApp && "getHttpApp" in opts.httpApp ? opts.httpApp.getHttpApp() : opts?.httpApp;
|
|
229
|
+
if (httpApp) this.wsApp = createWsApp(httpApp, wsOpts || {});
|
|
230
|
+
else this.wsApp = createWsApp(wsOpts || {});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region packages/event-ws/src/quick-ws.ts
|
|
237
|
+
function _define_property(obj, key, value) {
|
|
238
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
239
|
+
value,
|
|
240
|
+
enumerable: true,
|
|
241
|
+
configurable: true,
|
|
242
|
+
writable: true
|
|
243
|
+
});
|
|
244
|
+
else obj[key] = value;
|
|
245
|
+
return obj;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Quick WS App factory class.
|
|
249
|
+
*
|
|
250
|
+
* Use this class to quickly build a standalone WebSocket application
|
|
251
|
+
* with controllers. It extends the Moost class and wraps MoostWs initialization.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* import { WsApp } from '@moostjs/event-ws'
|
|
256
|
+
* import { ChatController } from './chat.controller.ts'
|
|
257
|
+
*
|
|
258
|
+
* new WsApp()
|
|
259
|
+
* .controllers(ChatController)
|
|
260
|
+
* .start(3000)
|
|
261
|
+
* ```
|
|
262
|
+
*/ var WsApp = class extends Moost {
|
|
263
|
+
/**
|
|
264
|
+
* Registers one or more WS controllers.
|
|
265
|
+
*
|
|
266
|
+
* (Shortcut for `registerControllers` method.)
|
|
267
|
+
*/ controllers(...controllers) {
|
|
268
|
+
return this.registerControllers(...controllers);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Configures the WS options.
|
|
272
|
+
*/ useWsOptions(wsOpts) {
|
|
273
|
+
this._wsOpts = wsOpts;
|
|
274
|
+
return this;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Returns the underlying MoostWs adapter instance.
|
|
278
|
+
*/ getWsAdapter() {
|
|
279
|
+
return this._wsAdapter;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Starts the standalone WebSocket application.
|
|
283
|
+
*
|
|
284
|
+
* @param port - Port to listen on
|
|
285
|
+
* @param hostname - Optional hostname
|
|
286
|
+
*/ async start(port, hostname) {
|
|
287
|
+
this._wsAdapter = new MoostWs({ wooksWs: this._wsOpts?.ws });
|
|
288
|
+
this.adapter(this._wsAdapter);
|
|
289
|
+
await this.init();
|
|
290
|
+
return this._wsAdapter.listen(port, hostname);
|
|
291
|
+
}
|
|
292
|
+
constructor(...args) {
|
|
293
|
+
super(...args), _define_property(this, "_wsOpts", void 0), _define_property(this, "_wsAdapter", void 0);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
//#endregion
|
|
298
|
+
export { Connect, ConnectionId, Controller, Description, Disconnect, Intercept, Message, MessageData, MessageId, MessagePath, MessageType, MoostWs, Param, RawMessage, WooksWs, WsApp, WsError, WsRoomManager, currentConnection, defineAfterInterceptor, defineBeforeInterceptor, defineInterceptor, prepareTestWsConnectionContext, prepareTestWsMessageContext, useWsConnection, useWsMessage, useWsRooms, useWsServer, wsConnectionKind, wsMessageKind };
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@moostjs/event-ws",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "@moostjs/event-ws",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"composables",
|
|
7
|
+
"framework",
|
|
8
|
+
"moost",
|
|
9
|
+
"moostjs",
|
|
10
|
+
"prostojs",
|
|
11
|
+
"websocket",
|
|
12
|
+
"wooksjs"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/moostjs/moostjs/tree/main/packages/event-ws#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/moostjs/moostjs/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Artem Maltsev",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/moostjs/moostjs.git",
|
|
23
|
+
"directory": "packages/event-ws"
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"moostjs-event-ws-skill": "./scripts/setup-skills.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"skills",
|
|
31
|
+
"scripts/setup-skills.js"
|
|
32
|
+
],
|
|
33
|
+
"type": "module",
|
|
34
|
+
"sideEffects": false,
|
|
35
|
+
"main": "dist/index.cjs",
|
|
36
|
+
"module": "dist/index.mjs",
|
|
37
|
+
"types": "dist/index.d.ts",
|
|
38
|
+
"exports": {
|
|
39
|
+
"./package.json": "./package.json",
|
|
40
|
+
".": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"import": "./dist/index.mjs",
|
|
43
|
+
"require": "./dist/index.cjs"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@wooksjs/event-ws": "^0.7.3"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"vitest": "3.2.4"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@prostojs/infact": "^0.4.1",
|
|
54
|
+
"@wooksjs/event-core": "^0.7.3",
|
|
55
|
+
"moost": "^0.6.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"pub": "pnpm publish --access public",
|
|
59
|
+
"test": "vitest",
|
|
60
|
+
"setup-skills": "node ./scripts/setup-skills.js"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* prettier-ignore */
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import os from 'os'
|
|
6
|
+
import { fileURLToPath } from 'url'
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
|
|
10
|
+
const SKILL_NAME = 'moostjs-event-ws'
|
|
11
|
+
const SKILL_SRC = path.join(__dirname, '..', 'skills', SKILL_NAME)
|
|
12
|
+
|
|
13
|
+
if (!fs.existsSync(SKILL_SRC)) {
|
|
14
|
+
console.error(`No skills found at ${SKILL_SRC}`)
|
|
15
|
+
console.error('Add your SKILL.md files to the skills/' + SKILL_NAME + '/ directory first.')
|
|
16
|
+
process.exit(1)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const AGENTS = {
|
|
20
|
+
'Claude Code': { dir: '.claude/skills', global: path.join(os.homedir(), '.claude', 'skills') },
|
|
21
|
+
'Cursor': { dir: '.cursor/skills', global: path.join(os.homedir(), '.cursor', 'skills') },
|
|
22
|
+
'Windsurf': { dir: '.windsurf/skills', global: path.join(os.homedir(), '.windsurf', 'skills') },
|
|
23
|
+
'Codex': { dir: '.codex/skills', global: path.join(os.homedir(), '.codex', 'skills') },
|
|
24
|
+
'OpenCode': { dir: '.opencode/skills', global: path.join(os.homedir(), '.opencode', 'skills') },
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const args = process.argv.slice(2)
|
|
28
|
+
const isGlobal = args.includes('--global') || args.includes('-g')
|
|
29
|
+
const isPostinstall = args.includes('--postinstall')
|
|
30
|
+
let installed = 0, skipped = 0
|
|
31
|
+
const installedDirs = []
|
|
32
|
+
|
|
33
|
+
for (const [agentName, cfg] of Object.entries(AGENTS)) {
|
|
34
|
+
const targetBase = isGlobal ? cfg.global : path.join(process.cwd(), cfg.dir)
|
|
35
|
+
const agentRootDir = path.dirname(cfg.global) // Check if the agent has ever been installed globally
|
|
36
|
+
|
|
37
|
+
// In postinstall mode: silently skip agents that aren't set up globally
|
|
38
|
+
if (isPostinstall || isGlobal) {
|
|
39
|
+
if (!fs.existsSync(agentRootDir)) { skipped++; continue }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dest = path.join(targetBase, SKILL_NAME)
|
|
43
|
+
try {
|
|
44
|
+
fs.mkdirSync(dest, { recursive: true })
|
|
45
|
+
fs.cpSync(SKILL_SRC, dest, { recursive: true })
|
|
46
|
+
console.log(`ā
${agentName}: installed to ${dest}`)
|
|
47
|
+
installed++
|
|
48
|
+
if (!isGlobal) installedDirs.push(cfg.dir + '/' + SKILL_NAME)
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.warn(`ā ļø ${agentName}: failed ā ${err.message}`)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add locally-installed skill dirs to .gitignore
|
|
55
|
+
if (!isGlobal && installedDirs.length > 0) {
|
|
56
|
+
const gitignorePath = path.join(process.cwd(), '.gitignore')
|
|
57
|
+
let gitignoreContent = ''
|
|
58
|
+
try { gitignoreContent = fs.readFileSync(gitignorePath, 'utf8') } catch {}
|
|
59
|
+
const linesToAdd = installedDirs.filter(d => !gitignoreContent.includes(d))
|
|
60
|
+
if (linesToAdd.length > 0) {
|
|
61
|
+
const hasHeader = gitignoreContent.includes('# AI agent skills')
|
|
62
|
+
const block = (gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '')
|
|
63
|
+
+ (hasHeader ? '' : '\n# AI agent skills (auto-generated by setup-skills)\n')
|
|
64
|
+
+ linesToAdd.join('\n') + '\n'
|
|
65
|
+
fs.appendFileSync(gitignorePath, block)
|
|
66
|
+
console.log(`š Added ${linesToAdd.length} entries to .gitignore`)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (installed === 0 && isPostinstall) {
|
|
71
|
+
// Silence is fine ā no agents present, nothing to do
|
|
72
|
+
} else if (installed === 0 && skipped === Object.keys(AGENTS).length) {
|
|
73
|
+
console.log('No agent directories detected. Try --global or run without it for project-local install.')
|
|
74
|
+
} else if (installed === 0) {
|
|
75
|
+
console.log('Nothing installed. Run without --global to install project-locally.')
|
|
76
|
+
} else {
|
|
77
|
+
console.log(`\n⨠Done! Restart your AI agent to pick up the "${SKILL_NAME}" skill.`)
|
|
78
|
+
}
|