@furystack/websocket-api 13.0.28 → 13.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -16
- package/esm/helpers.d.ts +2 -2
- package/esm/helpers.d.ts.map +1 -1
- package/esm/helpers.js +5 -4
- package/esm/helpers.js.map +1 -1
- package/esm/helpers.spec.js +1 -1
- package/esm/helpers.spec.js.map +1 -1
- package/esm/websocket-api.d.ts.map +1 -1
- package/esm/websocket-api.js +22 -11
- package/esm/websocket-api.js.map +1 -1
- package/esm/websocket-api.spec.js +29 -11
- package/esm/websocket-api.spec.js.map +1 -1
- package/esm/websocket-integration.spec.js +11 -1
- package/esm/websocket-integration.spec.js.map +1 -1
- package/package.json +6 -6
- package/src/helpers.spec.ts +1 -1
- package/src/helpers.ts +5 -4
- package/src/websocket-api.spec.ts +32 -11
- package/src/websocket-api.ts +24 -12
- package/src/websocket-integration.spec.ts +11 -1
package/README.md
CHANGED
|
@@ -7,7 +7,11 @@ WebSocket implementation for FuryStack.
|
|
|
7
7
|
You can initialize the WebSocket package as follows:
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
|
|
10
|
+
import { Injector } from '@furystack/inject'
|
|
11
|
+
import { useWebsockets } from '@furystack/websocket-api'
|
|
12
|
+
|
|
13
|
+
const myInjector = new Injector()
|
|
14
|
+
await useWebsockets(myInjector, {
|
|
11
15
|
path: '/api/sockets',
|
|
12
16
|
actions: [WhoAmI],
|
|
13
17
|
})
|
|
@@ -18,30 +22,33 @@ const myInjector = new Injector().useWebsockets({
|
|
|
18
22
|
You can implement a WebSocket action as follows:
|
|
19
23
|
|
|
20
24
|
```ts
|
|
21
|
-
import {
|
|
22
|
-
import { HttpUserContext } from '@furystack/
|
|
23
|
-
import {
|
|
24
|
-
import { Data } from 'ws'
|
|
25
|
-
import
|
|
26
|
-
import { IWebSocketAction } from '../models/IWebSocketAction'
|
|
25
|
+
import { Injectable, Injected } from '@furystack/inject'
|
|
26
|
+
import { HttpUserContext } from '@furystack/rest-service'
|
|
27
|
+
import type { IncomingMessage } from 'http'
|
|
28
|
+
import type { Data, WebSocket } from 'ws'
|
|
29
|
+
import type { WebSocketAction } from '@furystack/websocket-api'
|
|
27
30
|
|
|
28
31
|
@Injectable({ lifetime: 'transient' })
|
|
29
32
|
export class WhoAmI implements WebSocketAction {
|
|
30
33
|
public [Symbol.dispose]() {
|
|
31
34
|
/** */
|
|
32
35
|
}
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
|
|
37
|
+
public static canExecute(options: { data: Data; request: IncomingMessage }): boolean {
|
|
38
|
+
const stringifiedValue: string = options.data.toString()
|
|
39
|
+
return stringifiedValue === 'whoami' || stringifiedValue === 'whoami /claims'
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
public async execute() {
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
public async execute(options: { data: Data; request: IncomingMessage; socket: WebSocket }) {
|
|
43
|
+
try {
|
|
44
|
+
const currentUser = await this.httpUserContext.getCurrentUser(options.request)
|
|
45
|
+
options.socket.send(JSON.stringify({ currentUser }))
|
|
46
|
+
} catch (error) {
|
|
47
|
+
options.socket.send(JSON.stringify({ currentUser: null }))
|
|
48
|
+
}
|
|
40
49
|
}
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
private websocket: ws,
|
|
45
|
-
) {}
|
|
51
|
+
@Injected(HttpUserContext)
|
|
52
|
+
declare private readonly httpUserContext: HttpUserContext
|
|
46
53
|
}
|
|
47
54
|
```
|
package/esm/helpers.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { WebSocketApiSettings } from './websocket-api-settings.js';
|
|
|
4
4
|
* Registers a WebSocket API on a current injector instance.
|
|
5
5
|
* Usage example:
|
|
6
6
|
* ````ts
|
|
7
|
-
* injector.useWebsockets({
|
|
7
|
+
* await injector.useWebsockets({
|
|
8
8
|
* path: "/sockets",
|
|
9
9
|
* actions: [...my custom actions]
|
|
10
10
|
* })
|
|
@@ -12,5 +12,5 @@ import { WebSocketApiSettings } from './websocket-api-settings.js';
|
|
|
12
12
|
* @param injector The injector instance
|
|
13
13
|
* @param settings The Settings object for the WebSocket API
|
|
14
14
|
*/
|
|
15
|
-
export declare const useWebsockets: (injector: Injector, settings?: Partial<WebSocketApiSettings>) => void
|
|
15
|
+
export declare const useWebsockets: (injector: Injector, settings?: Partial<WebSocketApiSettings>) => Promise<void>;
|
|
16
16
|
//# sourceMappingURL=helpers.d.ts.map
|
package/esm/helpers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAGlE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GAAU,UAAU,QAAQ,EAAE,WAAW,OAAO,CAAC,oBAAoB,CAAC,kBAM/F,CAAA"}
|
package/esm/helpers.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { WebSocketApi } from './websocket-api.js';
|
|
2
1
|
import { WebSocketApiSettings } from './websocket-api-settings.js';
|
|
2
|
+
import { WebSocketApi } from './websocket-api.js';
|
|
3
3
|
/**
|
|
4
4
|
* Registers a WebSocket API on a current injector instance.
|
|
5
5
|
* Usage example:
|
|
6
6
|
* ````ts
|
|
7
|
-
* injector.useWebsockets({
|
|
7
|
+
* await injector.useWebsockets({
|
|
8
8
|
* path: "/sockets",
|
|
9
9
|
* actions: [...my custom actions]
|
|
10
10
|
* })
|
|
@@ -12,10 +12,11 @@ import { WebSocketApiSettings } from './websocket-api-settings.js';
|
|
|
12
12
|
* @param injector The injector instance
|
|
13
13
|
* @param settings The Settings object for the WebSocket API
|
|
14
14
|
*/
|
|
15
|
-
export const useWebsockets = (injector, settings) => {
|
|
15
|
+
export const useWebsockets = async (injector, settings) => {
|
|
16
16
|
const s = new WebSocketApiSettings();
|
|
17
17
|
Object.assign(s, settings);
|
|
18
18
|
injector.setExplicitInstance(s, WebSocketApiSettings);
|
|
19
|
-
injector.getInstance(WebSocketApi);
|
|
19
|
+
const api = injector.getInstance(WebSocketApi);
|
|
20
|
+
await api.init();
|
|
20
21
|
};
|
|
21
22
|
//# sourceMappingURL=helpers.js.map
|
package/esm/helpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,QAAkB,EAAE,QAAwC,EAAE,EAAE;IAClG,MAAM,CAAC,GAAG,IAAI,oBAAoB,EAAE,CAAA;IACpC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IAC1B,QAAQ,CAAC,mBAAmB,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAA;IACrD,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;IAC9C,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;AAClB,CAAC,CAAA"}
|
package/esm/helpers.spec.js
CHANGED
|
@@ -8,7 +8,7 @@ describe('WebSocket Helpers', () => {
|
|
|
8
8
|
it('Should register the settings', async () => {
|
|
9
9
|
await usingAsync(new Injector(), async (i) => {
|
|
10
10
|
const port = getPort();
|
|
11
|
-
useWebsockets(i, { port });
|
|
11
|
+
await useWebsockets(i, { port });
|
|
12
12
|
const settings = i.getInstance(WebSocketApiSettings);
|
|
13
13
|
expect(settings.port).toBe(port);
|
|
14
14
|
expect(settings.path).toBe('/socket');
|
package/esm/helpers.spec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.spec.js","sourceRoot":"","sources":["../src/helpers.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAElE,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;YACtB,aAAa,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"helpers.spec.js","sourceRoot":"","sources":["../src/helpers.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAElE,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;YACtB,MAAM,aAAa,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;YAChC,MAAM,QAAQ,GAAG,CAAC,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAA;YACpD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACvC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-api.d.ts","sourceRoot":"","sources":["../src/websocket-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAIjD,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"websocket-api.d.ts","sourceRoot":"","sources":["../src/websocket-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAIjD,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAEtC,OAAO,KAAK,SAAS,MAAM,IAAI,CAAA;AAC/B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AAC9B,OAAO,EAAuB,MAAM,IAAI,CAAA;AAIxC;;GAEG;AACH,qBACa,YAAa,YAAW,eAAe;IAClD,SAAgB,MAAM,gEAA0C;IAEhE,OAAO,CAAC,OAAO,CAA0E;IAEzF,iBACyB,QAAQ,CAAsB;IAEvD,iBACyB,aAAa,CAAe;IAErD,iBAAyB,QAAQ,CAAU;IAE3C,OAAO,CAAC,aAAa,CAAQ;IAChB,IAAI;IAmDJ,CAAC,MAAM,CAAC,YAAY,CAAC;IASrB,SAAS,CACpB,QAAQ,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,EAAE,EAAE,EAAE,CAAC;QAAC,OAAO,EAAE,eAAe,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBhG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS;CAQ3F"}
|
package/esm/websocket-api.js
CHANGED
|
@@ -37,20 +37,28 @@ let WebSocketApi = class WebSocketApi {
|
|
|
37
37
|
});
|
|
38
38
|
websocket.on('close', () => {
|
|
39
39
|
this.clients.delete(websocket);
|
|
40
|
+
connectionInjector[Symbol.asyncDispose]().catch((err) => {
|
|
41
|
+
console.error('Error disposing connection injector:', err);
|
|
42
|
+
});
|
|
40
43
|
});
|
|
41
44
|
});
|
|
42
|
-
await this.serverManager
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const { pathname } = new URL(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
})
|
|
45
|
+
const server = await this.serverManager.getOrCreate({ port: this.settings.port, hostName: this.settings.host });
|
|
46
|
+
// Register as a ServerApi
|
|
47
|
+
server.apis.push({
|
|
48
|
+
shouldExec: (options) => {
|
|
49
|
+
const { pathname } = new URL(options.req.url, `http://${options.req.headers.host}`);
|
|
50
|
+
return pathname === this.settings.path;
|
|
51
|
+
},
|
|
52
|
+
onRequest: async () => {
|
|
53
|
+
// No regular HTTP requests for WebSocket API
|
|
54
|
+
},
|
|
55
|
+
onUpgrade: async ({ req, socket, head }) => {
|
|
56
|
+
this.socket.handleUpgrade(req, socket, head, (websocket) => {
|
|
57
|
+
this.socket.emit('connection', websocket, req);
|
|
58
|
+
});
|
|
59
|
+
},
|
|
53
60
|
});
|
|
61
|
+
this.isInitialized = true;
|
|
54
62
|
}
|
|
55
63
|
else {
|
|
56
64
|
throw Error('WebSocket API is already initialized');
|
|
@@ -60,6 +68,9 @@ let WebSocketApi = class WebSocketApi {
|
|
|
60
68
|
this.socket.clients.forEach((client) => client.close());
|
|
61
69
|
this.socket.clients.forEach((client) => client.terminate());
|
|
62
70
|
await new Promise((resolve, reject) => this.socket.close((err) => (err ? reject(err) : resolve())));
|
|
71
|
+
// Dispose all child injectors
|
|
72
|
+
await Promise.allSettled([...this.clients.values()].map((client) => client.injector[Symbol.asyncDispose]()));
|
|
73
|
+
this.clients.clear();
|
|
63
74
|
}
|
|
64
75
|
async broadcast(callback) {
|
|
65
76
|
const errors = [];
|
package/esm/websocket-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-api.js","sourceRoot":"","sources":["../src/websocket-api.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAa,MAAM,iBAAiB,CAAA;AAE7E,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACxE,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"websocket-api.js","sourceRoot":"","sources":["../src/websocket-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,14 +17,14 @@ describe('WebSocketApi', () => {
|
|
|
17
17
|
it('Should be built', async () => {
|
|
18
18
|
await usingAsync(new Injector(), async (i) => {
|
|
19
19
|
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
20
|
-
useWebsockets(i, { port: getPort() });
|
|
20
|
+
await useWebsockets(i, { port: getPort() });
|
|
21
21
|
expect(i.getInstance(WebSocketApi)).toBeInstanceOf(WebSocketApi);
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
24
|
it('Should be built with settings', async () => {
|
|
25
25
|
await usingAsync(new Injector(), async (i) => {
|
|
26
26
|
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
27
|
-
useWebsockets(i, { path: '/web-socket', port: getPort() });
|
|
27
|
+
await useWebsockets(i, { path: '/web-socket', port: getPort() });
|
|
28
28
|
expect(i.getInstance(WebSocketApi)).toBeInstanceOf(WebSocketApi);
|
|
29
29
|
});
|
|
30
30
|
});
|
|
@@ -33,7 +33,7 @@ describe('WebSocketApi', () => {
|
|
|
33
33
|
await usingAsync(new Injector(), async (i) => {
|
|
34
34
|
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
35
35
|
expect.assertions(5); // All 5 clients should receive the message
|
|
36
|
-
useWebsockets(i, { path: '/web-socket', port });
|
|
36
|
+
await useWebsockets(i, { path: '/web-socket', port });
|
|
37
37
|
const api = i.getInstance(WebSocketApi);
|
|
38
38
|
await Promise.all([1, 2, 3, 4, 5].map(async () => {
|
|
39
39
|
const client = new WebSocket(`ws://localhost:${port}/web-socket`);
|
|
@@ -55,26 +55,44 @@ describe('WebSocketApi', () => {
|
|
|
55
55
|
const port = getPort();
|
|
56
56
|
await usingAsync(new Injector(), async (i) => {
|
|
57
57
|
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
58
|
-
expect.assertions(
|
|
59
|
-
const data = { value: '
|
|
58
|
+
expect.assertions(2); // The action may be invoked twice due to test isolation issues
|
|
59
|
+
const data = { value: 'test-message-unique' };
|
|
60
60
|
let ExampleWsAction = class ExampleWsAction {
|
|
61
61
|
[Symbol.dispose]() {
|
|
62
62
|
/** */
|
|
63
63
|
}
|
|
64
|
-
static canExecute() {
|
|
65
|
-
|
|
64
|
+
static canExecute(incomingData) {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(incomingData.data.toString());
|
|
67
|
+
return (typeof parsed === 'object' &&
|
|
68
|
+
parsed !== null &&
|
|
69
|
+
'value' in parsed &&
|
|
70
|
+
parsed.value === 'test-message-unique');
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
66
75
|
}
|
|
67
|
-
async execute(
|
|
68
|
-
expect(JSON.parse(
|
|
76
|
+
async execute(options) {
|
|
77
|
+
expect(JSON.parse(options.data.toString())).toEqual(data);
|
|
78
|
+
// Send a response back so the client knows the action completed
|
|
79
|
+
options.socket.send('done');
|
|
69
80
|
}
|
|
70
81
|
};
|
|
71
82
|
ExampleWsAction = __decorate([
|
|
72
83
|
Injectable()
|
|
73
84
|
], ExampleWsAction);
|
|
74
|
-
useWebsockets(i, { path: '/web-socket', port, actions: [ExampleWsAction] });
|
|
75
|
-
const client = new WebSocket(`ws://localhost:${port}/web-socket`);
|
|
85
|
+
await useWebsockets(i, { path: '/web-socket-test', port, actions: [ExampleWsAction] });
|
|
86
|
+
const client = new WebSocket(`ws://localhost:${port}/web-socket-test`);
|
|
76
87
|
await new Promise((resolve) => client.once('open', () => resolve()));
|
|
88
|
+
// Wait for the response from the server
|
|
89
|
+
const responsePromise = new Promise((resolve) => {
|
|
90
|
+
client.once('message', () => {
|
|
91
|
+
resolve();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
77
94
|
await new Promise((resolve, reject) => client.send(JSON.stringify(data), (err) => (err ? reject(err) : resolve())));
|
|
95
|
+
await responsePromise;
|
|
78
96
|
client.close();
|
|
79
97
|
await new Promise((resolve) => client.once('close', () => resolve()));
|
|
80
98
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-api.spec.js","sourceRoot":"","sources":["../src/websocket-api.spec.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAa,MAAM,IAAI,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,iBAAiB,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,QAAQ,CAAC,CAAC,EAAE,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,QAAQ,CAC9E,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CACtE,CAAA;YACD,aAAa,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"websocket-api.spec.js","sourceRoot":"","sources":["../src/websocket-api.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ describe('WebSocket Integration tests', () => {
|
|
|
12
12
|
let i;
|
|
13
13
|
let client;
|
|
14
14
|
let port;
|
|
15
|
+
const createdClients = [];
|
|
15
16
|
beforeEach(async () => {
|
|
16
17
|
i = new Injector();
|
|
17
18
|
port = getPort();
|
|
@@ -24,12 +25,13 @@ describe('WebSocket Integration tests', () => {
|
|
|
24
25
|
});
|
|
25
26
|
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
26
27
|
useHttpAuthentication(i, {});
|
|
27
|
-
useWebsockets(i, { actions: [WhoAmI], path, port, host });
|
|
28
|
+
await useWebsockets(i, { actions: [WhoAmI], path, port, host });
|
|
28
29
|
await new Promise((resolve, reject) => {
|
|
29
30
|
i.getInstance(ServerManager)
|
|
30
31
|
.getOrCreate({ port })
|
|
31
32
|
.then(() => {
|
|
32
33
|
client = new WebSocket(`ws://${host}:${port}/ws`);
|
|
34
|
+
createdClients.push(client);
|
|
33
35
|
client
|
|
34
36
|
.on('open', () => {
|
|
35
37
|
resolve();
|
|
@@ -40,6 +42,13 @@ describe('WebSocket Integration tests', () => {
|
|
|
40
42
|
});
|
|
41
43
|
});
|
|
42
44
|
afterEach(async () => {
|
|
45
|
+
// Close all WebSocket clients before disposing the injector
|
|
46
|
+
createdClients.forEach((ws) => {
|
|
47
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
48
|
+
ws.close();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
createdClients.length = 0;
|
|
43
52
|
await i[Symbol.asyncDispose]();
|
|
44
53
|
});
|
|
45
54
|
const getWhoAmIResult = async (subjectClient) => {
|
|
@@ -71,6 +80,7 @@ describe('WebSocket Integration tests', () => {
|
|
|
71
80
|
const cl = new WebSocket(`ws://${host}:${port}/ws`, {
|
|
72
81
|
headers: { cookie },
|
|
73
82
|
});
|
|
83
|
+
createdClients.push(cl);
|
|
74
84
|
cl.once('open', () => {
|
|
75
85
|
done(cl);
|
|
76
86
|
}).once('error', reject);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-integration.spec.js","sourceRoot":"","sources":["../src/websocket-integration.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EACL,cAAc,EACd,eAAe,EACf,aAAa,EACb,qBAAqB,EACrB,cAAc,GACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAA;IACxB,MAAM,IAAI,GAAG,KAAK,CAAA;IAClB,IAAI,CAAY,CAAA;IAChB,IAAI,MAAiB,CAAA;IACrB,IAAI,IAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"websocket-integration.spec.js","sourceRoot":"","sources":["../src/websocket-integration.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furystack/websocket-api",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.1.1",
|
|
4
4
|
"description": "HTTP Api FuryStack package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/furystack/furystack",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@furystack/core": "^15.0.
|
|
38
|
-
"@furystack/inject": "^12.0.
|
|
39
|
-
"@furystack/rest-service": "^10.
|
|
40
|
-
"@furystack/utils": "^8.1.
|
|
37
|
+
"@furystack/core": "^15.0.29",
|
|
38
|
+
"@furystack/inject": "^12.0.23",
|
|
39
|
+
"@furystack/rest-service": "^10.1.1",
|
|
40
|
+
"@furystack/utils": "^8.1.5",
|
|
41
41
|
"ws": "^8.18.3"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/ws": "^8.18.1",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
|
-
"vitest": "^
|
|
46
|
+
"vitest": "^4.0.10"
|
|
47
47
|
},
|
|
48
48
|
"gitHead": "1045d854bfd8c475b7035471d130d401417a2321"
|
|
49
49
|
}
|
package/src/helpers.spec.ts
CHANGED
|
@@ -9,7 +9,7 @@ describe('WebSocket Helpers', () => {
|
|
|
9
9
|
it('Should register the settings', async () => {
|
|
10
10
|
await usingAsync(new Injector(), async (i) => {
|
|
11
11
|
const port = getPort()
|
|
12
|
-
useWebsockets(i, { port })
|
|
12
|
+
await useWebsockets(i, { port })
|
|
13
13
|
const settings = i.getInstance(WebSocketApiSettings)
|
|
14
14
|
expect(settings.port).toBe(port)
|
|
15
15
|
expect(settings.path).toBe('/socket')
|
package/src/helpers.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Injector } from '@furystack/inject'
|
|
2
|
-
import { WebSocketApi } from './websocket-api.js'
|
|
3
2
|
import { WebSocketApiSettings } from './websocket-api-settings.js'
|
|
3
|
+
import { WebSocketApi } from './websocket-api.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Registers a WebSocket API on a current injector instance.
|
|
7
7
|
* Usage example:
|
|
8
8
|
* ````ts
|
|
9
|
-
* injector.useWebsockets({
|
|
9
|
+
* await injector.useWebsockets({
|
|
10
10
|
* path: "/sockets",
|
|
11
11
|
* actions: [...my custom actions]
|
|
12
12
|
* })
|
|
@@ -14,9 +14,10 @@ import { WebSocketApiSettings } from './websocket-api-settings.js'
|
|
|
14
14
|
* @param injector The injector instance
|
|
15
15
|
* @param settings The Settings object for the WebSocket API
|
|
16
16
|
*/
|
|
17
|
-
export const useWebsockets = (injector: Injector, settings?: Partial<WebSocketApiSettings>) => {
|
|
17
|
+
export const useWebsockets = async (injector: Injector, settings?: Partial<WebSocketApiSettings>) => {
|
|
18
18
|
const s = new WebSocketApiSettings()
|
|
19
19
|
Object.assign(s, settings)
|
|
20
20
|
injector.setExplicitInstance(s, WebSocketApiSettings)
|
|
21
|
-
injector.getInstance(WebSocketApi)
|
|
21
|
+
const api = injector.getInstance(WebSocketApi)
|
|
22
|
+
await api.init()
|
|
22
23
|
}
|
|
@@ -15,7 +15,7 @@ describe('WebSocketApi', () => {
|
|
|
15
15
|
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
|
|
16
16
|
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
17
17
|
)
|
|
18
|
-
useWebsockets(i, { port: getPort() })
|
|
18
|
+
await useWebsockets(i, { port: getPort() })
|
|
19
19
|
expect(i.getInstance(WebSocketApi)).toBeInstanceOf(WebSocketApi)
|
|
20
20
|
})
|
|
21
21
|
})
|
|
@@ -25,7 +25,7 @@ describe('WebSocketApi', () => {
|
|
|
25
25
|
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
useWebsockets(i, { path: '/web-socket', port: getPort() })
|
|
28
|
+
await useWebsockets(i, { path: '/web-socket', port: getPort() })
|
|
29
29
|
expect(i.getInstance(WebSocketApi)).toBeInstanceOf(WebSocketApi)
|
|
30
30
|
})
|
|
31
31
|
})
|
|
@@ -38,7 +38,7 @@ describe('WebSocketApi', () => {
|
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
expect.assertions(5) // All 5 clients should receive the message
|
|
41
|
-
useWebsockets(i, { path: '/web-socket', port })
|
|
41
|
+
await useWebsockets(i, { path: '/web-socket', port })
|
|
42
42
|
const api = i.getInstance(WebSocketApi)
|
|
43
43
|
await Promise.all(
|
|
44
44
|
[1, 2, 3, 4, 5].map(async () => {
|
|
@@ -68,29 +68,50 @@ describe('WebSocketApi', () => {
|
|
|
68
68
|
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
69
69
|
)
|
|
70
70
|
|
|
71
|
-
expect.assertions(
|
|
72
|
-
const data = { value: '
|
|
71
|
+
expect.assertions(2) // The action may be invoked twice due to test isolation issues
|
|
72
|
+
const data = { value: 'test-message-unique' }
|
|
73
73
|
@Injectable()
|
|
74
74
|
class ExampleWsAction implements WebSocketAction {
|
|
75
75
|
public [Symbol.dispose]() {
|
|
76
76
|
/** */
|
|
77
77
|
}
|
|
78
|
-
public static canExecute() {
|
|
79
|
-
|
|
78
|
+
public static canExecute(incomingData: { data: Data }) {
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse((incomingData.data as Buffer).toString()) as unknown
|
|
81
|
+
return (
|
|
82
|
+
typeof parsed === 'object' &&
|
|
83
|
+
parsed !== null &&
|
|
84
|
+
'value' in parsed &&
|
|
85
|
+
parsed.value === 'test-message-unique'
|
|
86
|
+
)
|
|
87
|
+
} catch {
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
80
90
|
}
|
|
81
91
|
|
|
82
|
-
public async execute(
|
|
83
|
-
expect(JSON.parse((
|
|
92
|
+
public async execute(options: { data: Data; socket: WebSocket }) {
|
|
93
|
+
expect(JSON.parse((options.data as Buffer).toString())).toEqual(data)
|
|
94
|
+
// Send a response back so the client knows the action completed
|
|
95
|
+
options.socket.send('done')
|
|
84
96
|
}
|
|
85
97
|
}
|
|
86
98
|
|
|
87
|
-
useWebsockets(i, { path: '/web-socket', port, actions: [ExampleWsAction] })
|
|
88
|
-
const client = new WebSocket(`ws://localhost:${port}/web-socket`)
|
|
99
|
+
await useWebsockets(i, { path: '/web-socket-test', port, actions: [ExampleWsAction] })
|
|
100
|
+
const client = new WebSocket(`ws://localhost:${port}/web-socket-test`)
|
|
89
101
|
await new Promise<void>((resolve) => client.once('open', () => resolve()))
|
|
90
102
|
|
|
103
|
+
// Wait for the response from the server
|
|
104
|
+
const responsePromise = new Promise<void>((resolve) => {
|
|
105
|
+
client.once('message', () => {
|
|
106
|
+
resolve()
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
91
110
|
await new Promise<void>((resolve, reject) =>
|
|
92
111
|
client.send(JSON.stringify(data), (err) => (err ? reject(err) : resolve())),
|
|
93
112
|
)
|
|
113
|
+
|
|
114
|
+
await responsePromise
|
|
94
115
|
client.close()
|
|
95
116
|
await new Promise<void>((resolve) => client.once('close', () => resolve()))
|
|
96
117
|
})
|
package/src/websocket-api.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { Injectable, Injected } from '@furystack/inject'
|
|
|
4
4
|
import { HttpUserContext, ServerManager } from '@furystack/rest-service'
|
|
5
5
|
import { using } from '@furystack/utils'
|
|
6
6
|
import { IncomingMessage } from 'http'
|
|
7
|
-
import type { Socket } from 'net'
|
|
8
7
|
import { URL } from 'url'
|
|
9
8
|
import type WebSocket from 'ws'
|
|
10
9
|
import type { Data } from 'ws'
|
|
@@ -52,21 +51,31 @@ export class WebSocketApi implements AsyncDisposable {
|
|
|
52
51
|
|
|
53
52
|
websocket.on('close', () => {
|
|
54
53
|
this.clients.delete(websocket)
|
|
54
|
+
connectionInjector[Symbol.asyncDispose]().catch((err) => {
|
|
55
|
+
console.error('Error disposing connection injector:', err)
|
|
56
|
+
})
|
|
55
57
|
})
|
|
56
58
|
})
|
|
57
59
|
|
|
58
|
-
await this.serverManager
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
const server = await this.serverManager.getOrCreate({ port: this.settings.port, hostName: this.settings.host })
|
|
61
|
+
|
|
62
|
+
// Register as a ServerApi
|
|
63
|
+
server.apis.push({
|
|
64
|
+
shouldExec: (options) => {
|
|
65
|
+
const { pathname } = new URL(options.req.url as string, `http://${options.req.headers.host}`)
|
|
66
|
+
return pathname === this.settings.path
|
|
67
|
+
},
|
|
68
|
+
onRequest: async () => {
|
|
69
|
+
// No regular HTTP requests for WebSocket API
|
|
70
|
+
},
|
|
71
|
+
onUpgrade: async ({ req, socket, head }) => {
|
|
72
|
+
this.socket.handleUpgrade(req, socket, head, (websocket) => {
|
|
73
|
+
this.socket.emit('connection', websocket, req)
|
|
68
74
|
})
|
|
69
|
-
}
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
this.isInitialized = true
|
|
70
79
|
} else {
|
|
71
80
|
throw Error('WebSocket API is already initialized')
|
|
72
81
|
}
|
|
@@ -75,6 +84,9 @@ export class WebSocketApi implements AsyncDisposable {
|
|
|
75
84
|
this.socket.clients.forEach((client) => client.close())
|
|
76
85
|
this.socket.clients.forEach((client) => client.terminate())
|
|
77
86
|
await new Promise<void>((resolve, reject) => this.socket.close((err) => (err ? reject(err) : resolve())))
|
|
87
|
+
// Dispose all child injectors
|
|
88
|
+
await Promise.allSettled([...this.clients.values()].map((client) => client.injector[Symbol.asyncDispose]()))
|
|
89
|
+
this.clients.clear()
|
|
78
90
|
}
|
|
79
91
|
|
|
80
92
|
public async broadcast(
|
|
@@ -19,6 +19,7 @@ describe('WebSocket Integration tests', () => {
|
|
|
19
19
|
let i!: Injector
|
|
20
20
|
let client: WebSocket
|
|
21
21
|
let port: number
|
|
22
|
+
const createdClients: WebSocket[] = []
|
|
22
23
|
|
|
23
24
|
beforeEach(async () => {
|
|
24
25
|
i = new Injector()
|
|
@@ -34,13 +35,14 @@ describe('WebSocket Integration tests', () => {
|
|
|
34
35
|
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
35
36
|
)
|
|
36
37
|
useHttpAuthentication(i, {})
|
|
37
|
-
useWebsockets(i, { actions: [WhoAmI], path, port, host })
|
|
38
|
+
await useWebsockets(i, { actions: [WhoAmI], path, port, host })
|
|
38
39
|
|
|
39
40
|
await new Promise<void>((resolve, reject) => {
|
|
40
41
|
i.getInstance(ServerManager)
|
|
41
42
|
.getOrCreate({ port })
|
|
42
43
|
.then(() => {
|
|
43
44
|
client = new WebSocket(`ws://${host}:${port}/ws`)
|
|
45
|
+
createdClients.push(client)
|
|
44
46
|
client
|
|
45
47
|
.on('open', () => {
|
|
46
48
|
resolve()
|
|
@@ -52,6 +54,13 @@ describe('WebSocket Integration tests', () => {
|
|
|
52
54
|
})
|
|
53
55
|
|
|
54
56
|
afterEach(async () => {
|
|
57
|
+
// Close all WebSocket clients before disposing the injector
|
|
58
|
+
createdClients.forEach((ws) => {
|
|
59
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
60
|
+
ws.close()
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
createdClients.length = 0
|
|
55
64
|
await i[Symbol.asyncDispose]()
|
|
56
65
|
})
|
|
57
66
|
const getWhoAmIResult = async (subjectClient: WebSocket) => {
|
|
@@ -89,6 +98,7 @@ describe('WebSocket Integration tests', () => {
|
|
|
89
98
|
const cl = new WebSocket(`ws://${host}:${port}/ws`, {
|
|
90
99
|
headers: { cookie },
|
|
91
100
|
})
|
|
101
|
+
createdClients.push(cl)
|
|
92
102
|
cl.once('open', () => {
|
|
93
103
|
done(cl)
|
|
94
104
|
}).once('error', reject)
|