@furystack/websocket-api 13.1.6 → 13.1.7
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [13.1.7] - 2026-02-09
|
|
4
|
+
|
|
5
|
+
### ⬆️ Dependencies
|
|
6
|
+
|
|
7
|
+
- Updated `@furystack/core` dependency
|
|
8
|
+
|
|
9
|
+
### 🧪 Tests
|
|
10
|
+
|
|
11
|
+
- Refactored WebSocket integration tests to use `usingAsync` for proper `Injector` disposal
|
|
12
|
+
|
|
3
13
|
## [13.1.6] - 2026-01-26
|
|
4
14
|
|
|
5
15
|
### 🔧 Chores
|
|
@@ -2,55 +2,55 @@ import { addStore, InMemoryStore, StoreManager, User } from '@furystack/core';
|
|
|
2
2
|
import { getPort } from '@furystack/core/port-generator';
|
|
3
3
|
import { Injector } from '@furystack/inject';
|
|
4
4
|
import { DefaultSession, HttpUserContext, ServerManager, useHttpAuthentication, useRestService, } from '@furystack/rest-service';
|
|
5
|
-
import {
|
|
5
|
+
import { usingAsync } from '@furystack/utils';
|
|
6
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
6
7
|
import { WebSocket } from 'ws';
|
|
7
8
|
import { WhoAmI } from './actions/whoami.js';
|
|
8
9
|
import { useWebsockets } from './helpers.js';
|
|
9
10
|
describe('WebSocket Integration tests', () => {
|
|
10
11
|
const host = 'localhost';
|
|
11
12
|
const path = '/ws';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
beforeEach(async () => {
|
|
17
|
-
i = new Injector();
|
|
18
|
-
port = getPort();
|
|
13
|
+
const setupWebSocket = async () => {
|
|
14
|
+
const injector = new Injector();
|
|
15
|
+
const port = getPort();
|
|
16
|
+
const createdClients = [];
|
|
19
17
|
await useRestService({
|
|
20
|
-
injector
|
|
18
|
+
injector,
|
|
21
19
|
api: {},
|
|
22
20
|
root: '',
|
|
23
21
|
port,
|
|
24
22
|
hostName: host,
|
|
25
23
|
});
|
|
26
|
-
addStore(
|
|
27
|
-
useHttpAuthentication(
|
|
28
|
-
await useWebsockets(
|
|
29
|
-
await new Promise((resolve, reject) => {
|
|
30
|
-
|
|
24
|
+
addStore(injector, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
25
|
+
useHttpAuthentication(injector, {});
|
|
26
|
+
await useWebsockets(injector, { actions: [WhoAmI], path, port, host });
|
|
27
|
+
const client = await new Promise((resolve, reject) => {
|
|
28
|
+
injector
|
|
29
|
+
.getInstance(ServerManager)
|
|
31
30
|
.getOrCreate({ port })
|
|
32
31
|
.then(() => {
|
|
33
|
-
|
|
34
|
-
createdClients.push(
|
|
35
|
-
|
|
36
|
-
.on('open', () => {
|
|
37
|
-
resolve();
|
|
38
|
-
})
|
|
39
|
-
.on('error', reject);
|
|
32
|
+
const ws = new WebSocket(`ws://${host}:${port}/ws`);
|
|
33
|
+
createdClients.push(ws);
|
|
34
|
+
ws.on('open', () => resolve(ws)).on('error', reject);
|
|
40
35
|
})
|
|
41
36
|
.catch(reject);
|
|
42
37
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
return {
|
|
39
|
+
injector,
|
|
40
|
+
client,
|
|
41
|
+
createdClients,
|
|
42
|
+
port,
|
|
43
|
+
[Symbol.asyncDispose]: async () => {
|
|
44
|
+
createdClients.forEach((ws) => {
|
|
45
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
46
|
+
ws.close();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
createdClients.length = 0;
|
|
50
|
+
await injector[Symbol.asyncDispose]();
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
54
|
const getWhoAmIResult = async (subjectClient) => {
|
|
55
55
|
return new Promise((resolve, reject) => {
|
|
56
56
|
subjectClient.once('message', (data) => {
|
|
@@ -62,43 +62,47 @@ describe('WebSocket Integration tests', () => {
|
|
|
62
62
|
};
|
|
63
63
|
describe('Authentication', () => {
|
|
64
64
|
it('Should be unauthenticated by default', async () => {
|
|
65
|
-
|
|
65
|
+
await usingAsync(await setupWebSocket(), async ({ client }) => {
|
|
66
|
+
expect((await getWhoAmIResult(client)).currentUser).toBe(null);
|
|
67
|
+
});
|
|
66
68
|
});
|
|
67
69
|
});
|
|
68
70
|
it('Should be authenticated, roles should be updated and should be logged out', async () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const authenticatedClient = await new Promise((done, reject) => {
|
|
80
|
-
const cl = new WebSocket(`ws://${host}:${port}/ws`, {
|
|
81
|
-
headers: { cookie },
|
|
71
|
+
await usingAsync(await setupWebSocket(), async ({ injector, createdClients, port }) => {
|
|
72
|
+
const testUser = { username: 'test', password: 'test', roles: [] };
|
|
73
|
+
const userStore = injector.getInstance(StoreManager).getStoreFor(User, 'username');
|
|
74
|
+
await userStore.add(testUser);
|
|
75
|
+
const userCtx = injector.getInstance(HttpUserContext);
|
|
76
|
+
let cookie = '';
|
|
77
|
+
await userCtx.cookieLogin(testUser, {
|
|
78
|
+
setHeader: (_setCookie, cookieValue) => {
|
|
79
|
+
cookie = cookieValue;
|
|
80
|
+
},
|
|
82
81
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
82
|
+
const authenticatedClient = await new Promise((done, reject) => {
|
|
83
|
+
const cl = new WebSocket(`ws://${host}:${port}/ws`, {
|
|
84
|
+
headers: { cookie },
|
|
85
|
+
});
|
|
86
|
+
createdClients.push(cl);
|
|
87
|
+
cl.once('open', () => {
|
|
88
|
+
done(cl);
|
|
89
|
+
}).once('error', reject);
|
|
90
|
+
});
|
|
91
|
+
const whoAmIResult = await getWhoAmIResult(authenticatedClient);
|
|
92
|
+
expect(whoAmIResult.currentUser).toEqual(testUser);
|
|
93
|
+
await userStore.update(testUser.username, { ...testUser, roles: ['newFancyRole'] });
|
|
94
|
+
const updatedWhoAmIResult = await getWhoAmIResult(authenticatedClient);
|
|
95
|
+
expect(updatedWhoAmIResult.currentUser.roles).toEqual(['newFancyRole']);
|
|
96
|
+
await userCtx.cookieLogout({
|
|
97
|
+
headers: {
|
|
98
|
+
cookie,
|
|
99
|
+
},
|
|
100
|
+
}, {
|
|
101
|
+
setHeader: vi.fn(),
|
|
102
|
+
});
|
|
103
|
+
const loggedOutWhoAmIResult = await getWhoAmIResult(authenticatedClient);
|
|
104
|
+
expect(loggedOutWhoAmIResult.currentUser).toBe(null);
|
|
99
105
|
});
|
|
100
|
-
const loggedOutWhoAmIResult = await getWhoAmIResult(authenticatedClient);
|
|
101
|
-
expect(loggedOutWhoAmIResult.currentUser).toBe(null);
|
|
102
106
|
});
|
|
103
107
|
});
|
|
104
108
|
//# sourceMappingURL=websocket-integration.spec.js.map
|
|
@@ -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,
|
|
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.1.
|
|
3
|
+
"version": "13.1.7",
|
|
4
4
|
"description": "WebSocket API implementation for FuryStack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/furystack/furystack",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@furystack/core": "^15.0.
|
|
41
|
-
"@furystack/inject": "^12.0.
|
|
42
|
-
"@furystack/rest-service": "^11.0.
|
|
40
|
+
"@furystack/core": "^15.0.35",
|
|
41
|
+
"@furystack/inject": "^12.0.29",
|
|
42
|
+
"@furystack/rest-service": "^11.0.3",
|
|
43
43
|
"@furystack/utils": "^8.1.9",
|
|
44
44
|
"ws": "^8.19.0"
|
|
45
45
|
},
|
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
useHttpAuthentication,
|
|
9
9
|
useRestService,
|
|
10
10
|
} from '@furystack/rest-service'
|
|
11
|
-
import {
|
|
11
|
+
import { usingAsync } from '@furystack/utils'
|
|
12
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
12
13
|
import { WebSocket } from 'ws'
|
|
13
14
|
import { WhoAmI } from './actions/whoami.js'
|
|
14
15
|
import { useWebsockets } from './helpers.js'
|
|
@@ -16,53 +17,54 @@ import { useWebsockets } from './helpers.js'
|
|
|
16
17
|
describe('WebSocket Integration tests', () => {
|
|
17
18
|
const host = 'localhost'
|
|
18
19
|
const path = '/ws'
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
i = new Injector()
|
|
26
|
-
port = getPort()
|
|
20
|
+
|
|
21
|
+
const setupWebSocket = async () => {
|
|
22
|
+
const injector = new Injector()
|
|
23
|
+
const port = getPort()
|
|
24
|
+
const createdClients: WebSocket[] = []
|
|
25
|
+
|
|
27
26
|
await useRestService({
|
|
28
|
-
injector
|
|
27
|
+
injector,
|
|
29
28
|
api: {},
|
|
30
29
|
root: '',
|
|
31
30
|
port,
|
|
32
31
|
hostName: host,
|
|
33
32
|
})
|
|
34
|
-
addStore(
|
|
33
|
+
addStore(injector, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
|
|
35
34
|
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
36
35
|
)
|
|
37
|
-
useHttpAuthentication(
|
|
38
|
-
await useWebsockets(
|
|
36
|
+
useHttpAuthentication(injector, {})
|
|
37
|
+
await useWebsockets(injector, { actions: [WhoAmI], path, port, host })
|
|
39
38
|
|
|
40
|
-
await new Promise<
|
|
41
|
-
|
|
39
|
+
const client = await new Promise<WebSocket>((resolve, reject) => {
|
|
40
|
+
injector
|
|
41
|
+
.getInstance(ServerManager)
|
|
42
42
|
.getOrCreate({ port })
|
|
43
43
|
.then(() => {
|
|
44
|
-
|
|
45
|
-
createdClients.push(
|
|
46
|
-
|
|
47
|
-
.on('open', () => {
|
|
48
|
-
resolve()
|
|
49
|
-
})
|
|
50
|
-
.on('error', reject)
|
|
44
|
+
const ws = new WebSocket(`ws://${host}:${port}/ws`)
|
|
45
|
+
createdClients.push(ws)
|
|
46
|
+
ws.on('open', () => resolve(ws)).on('error', reject)
|
|
51
47
|
})
|
|
52
48
|
.catch(reject)
|
|
53
49
|
})
|
|
54
|
-
})
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
51
|
+
return {
|
|
52
|
+
injector,
|
|
53
|
+
client,
|
|
54
|
+
createdClients,
|
|
55
|
+
port,
|
|
56
|
+
[Symbol.asyncDispose]: async () => {
|
|
57
|
+
createdClients.forEach((ws) => {
|
|
58
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
59
|
+
ws.close()
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
createdClients.length = 0
|
|
63
|
+
await injector[Symbol.asyncDispose]()
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
66
68
|
const getWhoAmIResult = async (subjectClient: WebSocket) => {
|
|
67
69
|
return new Promise<{ currentUser: User }>((resolve, reject) => {
|
|
68
70
|
subjectClient.once('message', (data: Buffer) => {
|
|
@@ -75,54 +77,58 @@ describe('WebSocket Integration tests', () => {
|
|
|
75
77
|
|
|
76
78
|
describe('Authentication', () => {
|
|
77
79
|
it('Should be unauthenticated by default', async () => {
|
|
78
|
-
|
|
80
|
+
await usingAsync(await setupWebSocket(), async ({ client }) => {
|
|
81
|
+
expect((await getWhoAmIResult(client)).currentUser).toBe(null)
|
|
82
|
+
})
|
|
79
83
|
})
|
|
80
84
|
})
|
|
81
85
|
|
|
82
86
|
it('Should be authenticated, roles should be updated and should be logged out', async () => {
|
|
83
|
-
|
|
87
|
+
await usingAsync(await setupWebSocket(), async ({ injector, createdClients, port }) => {
|
|
88
|
+
const testUser = { username: 'test', password: 'test', roles: [] } as User
|
|
84
89
|
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
const userStore = injector.getInstance(StoreManager).getStoreFor(User, 'username')
|
|
91
|
+
await userStore.add(testUser)
|
|
87
92
|
|
|
88
|
-
|
|
93
|
+
const userCtx = injector.getInstance(HttpUserContext)
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
let cookie = ''
|
|
96
|
+
await userCtx.cookieLogin(testUser, {
|
|
97
|
+
setHeader: (_setCookie, cookieValue) => {
|
|
98
|
+
cookie = cookieValue
|
|
99
|
+
},
|
|
100
|
+
})
|
|
96
101
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
const authenticatedClient = await new Promise<WebSocket>((done, reject) => {
|
|
103
|
+
const cl = new WebSocket(`ws://${host}:${port}/ws`, {
|
|
104
|
+
headers: { cookie },
|
|
105
|
+
})
|
|
106
|
+
createdClients.push(cl)
|
|
107
|
+
cl.once('open', () => {
|
|
108
|
+
done(cl)
|
|
109
|
+
}).once('error', reject)
|
|
100
110
|
})
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
done(cl)
|
|
104
|
-
}).once('error', reject)
|
|
105
|
-
})
|
|
106
|
-
const whoAmIResult = await getWhoAmIResult(authenticatedClient)
|
|
107
|
-
expect(whoAmIResult.currentUser).toEqual(testUser)
|
|
111
|
+
const whoAmIResult = await getWhoAmIResult(authenticatedClient)
|
|
112
|
+
expect(whoAmIResult.currentUser).toEqual(testUser)
|
|
108
113
|
|
|
109
|
-
|
|
114
|
+
await userStore.update(testUser.username, { ...testUser, roles: ['newFancyRole'] })
|
|
110
115
|
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
const updatedWhoAmIResult = await getWhoAmIResult(authenticatedClient)
|
|
117
|
+
expect(updatedWhoAmIResult.currentUser.roles).toEqual(['newFancyRole'])
|
|
113
118
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
await userCtx.cookieLogout(
|
|
120
|
+
{
|
|
121
|
+
headers: {
|
|
122
|
+
cookie,
|
|
123
|
+
},
|
|
118
124
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
)
|
|
125
|
+
{
|
|
126
|
+
setHeader: vi.fn(),
|
|
127
|
+
},
|
|
128
|
+
)
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
const loggedOutWhoAmIResult = await getWhoAmIResult(authenticatedClient)
|
|
131
|
+
expect(loggedOutWhoAmIResult.currentUser).toBe(null)
|
|
132
|
+
})
|
|
127
133
|
})
|
|
128
134
|
})
|