ws-stomp 1.1.5 → 2.0.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/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # ws-stomp
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/ws-stomp.svg?style=flat-square)](https://www.npmjs.com/package/ws-stomp)
3
+ [![npm version](https://img.shields.io/npm/v/ws-Stomp.svg?style=flat-square)](https://www.npmjs.com/package/ws-stomp)
4
4
  [![Alt](https://img.shields.io/npm/dt/ws-stomp?style=flat-square)](https://npmcharts.com/compare/ws-stomp?minimal=true)
5
5
  ![Alt](https://img.shields.io/github/license/mivui/node-ws-stomp?style=flat-square)
6
6
 
7
- ### ws-stomp is a simple Node.js server base on websocket stomp
7
+ ### implementation of the stomp version of ws
8
8
 
9
9
  ### install
10
10
 
@@ -12,71 +12,144 @@
12
12
  npm i ws-stomp
13
13
  ```
14
14
 
15
- ### http
15
+ > http
16
16
 
17
17
  ```ts
18
18
  import { createServer } from 'http';
19
- import stomp from 'ws-stomp';
19
+ import { Stomp } from 'ws-stomp';
20
20
 
21
21
  const server = createServer();
22
- stomp.server(server, '/ws');
22
+ Stomp.server(server, '/ws');
23
23
  server.listen(3000);
24
24
  // ws://lcalhost:3000/ws
25
25
  ```
26
26
 
27
- ### send
27
+ > send
28
28
 
29
29
  ```ts
30
- import stomp from 'ws-stomp';
30
+ import { Stomp } from 'ws-stomp';
31
31
 
32
- function publish() {
33
- stomp.send('/topic/something', JSON.stringify({ name: 'name' }), { token: 'token' });
34
- }
32
+ Stomp.send('/topic/something', JSON.stringify({ name: 'name' }), { token: 'token' });
35
33
  ```
36
34
 
37
- ### subscribe
35
+ > subscribe
38
36
 
39
37
  ```ts
40
- import stomp from 'ws-stomp';
38
+ import { Stomp } from 'ws-stomp';
41
39
 
42
- function subscribe() {
43
- stomp.subscribe('/topic/greetings', (e) => {
44
- const body = e.body;
45
- });
46
- }
40
+ Stomp.subscribe('/topic/greetings', (e) => {
41
+ const body = e.body;
42
+ });
47
43
  ```
48
44
 
49
- ### unsubscribe
45
+ > unsubscribe
50
46
 
51
47
  ```ts
52
- import stomp from 'ws-stomp';
48
+ import { Stomp } from 'ws-stomp';
53
49
 
54
- function unsubscribe() {
55
- stomp.unsubscribe('/topic/greetings');
56
- }
50
+ Stomp.unsubscribe('/topic/greetings');
57
51
  ```
58
52
 
59
- ### server and client
53
+ > express
60
54
 
61
- ### server.js
55
+ #### server.js
62
56
 
63
57
  ```ts
64
58
  import express from 'express';
65
- import stomp from 'ws-stomp';
59
+ import { Stomp } from 'ws-stomp';
66
60
 
67
61
  const app = express();
68
62
  app.get('/send', (_, res) => {
69
- stomp.send('/topic/something', 'payload');
63
+ Stomp.send('/topic/something', 'payload');
70
64
  res.status(200).json({});
71
65
  });
72
66
  const server = app.listen(3000);
73
- stomp.server(server, '/ws');
74
- stomp.subscribe('/topic/greetings', (message) => {
67
+ Stomp.server(server, '/ws');
68
+ Stomp.subscribe('/topic/greetings', (message) => {
75
69
  console.log(message.body);
76
70
  });
77
71
  ```
78
72
 
79
- ### browser.js
73
+ #### browser.js
74
+
75
+ ```ts
76
+ import { Client } from '@stomp/stompjs';
77
+
78
+ const client = new Client({
79
+ brokerURL: 'ws://lcalhost:3000/ws',
80
+ onConnect: () => {
81
+ client.publish({ destination: '/topic/greetings', body: 'Hello World!' });
82
+ client.subscribe('/topic/something', (message) => {
83
+ console.log(message.body);
84
+ });
85
+ },
86
+ });
87
+ client.activate();
88
+ ```
89
+
90
+ > nest
91
+
92
+ #### install
93
+
94
+ ```shell
95
+ npm i @nestjs/websockets
96
+ ```
97
+
98
+ #### websocket.gateway.ts
99
+
100
+ ```ts
101
+ import {
102
+ MessageBody,
103
+ SubscribeMessage,
104
+ WebSocketGateway,
105
+ WebSocketServer,
106
+ } from '@nestjs/websockets';
107
+
108
+ import { StompFrame, StompServer } from 'ws-stomp';
109
+
110
+ @WebSocketGateway()
111
+ export class WebsocketGateway {
112
+ @WebSocketServer()
113
+ private readonly server: StompServer;
114
+
115
+ @SubscribeMessage('/topic/greetings')
116
+ public send(@MessageBody() message: StompFrame) {
117
+ console.log(message.body);
118
+ this.server.send('/topic/something', message.body);
119
+ }
120
+ }
121
+ ```
122
+
123
+ #### app.module.ts
124
+
125
+ ```ts
126
+ import { Module } from '@nestjs/common';
127
+
128
+ import { WebsocketGateway } from './websocket.gateway';
129
+
130
+ @Module({
131
+ providers: [WebsocketGateway],
132
+ })
133
+ export class AppModule {}
134
+ ```
135
+
136
+ #### main.ts
137
+
138
+ ```ts
139
+ import { StompAdapter } from 'ws-stomp';
140
+ import { NestFactory } from '@nestjs/core';
141
+
142
+ import { AppModule } from './app.module';
143
+
144
+ async function bootstrap() {
145
+ const app = await NestFactory.create(AppModule);
146
+ app.useWebSocketAdapter(new StompAdapter({ app, path: '/ws' }));
147
+ await app.listen(3000);
148
+ }
149
+ bootstrap();
150
+ ```
151
+
152
+ #### browser.js
80
153
 
81
154
  ```ts
82
155
  import { Client } from '@stomp/stompjs';
package/dist/index.cjs.js CHANGED
@@ -1,16 +1,22 @@
1
1
  'use strict';
2
2
 
3
+ var websockets = require('@nestjs/websockets');
4
+ var rxjs = require('rxjs');
3
5
  var crypto = require('crypto');
4
- var WebSocket = require('ws');
6
+ var ws = require('ws');
5
7
 
6
- class Autowired {
7
- constructor() { }
8
- static container = {};
9
- static register(name, instance) {
10
- this.container[name] = instance;
8
+ class SimpleAuthProvider {
9
+ login;
10
+ passcode;
11
+ constructor(login, passcode) {
12
+ this.login = login ?? 'anonymous';
13
+ this.passcode = passcode ?? 'anonymous';
11
14
  }
12
- static get(name) {
13
- return this.container[name];
15
+ authenticate(frame) {
16
+ const { login } = frame.headers;
17
+ const { passcode } = frame.headers;
18
+ const success = Boolean(login) && Boolean(passcode) && login === this.login && passcode === this.passcode;
19
+ return success;
14
20
  }
15
21
  }
16
22
 
@@ -95,16 +101,15 @@ class StompFrame {
95
101
  }
96
102
  }
97
103
 
98
- class StompServer {
99
- ws;
100
- subscribeClients = []; //已订阅的客户端
101
- subscribehandler = new Map(); //服务端订阅处理
102
- authProvider;
104
+ class StompServer extends ws.WebSocketServer {
105
+ _clients = []; // 已订阅的客户端
106
+ _handlers = new Map(); // 服务端订阅处理
107
+ auth;
103
108
  constructor(options) {
104
- const { server, path, authProvider } = options;
105
- this.authProvider = authProvider;
106
- this.ws = new WebSocket.WebSocketServer({ server, path });
107
- this.ws.on('connection', (ws) => {
109
+ const { server, path, auth } = options;
110
+ super({ server, path });
111
+ this.auth = auth;
112
+ this.on('connection', (ws) => {
108
113
  ws.on('message', (data) => {
109
114
  this.handleMessage(data, ws);
110
115
  });
@@ -113,9 +118,6 @@ class StompServer {
113
118
  });
114
119
  });
115
120
  }
116
- static server(options) {
117
- return new StompServer(options);
118
- }
119
121
  async handleMessage(data, ws) {
120
122
  try {
121
123
  const frame = StompFrame.parse(data);
@@ -160,7 +162,7 @@ class StompServer {
160
162
  }
161
163
  async handleConnect(frame, ws) {
162
164
  // 认证检查(如果启用)
163
- if (this.authProvider && !(await this.authProvider.authenticate(frame))) {
165
+ if (this.auth && !(await this.auth.authenticate(frame))) {
164
166
  this.sendError(ws, 'Authentication failed');
165
167
  ws.close();
166
168
  return;
@@ -174,7 +176,7 @@ class StompServer {
174
176
  }
175
177
  handleSend(frame, _ws) {
176
178
  const { destination } = frame.headers;
177
- this.subscribehandler.forEach((callback, key) => {
179
+ this._handlers.forEach((callback, key) => {
178
180
  if (destination === key) {
179
181
  callback(frame);
180
182
  }
@@ -187,7 +189,7 @@ class StompServer {
187
189
  this.sendError(ws, 'Missing subscription headers');
188
190
  return;
189
191
  }
190
- this.subscribeClients.push({
192
+ this._clients.push({
191
193
  id: subId,
192
194
  destination,
193
195
  ws,
@@ -195,7 +197,7 @@ class StompServer {
195
197
  }
196
198
  handleUnsubscribe(frame, ws) {
197
199
  const subId = frame.headers.id;
198
- this.subscribeClients = this.subscribeClients.filter((sub) => !(sub.id === subId && sub.ws === ws));
200
+ this._clients = this._clients.filter((sub) => !(sub.id === subId && sub.ws === ws));
199
201
  }
200
202
  handleDisconnect(ws) {
201
203
  this.cleanupSubscriptions(ws);
@@ -203,15 +205,15 @@ class StompServer {
203
205
  }
204
206
  // 清理断开连接的订阅
205
207
  cleanupSubscriptions(ws) {
206
- this.subscribeClients = this.subscribeClients.filter((sub) => sub.ws !== ws);
208
+ this._clients = this._clients.filter((sub) => sub.ws !== ws);
207
209
  }
208
210
  // 清理断开连接的WebSocket
209
211
  cleanupWebsockets() {
210
- this.subscribeClients = this.subscribeClients.filter((sub) => sub.ws.readyState === WebSocket.OPEN);
212
+ this._clients = this._clients.filter((sub) => sub.ws.readyState === ws.WebSocket.OPEN);
211
213
  }
212
214
  // 服务端主动发送消息
213
215
  send(destination, body, headers = {}) {
214
- const subs = this.subscribeClients.filter((sub) => sub.destination === destination);
216
+ const subs = this._clients.filter((sub) => sub.destination === destination);
215
217
  if (subs.length) {
216
218
  subs.forEach((sub) => {
217
219
  const frame = new StompFrame(StompCommand.MESSAGE, {
@@ -221,7 +223,7 @@ class StompServer {
221
223
  subscription: sub.id,
222
224
  ...headers,
223
225
  }, body);
224
- if (sub.ws.readyState === WebSocket.OPEN) {
226
+ if (sub.ws.readyState === ws.WebSocket.OPEN) {
225
227
  sub.ws.send(frame.serialize());
226
228
  }
227
229
  });
@@ -230,10 +232,10 @@ class StompServer {
230
232
  console.log(`${destination} unsubscribed client!`);
231
233
  }
232
234
  subscribe(destination, callback) {
233
- this.subscribehandler.set(destination, callback);
235
+ this._handlers.set(destination, callback);
234
236
  }
235
237
  unsubscribe(destination) {
236
- this.subscribehandler.delete(destination);
238
+ this._handlers.delete(destination);
237
239
  }
238
240
  sendError(ws, message) {
239
241
  const errorFrame = new StompFrame(StompCommand.ERROR, { 'content-type': 'text/plain' }, message);
@@ -241,25 +243,73 @@ class StompServer {
241
243
  }
242
244
  }
243
245
 
244
- const className = 'stompServer';
246
+ class StompAdapter extends websockets.AbstractWsAdapter {
247
+ server;
248
+ _options;
249
+ constructor(options) {
250
+ super(options.app);
251
+ this._options = options;
252
+ }
253
+ create(_port, _options) {
254
+ if (this.server)
255
+ return this.server;
256
+ const { path, auth } = this._options;
257
+ const server = new StompServer({ server: this.httpServer, path, auth });
258
+ this.server = server;
259
+ return server;
260
+ }
261
+ bindMessageHandlers(client, handlers, transform) {
262
+ rxjs.fromEvent(client, 'message')
263
+ .pipe(rxjs.mergeMap((data) => this.bindMessageHandler(data, handlers, transform)), rxjs.filter((result) => result !== undefined))
264
+ .subscribe((response) => {
265
+ client.send(response.data);
266
+ });
267
+ }
268
+ bindMessageHandler(message, handlers, transform) {
269
+ const frame = StompFrame.parse(message.data);
270
+ if (frame.command === StompCommand.SEND) {
271
+ const { destination } = frame.headers;
272
+ const messageHandler = handlers.find((handler) => handler.message === destination);
273
+ if (!messageHandler)
274
+ return rxjs.EMPTY;
275
+ return transform(messageHandler.callback(frame));
276
+ }
277
+ return rxjs.EMPTY;
278
+ }
279
+ }
280
+
281
+ class Autowired {
282
+ constructor() { }
283
+ static container = {};
284
+ static register(name, instance) {
285
+ this.container[name] = instance;
286
+ }
287
+ static get(name) {
288
+ return this.container[name];
289
+ }
290
+ }
245
291
  class Stomp {
246
292
  constructor() { }
247
- static server(server, path, authProvider) {
248
- const stomp = StompServer.server({ server, path, authProvider });
249
- Autowired.register(className, stomp);
293
+ static server(server, path, auth) {
294
+ const stomp = new StompServer({ server, path, auth });
295
+ Autowired.register(StompServer.name, stomp);
250
296
  }
251
297
  static send(destination, body, headers = {}) {
252
- const server = Autowired.get(className);
298
+ const server = Autowired.get(StompServer.name);
253
299
  server?.send(destination, body, headers);
254
300
  }
255
301
  static subscribe(destination, callback) {
256
- const server = Autowired.get(className);
302
+ const server = Autowired.get(StompServer.name);
257
303
  server?.subscribe(destination, callback);
258
304
  }
259
305
  static unsubscribe(destination) {
260
- const server = Autowired.get(className);
306
+ const server = Autowired.get(StompServer.name);
261
307
  server?.unsubscribe(destination);
262
308
  }
263
309
  }
264
310
 
265
- module.exports = Stomp;
311
+ exports.SimpleAuthProvider = SimpleAuthProvider;
312
+ exports.Stomp = Stomp;
313
+ exports.StompAdapter = StompAdapter;
314
+ exports.StompFrame = StompFrame;
315
+ exports.StompServer = StompServer;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,12 @@
1
+ import { INestApplicationContext, WsMessageHandler } from '@nestjs/common';
2
+ import { AbstractWsAdapter, GatewayMetadata } from '@nestjs/websockets';
3
+ import { Observable } from 'rxjs';
4
+ import WebSocket, { WebSocketServer } from 'ws';
1
5
  import { Server } from 'http';
2
6
  import { Server as Server$1 } from 'https';
3
7
 
4
8
  declare enum StompCommand {
5
- PING = "PING",
9
+ PING = "PING",//custom usage
6
10
  CONNECT = "CONNECT",
7
11
  CONNECTED = "CONNECTED",
8
12
  SEND = "SEND",
@@ -30,13 +34,59 @@ declare class StompFrame {
30
34
  interface AuthProvider {
31
35
  authenticate: (frame: StompFrame) => Promise<boolean> | boolean;
32
36
  }
37
+ declare class SimpleAuthProvider implements AuthProvider {
38
+ private readonly login;
39
+ private readonly passcode;
40
+ constructor(login?: string, passcode?: string);
41
+ authenticate(frame: StompFrame): boolean;
42
+ }
43
+
44
+ interface StompServerOptions {
45
+ server: Server | Server$1;
46
+ path: string;
47
+ auth?: AuthProvider;
48
+ }
49
+ declare class StompServer extends WebSocketServer {
50
+ private _clients;
51
+ private readonly _handlers;
52
+ private readonly auth?;
53
+ constructor(options: StompServerOptions);
54
+ private handleMessage;
55
+ private handleConnect;
56
+ private handleSend;
57
+ private handleSubscribe;
58
+ private handleUnsubscribe;
59
+ private handleDisconnect;
60
+ private cleanupSubscriptions;
61
+ private cleanupWebsockets;
62
+ send(destination: string, body: string, headers?: Record<string, string>): void;
63
+ subscribe(destination: string, callback: (frame: StompFrame) => void): void;
64
+ unsubscribe(destination: string): void;
65
+ private sendError;
66
+ }
67
+
68
+ interface StompAdapterOptions extends Omit<StompServerOptions, 'server'> {
69
+ app: INestApplicationContext;
70
+ }
71
+ interface Message {
72
+ data: Buffer;
73
+ }
74
+ declare class StompAdapter extends AbstractWsAdapter {
75
+ private server?;
76
+ private readonly _options;
77
+ constructor(options: StompAdapterOptions);
78
+ create(_port: number, _options?: GatewayMetadata): StompServer;
79
+ bindMessageHandlers(client: WebSocket, handlers: WsMessageHandler[], transform: (data: Observable<StompFrame>) => Observable<Message>): void;
80
+ bindMessageHandler(message: Message, handlers: WsMessageHandler[], transform: (data: Observable<StompFrame>) => Observable<Message>): Observable<Message>;
81
+ }
33
82
 
34
83
  declare class Stomp {
35
84
  private constructor();
36
- static server(server: Server | Server$1, path: string, authProvider?: AuthProvider): void;
85
+ static server(server: Server | Server$1, path: string, auth?: AuthProvider): void;
37
86
  static send(destination: string, body: string, headers?: Record<string, string>): void;
38
87
  static subscribe(destination: string, callback: (frame: StompFrame) => void): void;
39
88
  static unsubscribe(destination: string): void;
40
89
  }
41
90
 
42
- export { Stomp as default };
91
+ export { SimpleAuthProvider, Stomp, StompAdapter, StompFrame, StompServer };
92
+ export type { AuthProvider, StompServerOptions };
package/dist/index.esm.js CHANGED
@@ -1,14 +1,20 @@
1
- import crypto from 'crypto';
2
- import WebSocket, { WebSocketServer } from 'ws';
1
+ import { AbstractWsAdapter } from '@nestjs/websockets';
2
+ import { fromEvent, mergeMap, filter, EMPTY } from 'rxjs';
3
+ import { randomUUID } from 'crypto';
4
+ import { WebSocketServer, WebSocket } from 'ws';
3
5
 
4
- class Autowired {
5
- constructor() { }
6
- static container = {};
7
- static register(name, instance) {
8
- this.container[name] = instance;
6
+ class SimpleAuthProvider {
7
+ login;
8
+ passcode;
9
+ constructor(login, passcode) {
10
+ this.login = login ?? 'anonymous';
11
+ this.passcode = passcode ?? 'anonymous';
9
12
  }
10
- static get(name) {
11
- return this.container[name];
13
+ authenticate(frame) {
14
+ const { login } = frame.headers;
15
+ const { passcode } = frame.headers;
16
+ const success = Boolean(login) && Boolean(passcode) && login === this.login && passcode === this.passcode;
17
+ return success;
12
18
  }
13
19
  }
14
20
 
@@ -93,16 +99,15 @@ class StompFrame {
93
99
  }
94
100
  }
95
101
 
96
- class StompServer {
97
- ws;
98
- subscribeClients = []; //已订阅的客户端
99
- subscribehandler = new Map(); //服务端订阅处理
100
- authProvider;
102
+ class StompServer extends WebSocketServer {
103
+ _clients = []; // 已订阅的客户端
104
+ _handlers = new Map(); // 服务端订阅处理
105
+ auth;
101
106
  constructor(options) {
102
- const { server, path, authProvider } = options;
103
- this.authProvider = authProvider;
104
- this.ws = new WebSocketServer({ server, path });
105
- this.ws.on('connection', (ws) => {
107
+ const { server, path, auth } = options;
108
+ super({ server, path });
109
+ this.auth = auth;
110
+ this.on('connection', (ws) => {
106
111
  ws.on('message', (data) => {
107
112
  this.handleMessage(data, ws);
108
113
  });
@@ -111,9 +116,6 @@ class StompServer {
111
116
  });
112
117
  });
113
118
  }
114
- static server(options) {
115
- return new StompServer(options);
116
- }
117
119
  async handleMessage(data, ws) {
118
120
  try {
119
121
  const frame = StompFrame.parse(data);
@@ -158,7 +160,7 @@ class StompServer {
158
160
  }
159
161
  async handleConnect(frame, ws) {
160
162
  // 认证检查(如果启用)
161
- if (this.authProvider && !(await this.authProvider.authenticate(frame))) {
163
+ if (this.auth && !(await this.auth.authenticate(frame))) {
162
164
  this.sendError(ws, 'Authentication failed');
163
165
  ws.close();
164
166
  return;
@@ -172,7 +174,7 @@ class StompServer {
172
174
  }
173
175
  handleSend(frame, _ws) {
174
176
  const { destination } = frame.headers;
175
- this.subscribehandler.forEach((callback, key) => {
177
+ this._handlers.forEach((callback, key) => {
176
178
  if (destination === key) {
177
179
  callback(frame);
178
180
  }
@@ -185,7 +187,7 @@ class StompServer {
185
187
  this.sendError(ws, 'Missing subscription headers');
186
188
  return;
187
189
  }
188
- this.subscribeClients.push({
190
+ this._clients.push({
189
191
  id: subId,
190
192
  destination,
191
193
  ws,
@@ -193,7 +195,7 @@ class StompServer {
193
195
  }
194
196
  handleUnsubscribe(frame, ws) {
195
197
  const subId = frame.headers.id;
196
- this.subscribeClients = this.subscribeClients.filter((sub) => !(sub.id === subId && sub.ws === ws));
198
+ this._clients = this._clients.filter((sub) => !(sub.id === subId && sub.ws === ws));
197
199
  }
198
200
  handleDisconnect(ws) {
199
201
  this.cleanupSubscriptions(ws);
@@ -201,20 +203,20 @@ class StompServer {
201
203
  }
202
204
  // 清理断开连接的订阅
203
205
  cleanupSubscriptions(ws) {
204
- this.subscribeClients = this.subscribeClients.filter((sub) => sub.ws !== ws);
206
+ this._clients = this._clients.filter((sub) => sub.ws !== ws);
205
207
  }
206
208
  // 清理断开连接的WebSocket
207
209
  cleanupWebsockets() {
208
- this.subscribeClients = this.subscribeClients.filter((sub) => sub.ws.readyState === WebSocket.OPEN);
210
+ this._clients = this._clients.filter((sub) => sub.ws.readyState === WebSocket.OPEN);
209
211
  }
210
212
  // 服务端主动发送消息
211
213
  send(destination, body, headers = {}) {
212
- const subs = this.subscribeClients.filter((sub) => sub.destination === destination);
214
+ const subs = this._clients.filter((sub) => sub.destination === destination);
213
215
  if (subs.length) {
214
216
  subs.forEach((sub) => {
215
217
  const frame = new StompFrame(StompCommand.MESSAGE, {
216
218
  destination,
217
- 'message-id': crypto.randomUUID(),
219
+ 'message-id': randomUUID(),
218
220
  timestamp: `${Date.now()}`,
219
221
  subscription: sub.id,
220
222
  ...headers,
@@ -228,10 +230,10 @@ class StompServer {
228
230
  console.log(`${destination} unsubscribed client!`);
229
231
  }
230
232
  subscribe(destination, callback) {
231
- this.subscribehandler.set(destination, callback);
233
+ this._handlers.set(destination, callback);
232
234
  }
233
235
  unsubscribe(destination) {
234
- this.subscribehandler.delete(destination);
236
+ this._handlers.delete(destination);
235
237
  }
236
238
  sendError(ws, message) {
237
239
  const errorFrame = new StompFrame(StompCommand.ERROR, { 'content-type': 'text/plain' }, message);
@@ -239,25 +241,69 @@ class StompServer {
239
241
  }
240
242
  }
241
243
 
242
- const className = 'stompServer';
244
+ class StompAdapter extends AbstractWsAdapter {
245
+ server;
246
+ _options;
247
+ constructor(options) {
248
+ super(options.app);
249
+ this._options = options;
250
+ }
251
+ create(_port, _options) {
252
+ if (this.server)
253
+ return this.server;
254
+ const { path, auth } = this._options;
255
+ const server = new StompServer({ server: this.httpServer, path, auth });
256
+ this.server = server;
257
+ return server;
258
+ }
259
+ bindMessageHandlers(client, handlers, transform) {
260
+ fromEvent(client, 'message')
261
+ .pipe(mergeMap((data) => this.bindMessageHandler(data, handlers, transform)), filter((result) => result !== undefined))
262
+ .subscribe((response) => {
263
+ client.send(response.data);
264
+ });
265
+ }
266
+ bindMessageHandler(message, handlers, transform) {
267
+ const frame = StompFrame.parse(message.data);
268
+ if (frame.command === StompCommand.SEND) {
269
+ const { destination } = frame.headers;
270
+ const messageHandler = handlers.find((handler) => handler.message === destination);
271
+ if (!messageHandler)
272
+ return EMPTY;
273
+ return transform(messageHandler.callback(frame));
274
+ }
275
+ return EMPTY;
276
+ }
277
+ }
278
+
279
+ class Autowired {
280
+ constructor() { }
281
+ static container = {};
282
+ static register(name, instance) {
283
+ this.container[name] = instance;
284
+ }
285
+ static get(name) {
286
+ return this.container[name];
287
+ }
288
+ }
243
289
  class Stomp {
244
290
  constructor() { }
245
- static server(server, path, authProvider) {
246
- const stomp = StompServer.server({ server, path, authProvider });
247
- Autowired.register(className, stomp);
291
+ static server(server, path, auth) {
292
+ const stomp = new StompServer({ server, path, auth });
293
+ Autowired.register(StompServer.name, stomp);
248
294
  }
249
295
  static send(destination, body, headers = {}) {
250
- const server = Autowired.get(className);
296
+ const server = Autowired.get(StompServer.name);
251
297
  server?.send(destination, body, headers);
252
298
  }
253
299
  static subscribe(destination, callback) {
254
- const server = Autowired.get(className);
300
+ const server = Autowired.get(StompServer.name);
255
301
  server?.subscribe(destination, callback);
256
302
  }
257
303
  static unsubscribe(destination) {
258
- const server = Autowired.get(className);
304
+ const server = Autowired.get(StompServer.name);
259
305
  server?.unsubscribe(destination);
260
306
  }
261
307
  }
262
308
 
263
- export { Stomp as default };
309
+ export { SimpleAuthProvider, Stomp, StompAdapter, StompFrame, StompServer };
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "ws-stomp",
3
- "version": "1.1.5",
3
+ "version": "2.0.0",
4
4
  "description": "ws-stomp is a simple Node.js server base on websocket stomp",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
7
7
  "module": "dist/index.esm.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "deprecated": "",
10
- "files": ["dist"],
10
+ "files": [
11
+ "dist"
12
+ ],
11
13
  "exports": {
12
14
  ".": {
13
15
  "types": "./dist/index.d.ts",
@@ -19,17 +21,21 @@
19
21
  "scripts": {
20
22
  "build": "rollup -c rollup.config.js"
21
23
  },
24
+ "peerDependencies": {
25
+ "@nestjs/websockets": "^11.1.15"
26
+ },
22
27
  "dependencies": {
23
28
  "ws": "^8.19.0"
24
29
  },
25
30
  "devDependencies": {
26
- "@rollup/plugin-commonjs": "^29.0.0",
31
+ "@rollup/plugin-commonjs": "^29.0.1",
27
32
  "@rollup/plugin-json": "^6.1.0",
28
33
  "@rollup/plugin-node-resolve": "^16.0.3",
29
34
  "@rollup/plugin-replace": "^6.0.3",
30
- "@rollup/plugin-terser": "^0.4.4",
35
+ "@rollup/plugin-terser": "^1.0.0",
31
36
  "@rollup/plugin-typescript": "^12.3.0",
32
- "@types/node": "^24.10.15",
37
+ "@types/node": "^24.11.0",
38
+ "@types/ws": "^8.18.1",
33
39
  "rollup": "^4.59.0",
34
40
  "rollup-plugin-dts": "^6.3.0",
35
41
  "tslib": "^2.8.1",
@@ -39,7 +45,13 @@
39
45
  },
40
46
  "author": "mivui",
41
47
  "license": "MIT",
42
- "keywords": ["stomp", "ws", "node","websocket"],
48
+ "keywords": [
49
+ "stomp",
50
+ "ws",
51
+ "node",
52
+ "websocket",
53
+ "nest"
54
+ ],
43
55
  "repository": {
44
56
  "type": "git",
45
57
  "url": "git+https://github.com/mivui/node-ws-stomp.git"