@furystack/websocket-api 13.1.6 → 13.1.8

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,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [13.1.8] - 2026-02-11
4
+
5
+ ### ⬆️ Dependencies
6
+
7
+ - Bump `vitest` from `^4.0.17` to `^4.0.18`
8
+ - Updated `@furystack/rest-service` dependency
9
+ - Updated internal dependencies
10
+
11
+ ## [13.1.7] - 2026-02-09
12
+
13
+ ### ⬆️ Dependencies
14
+
15
+ - Updated `@furystack/core` dependency
16
+
17
+ ### 🧪 Tests
18
+
19
+ - Refactored WebSocket integration tests to use `usingAsync` for proper `Injector` disposal
20
+
3
21
  ## [13.1.6] - 2026-01-26
4
22
 
5
23
  ### 🔧 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 { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
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
- let i;
13
- let client;
14
- let port;
15
- const createdClients = [];
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: i,
18
+ injector,
21
19
  api: {},
22
20
  root: '',
23
21
  port,
24
22
  hostName: host,
25
23
  });
26
- addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
27
- useHttpAuthentication(i, {});
28
- await useWebsockets(i, { actions: [WhoAmI], path, port, host });
29
- await new Promise((resolve, reject) => {
30
- i.getInstance(ServerManager)
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
- client = new WebSocket(`ws://${host}:${port}/ws`);
34
- createdClients.push(client);
35
- client
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
- 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;
52
- await i[Symbol.asyncDispose]();
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
- expect((await getWhoAmIResult(client)).currentUser).toBe(null);
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
- const testUser = { username: 'test', password: 'test', roles: [] };
70
- const userStore = i.getInstance(StoreManager).getStoreFor(User, 'username');
71
- await userStore.add(testUser);
72
- const userCtx = i.getInstance(HttpUserContext);
73
- let cookie = '';
74
- await userCtx.cookieLogin(testUser, {
75
- setHeader: (_setCookie, cookieValue) => {
76
- cookie = cookieValue;
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
- createdClients.push(cl);
84
- cl.once('open', () => {
85
- done(cl);
86
- }).once('error', reject);
87
- });
88
- const whoAmIResult = await getWhoAmIResult(authenticatedClient);
89
- expect(whoAmIResult.currentUser).toEqual(testUser);
90
- await userStore.update(testUser.username, { ...testUser, roles: ['newFancyRole'] });
91
- const updatedWhoAmIResult = await getWhoAmIResult(authenticatedClient);
92
- expect(updatedWhoAmIResult.currentUser.roles).toEqual(['newFancyRole']);
93
- await userCtx.cookieLogout({
94
- headers: {
95
- cookie,
96
- },
97
- }, {
98
- setHeader: vi.fn(),
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":"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"}
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.6",
3
+ "version": "13.1.8",
4
4
  "description": "WebSocket API implementation for FuryStack",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -37,16 +37,16 @@
37
37
  },
38
38
  "homepage": "https://github.com/furystack/furystack",
39
39
  "dependencies": {
40
- "@furystack/core": "^15.0.34",
41
- "@furystack/inject": "^12.0.28",
42
- "@furystack/rest-service": "^11.0.2",
43
- "@furystack/utils": "^8.1.9",
40
+ "@furystack/core": "^15.0.36",
41
+ "@furystack/inject": "^12.0.30",
42
+ "@furystack/rest-service": "^11.0.4",
43
+ "@furystack/utils": "^8.1.10",
44
44
  "ws": "^8.19.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/ws": "^8.18.1",
48
48
  "typescript": "^5.9.3",
49
- "vitest": "^4.0.17"
49
+ "vitest": "^4.0.18"
50
50
  },
51
51
  "engines": {
52
52
  "node": ">=22.0.0"
@@ -8,7 +8,8 @@ import {
8
8
  useHttpAuthentication,
9
9
  useRestService,
10
10
  } from '@furystack/rest-service'
11
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
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
- let i!: Injector
20
- let client: WebSocket
21
- let port: number
22
- const createdClients: WebSocket[] = []
23
-
24
- beforeEach(async () => {
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: i,
27
+ injector,
29
28
  api: {},
30
29
  root: '',
31
30
  port,
32
31
  hostName: host,
33
32
  })
34
- addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
33
+ addStore(injector, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
35
34
  new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
36
35
  )
37
- useHttpAuthentication(i, {})
38
- await useWebsockets(i, { actions: [WhoAmI], path, port, host })
36
+ useHttpAuthentication(injector, {})
37
+ await useWebsockets(injector, { actions: [WhoAmI], path, port, host })
39
38
 
40
- await new Promise<void>((resolve, reject) => {
41
- i.getInstance(ServerManager)
39
+ const client = await new Promise<WebSocket>((resolve, reject) => {
40
+ injector
41
+ .getInstance(ServerManager)
42
42
  .getOrCreate({ port })
43
43
  .then(() => {
44
- client = new WebSocket(`ws://${host}:${port}/ws`)
45
- createdClients.push(client)
46
- client
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
- 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
64
- await i[Symbol.asyncDispose]()
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
- expect((await getWhoAmIResult(client)).currentUser).toBe(null)
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
- const testUser = { username: 'test', password: 'test', roles: [] } as User
87
+ await usingAsync(await setupWebSocket(), async ({ injector, createdClients, port }) => {
88
+ const testUser = { username: 'test', password: 'test', roles: [] } as User
84
89
 
85
- const userStore = i.getInstance(StoreManager).getStoreFor(User, 'username')
86
- await userStore.add(testUser)
90
+ const userStore = injector.getInstance(StoreManager).getStoreFor(User, 'username')
91
+ await userStore.add(testUser)
87
92
 
88
- const userCtx = i.getInstance(HttpUserContext)
93
+ const userCtx = injector.getInstance(HttpUserContext)
89
94
 
90
- let cookie = ''
91
- await userCtx.cookieLogin(testUser, {
92
- setHeader: (_setCookie, cookieValue) => {
93
- cookie = cookieValue
94
- },
95
- })
95
+ let cookie = ''
96
+ await userCtx.cookieLogin(testUser, {
97
+ setHeader: (_setCookie, cookieValue) => {
98
+ cookie = cookieValue
99
+ },
100
+ })
96
101
 
97
- const authenticatedClient = await new Promise<WebSocket>((done, reject) => {
98
- const cl = new WebSocket(`ws://${host}:${port}/ws`, {
99
- headers: { cookie },
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
- createdClients.push(cl)
102
- cl.once('open', () => {
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
- await userStore.update(testUser.username, { ...testUser, roles: ['newFancyRole'] })
114
+ await userStore.update(testUser.username, { ...testUser, roles: ['newFancyRole'] })
110
115
 
111
- const updatedWhoAmIResult = await getWhoAmIResult(authenticatedClient)
112
- expect(updatedWhoAmIResult.currentUser.roles).toEqual(['newFancyRole'])
116
+ const updatedWhoAmIResult = await getWhoAmIResult(authenticatedClient)
117
+ expect(updatedWhoAmIResult.currentUser.roles).toEqual(['newFancyRole'])
113
118
 
114
- await userCtx.cookieLogout(
115
- {
116
- headers: {
117
- cookie,
119
+ await userCtx.cookieLogout(
120
+ {
121
+ headers: {
122
+ cookie,
123
+ },
118
124
  },
119
- },
120
- {
121
- setHeader: vi.fn(),
122
- },
123
- )
125
+ {
126
+ setHeader: vi.fn(),
127
+ },
128
+ )
124
129
 
125
- const loggedOutWhoAmIResult = await getWhoAmIResult(authenticatedClient)
126
- expect(loggedOutWhoAmIResult.currentUser).toBe(null)
130
+ const loggedOutWhoAmIResult = await getWhoAmIResult(authenticatedClient)
131
+ expect(loggedOutWhoAmIResult.currentUser).toBe(null)
132
+ })
127
133
  })
128
134
  })