@replit/river 0.9.2 → 0.10.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.
Files changed (54) hide show
  1. package/dist/__tests__/bandwidth.bench.js +11 -11
  2. package/dist/__tests__/cleanup.test.d.ts +2 -0
  3. package/dist/__tests__/cleanup.test.d.ts.map +1 -0
  4. package/dist/__tests__/{invariants.test.js → cleanup.test.js} +47 -20
  5. package/dist/__tests__/disconnects.test.d.ts +2 -0
  6. package/dist/__tests__/disconnects.test.d.ts.map +1 -0
  7. package/dist/__tests__/disconnects.test.js +163 -0
  8. package/dist/__tests__/e2e.test.js +33 -32
  9. package/dist/__tests__/fixtures/cleanup.d.ts +2 -2
  10. package/dist/__tests__/fixtures/cleanup.d.ts.map +1 -1
  11. package/dist/__tests__/fixtures/cleanup.js +6 -9
  12. package/dist/__tests__/fixtures/services.d.ts +36 -36
  13. package/dist/__tests__/fixtures/services.d.ts.map +1 -1
  14. package/dist/__tests__/fixtures/services.js +36 -53
  15. package/dist/__tests__/handler.test.js +6 -7
  16. package/dist/__tests__/typescript-stress.test.d.ts +149 -149
  17. package/dist/__tests__/typescript-stress.test.d.ts.map +1 -1
  18. package/dist/__tests__/typescript-stress.test.js +14 -14
  19. package/dist/router/builder.d.ts +6 -7
  20. package/dist/router/builder.d.ts.map +1 -1
  21. package/dist/router/client.d.ts +7 -3
  22. package/dist/router/client.d.ts.map +1 -1
  23. package/dist/router/client.js +204 -106
  24. package/dist/router/defs.d.ts +16 -0
  25. package/dist/router/defs.d.ts.map +1 -0
  26. package/dist/router/defs.js +11 -0
  27. package/dist/router/index.d.ts +2 -0
  28. package/dist/router/index.d.ts.map +1 -1
  29. package/dist/router/index.js +1 -0
  30. package/dist/router/result.d.ts +2 -1
  31. package/dist/router/result.d.ts.map +1 -1
  32. package/dist/router/result.js +5 -1
  33. package/dist/router/server.d.ts +5 -5
  34. package/dist/router/server.d.ts.map +1 -1
  35. package/dist/router/server.js +125 -82
  36. package/dist/transport/impls/stdio/stdio.test.js +1 -2
  37. package/dist/transport/impls/ws/client.d.ts +1 -4
  38. package/dist/transport/impls/ws/client.d.ts.map +1 -1
  39. package/dist/transport/impls/ws/client.js +5 -6
  40. package/dist/transport/impls/ws/server.d.ts +3 -0
  41. package/dist/transport/impls/ws/server.d.ts.map +1 -1
  42. package/dist/transport/impls/ws/server.js +28 -23
  43. package/dist/transport/impls/ws/ws.test.js +84 -16
  44. package/dist/transport/index.d.ts +0 -9
  45. package/dist/transport/index.d.ts.map +1 -1
  46. package/dist/transport/index.js +0 -21
  47. package/dist/transport/message.d.ts +3 -4
  48. package/dist/transport/message.d.ts.map +1 -1
  49. package/dist/util/testHelpers.d.ts +20 -97
  50. package/dist/util/testHelpers.d.ts.map +1 -1
  51. package/dist/util/testHelpers.js +94 -249
  52. package/package.json +14 -13
  53. package/dist/__tests__/invariants.test.d.ts +0 -2
  54. package/dist/__tests__/invariants.test.d.ts.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import http from 'http';
2
2
  import { assert, bench, describe } from 'vitest';
3
- import { createWebSocketServer, createWsTransports, onServerReady, } from '../util/testHelpers';
3
+ import { createWebSocketServer, createWsTransports, onServerReady, waitForMessage, } from '../util/testHelpers';
4
4
  import largePayload from './fixtures/largePayload.json';
5
5
  import { TestServiceConstructor } from './fixtures/services';
6
6
  import { createServer } from '../router/server';
7
7
  import { createClient } from '../router/client';
8
8
  import { StupidlyLargeService } from './typescript-stress.test';
9
- import { waitForMessage } from '../transport';
9
+ import { buildServiceDefs } from '../router/defs';
10
10
  let smallId = 0;
11
11
  let largeId = 0;
12
12
  const dummyPayloadSmall = () => ({
@@ -53,8 +53,8 @@ describe('simple router level bandwidth', async () => {
53
53
  const port = await onServerReady(httpServer);
54
54
  const webSocketServer = await createWebSocketServer(httpServer);
55
55
  const [clientTransport, serverTransport] = createWsTransports(port, webSocketServer);
56
- const serviceDefs = { test: TestServiceConstructor() };
57
- const server = await createServer(serverTransport, serviceDefs);
56
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
57
+ const server = createServer(serverTransport, serviceDefs);
58
58
  const client = createClient(clientTransport);
59
59
  bench('rpc (wait for response)', async () => {
60
60
  const result = await client.test.add.rpc({ n: 1 });
@@ -75,13 +75,13 @@ describe('complex (50 procedures) router level bandwidth', async () => {
75
75
  const port = await onServerReady(httpServer);
76
76
  const webSocketServer = await createWebSocketServer(httpServer);
77
77
  const [clientTransport, serverTransport] = createWsTransports(port, webSocketServer);
78
- const serviceDefs = {
79
- a: StupidlyLargeService(),
80
- b: StupidlyLargeService(),
81
- c: StupidlyLargeService(),
82
- d: StupidlyLargeService(),
83
- };
84
- const server = await createServer(serverTransport, serviceDefs);
78
+ const serviceDefs = buildServiceDefs([
79
+ StupidlyLargeService('a'),
80
+ StupidlyLargeService('b'),
81
+ StupidlyLargeService('c'),
82
+ StupidlyLargeService('d'),
83
+ ]);
84
+ const server = createServer(serverTransport, serviceDefs);
85
85
  const client = createClient(clientTransport);
86
86
  bench('rpc (wait for response)', async () => {
87
87
  const result = await client.b.f35.rpc({ a: 1 });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cleanup.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.test.d.ts","sourceRoot":"","sources":["../../__tests__/cleanup.test.ts"],"names":[],"mappings":""}
@@ -1,9 +1,10 @@
1
1
  import { afterAll, assert, describe, expect, test } from 'vitest';
2
2
  import http from 'http';
3
3
  import { createWebSocketServer, createWsTransports, iterNext, onServerReady, } from '../util/testHelpers';
4
- import { SubscribableServiceConstructor, TestServiceConstructor, } from './fixtures/services';
4
+ import { SubscribableServiceConstructor, TestServiceConstructor, UploadableServiceConstructor, } from './fixtures/services';
5
5
  import { createClient, createServer } from '../router';
6
- import { ensureServerIsClean, ensureTransportQueuesAreEventuallyEmpty, waitUntil, } from './fixtures/cleanup';
6
+ import { ensureServerIsClean, ensureTransportQueuesAreEventuallyEmpty, waitFor, } from './fixtures/cleanup';
7
+ import { buildServiceDefs } from '../router/defs';
7
8
  describe('procedures should leave no trace after finishing', async () => {
8
9
  const httpServer = http.createServer();
9
10
  const port = await onServerReady(httpServer);
@@ -15,8 +16,8 @@ describe('procedures should leave no trace after finishing', async () => {
15
16
  });
16
17
  test('closing a transport from the client cleans up connection on the server', async () => {
17
18
  const [clientTransport, serverTransport] = getTransports();
18
- const serviceDefs = { test: TestServiceConstructor() };
19
- const server = await createServer(serverTransport, serviceDefs);
19
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
20
+ const server = createServer(serverTransport, serviceDefs);
20
21
  const client = createClient(clientTransport);
21
22
  expect(clientTransport.connections.size).toEqual(0);
22
23
  expect(serverTransport.connections.size).toEqual(0);
@@ -28,12 +29,12 @@ describe('procedures should leave no trace after finishing', async () => {
28
29
  // should be back to 0 connections after client closes
29
30
  clientTransport.close();
30
31
  expect(clientTransport.connections.size).toEqual(0);
31
- await waitUntil(() => serverTransport.connections.size, 0, 'server should cleanup connection after client closes');
32
+ await waitFor(() => expect(serverTransport.connections.size, 'server should cleanup connection after client closes').toEqual(0));
32
33
  });
33
34
  test('closing a transport from the server cleans up connection on the client', async () => {
34
35
  const [clientTransport, serverTransport] = getTransports();
35
- const serviceDefs = { test: TestServiceConstructor() };
36
- const server = await createServer(serverTransport, serviceDefs);
36
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
37
+ const server = createServer(serverTransport, serviceDefs);
37
38
  const client = createClient(clientTransport);
38
39
  expect(clientTransport.connections.size).toEqual(0);
39
40
  expect(serverTransport.connections.size).toEqual(0);
@@ -45,12 +46,12 @@ describe('procedures should leave no trace after finishing', async () => {
45
46
  // should be back to 0 connections after client closes
46
47
  serverTransport.close();
47
48
  expect(serverTransport.connections.size).toEqual(0);
48
- await waitUntil(() => clientTransport.connections.size, 0, 'client should cleanup connection after server closes');
49
+ await waitFor(() => expect(clientTransport.connections.size, 'client should cleanup connection after server closes').toEqual(0));
49
50
  });
50
51
  test('rpc', async () => {
51
52
  const [clientTransport, serverTransport] = getTransports();
52
- const serviceDefs = { test: TestServiceConstructor() };
53
- const server = await createServer(serverTransport, serviceDefs);
53
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
54
+ const server = createServer(serverTransport, serviceDefs);
54
55
  const client = createClient(clientTransport);
55
56
  let serverListeners = serverTransport.eventDispatcher.numberOfListeners('message');
56
57
  let clientListeners = clientTransport.eventDispatcher.numberOfListeners('message');
@@ -70,8 +71,8 @@ describe('procedures should leave no trace after finishing', async () => {
70
71
  });
71
72
  test('stream', async () => {
72
73
  const [clientTransport, serverTransport] = getTransports();
73
- const serviceDefs = { test: TestServiceConstructor() };
74
- const server = await createServer(serverTransport, serviceDefs);
74
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
75
+ const server = createServer(serverTransport, serviceDefs);
75
76
  const client = createClient(clientTransport);
76
77
  let serverListeners = serverTransport.eventDispatcher.numberOfListeners('message');
77
78
  let clientListeners = clientTransport.eventDispatcher.numberOfListeners('message');
@@ -83,10 +84,10 @@ describe('procedures should leave no trace after finishing', async () => {
83
84
  assert(result1.ok);
84
85
  expect(result1.payload).toStrictEqual({ response: '1' });
85
86
  // ensure we only have one stream despite pushing multiple messages.
86
- await waitUntil(() => server.streams.size, 1);
87
+ await waitFor(() => expect(server.streams.size).toEqual(1));
87
88
  input.end();
88
89
  // ensure we no longer have any streams since the input was closed.
89
- await waitUntil(() => server.streams.size, 0);
90
+ await waitFor(() => expect(server.streams.size).toEqual(0));
90
91
  const result2 = await iterNext(output);
91
92
  assert(result2.ok);
92
93
  expect(result2.payload).toStrictEqual({ response: '2' });
@@ -107,24 +108,50 @@ describe('procedures should leave no trace after finishing', async () => {
107
108
  });
108
109
  test('subscription', async () => {
109
110
  const [clientTransport, serverTransport] = getTransports();
110
- const serviceDefs = { test: SubscribableServiceConstructor() };
111
- const server = await createServer(serverTransport, serviceDefs);
111
+ const serviceDefs = buildServiceDefs([SubscribableServiceConstructor()]);
112
+ const server = createServer(serverTransport, serviceDefs);
112
113
  const client = createClient(clientTransport);
113
114
  let serverListeners = serverTransport.eventDispatcher.numberOfListeners('message');
114
115
  let clientListeners = clientTransport.eventDispatcher.numberOfListeners('message');
115
116
  // start procedure
116
- const [subscription, close] = await client.test.value.subscribe({});
117
+ const [subscription, close] = await client.subscribable.value.subscribe({});
117
118
  let result = await iterNext(subscription);
118
119
  assert(result.ok);
119
120
  expect(result.payload).toStrictEqual({ result: 0 });
120
- const add1 = await client.test.add.rpc({ n: 1 });
121
+ const add1 = await client.subscribable.add.rpc({ n: 1 });
121
122
  assert(add1.ok);
122
123
  result = await iterNext(subscription);
123
124
  assert(result.ok);
124
- expect(result.payload).toStrictEqual({ result: 1 });
125
125
  close();
126
126
  // end procedure
127
- // number of message handlers shouldn't increase after stream ends
127
+ // number of message handlers shouldn't increase after subscription ends
128
+ expect(serverTransport.eventDispatcher.numberOfListeners('message')).toEqual(serverListeners);
129
+ expect(clientTransport.eventDispatcher.numberOfListeners('message')).toEqual(clientListeners);
130
+ // check number of connections
131
+ expect(serverTransport.connections.size).toEqual(1);
132
+ expect(clientTransport.connections.size).toEqual(1);
133
+ await ensureTransportQueuesAreEventuallyEmpty(clientTransport);
134
+ await ensureTransportQueuesAreEventuallyEmpty(serverTransport);
135
+ // ensure we have no streams left on the server
136
+ await ensureServerIsClean(server);
137
+ });
138
+ test('upload', async () => {
139
+ const [clientTransport, serverTransport] = getTransports();
140
+ const serviceDefs = buildServiceDefs([UploadableServiceConstructor()]);
141
+ const server = createServer(serverTransport, serviceDefs);
142
+ const client = createClient(clientTransport);
143
+ let serverListeners = serverTransport.eventDispatcher.numberOfListeners('message');
144
+ let clientListeners = clientTransport.eventDispatcher.numberOfListeners('message');
145
+ // start procedure
146
+ const [addStream, addResult] = await client.uploadable.addMultiple.upload();
147
+ addStream.push({ n: 1 });
148
+ addStream.push({ n: 2 });
149
+ addStream.end();
150
+ const result = await addResult;
151
+ assert(result.ok);
152
+ expect(result.payload).toStrictEqual({ result: 3 });
153
+ // end procedure
154
+ // number of message handlers shouldn't increase after upload ends
128
155
  expect(serverTransport.eventDispatcher.numberOfListeners('message')).toEqual(serverListeners);
129
156
  expect(clientTransport.eventDispatcher.numberOfListeners('message')).toEqual(clientListeners);
130
157
  // check number of connections
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=disconnects.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"disconnects.test.d.ts","sourceRoot":"","sources":["../../__tests__/disconnects.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,163 @@
1
+ import { afterAll, afterEach, assert, beforeEach, describe, expect, test, vi, } from 'vitest';
2
+ import http from 'http';
3
+ import { createLocalWebSocketClient, createWebSocketServer, createWsTransports, iterNext, onServerReady, } from '../util/testHelpers';
4
+ import { SubscribableServiceConstructor, TestServiceConstructor, UploadableServiceConstructor, } from './fixtures/services';
5
+ import { createClient, createServer } from '../router';
6
+ import { ensureServerIsClean, waitFor } from './fixtures/cleanup';
7
+ import { CONNECTION_GRACE_PERIOD_MS } from '../router/client';
8
+ import { Err, UNEXPECTED_DISCONNECT } from '../router/result';
9
+ import { WebSocketServerTransport } from '../transport/impls/ws/server';
10
+ import { WebSocketClientTransport } from '../transport/impls/ws/client';
11
+ import { buildServiceDefs } from '../router/defs';
12
+ describe('procedures should handle unexpected disconnects', async () => {
13
+ const httpServer = http.createServer();
14
+ const port = await onServerReady(httpServer);
15
+ const webSocketServer = await createWebSocketServer(httpServer);
16
+ const getTransports = () => createWsTransports(port, webSocketServer);
17
+ afterAll(() => {
18
+ webSocketServer.close();
19
+ httpServer.close();
20
+ });
21
+ beforeEach(() => {
22
+ vi.useFakeTimers();
23
+ });
24
+ afterEach(() => {
25
+ vi.useRealTimers();
26
+ });
27
+ test('rpc', async () => {
28
+ const [clientTransport, serverTransport] = getTransports();
29
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
30
+ const server = createServer(serverTransport, serviceDefs);
31
+ const client = createClient(clientTransport);
32
+ // start procedure
33
+ await client.test.add.rpc({ n: 3 });
34
+ expect(clientTransport.connections.size).toEqual(1);
35
+ expect(serverTransport.connections.size).toEqual(1);
36
+ clientTransport.connections.forEach((conn) => conn.ws.close());
37
+ clientTransport.tryReconnecting = false;
38
+ const procPromise = client.test.add.rpc({ n: 4 });
39
+ // end procedure
40
+ // after we've disconnected, hit end of grace period
41
+ await vi.runOnlyPendingTimersAsync();
42
+ await vi.advanceTimersByTimeAsync(CONNECTION_GRACE_PERIOD_MS);
43
+ // we should get an error + expect the streams to be cleaned up
44
+ await expect(procPromise).resolves.toMatchObject(Err({
45
+ code: UNEXPECTED_DISCONNECT,
46
+ }));
47
+ waitFor(() => expect(clientTransport.connections.size).toEqual(0));
48
+ waitFor(() => expect(serverTransport.connections.size).toEqual(0));
49
+ await ensureServerIsClean(server);
50
+ });
51
+ test('stream', async () => {
52
+ const [clientTransport, serverTransport] = getTransports();
53
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
54
+ const server = createServer(serverTransport, serviceDefs);
55
+ const client = createClient(clientTransport);
56
+ // start procedure
57
+ const [input, output] = await client.test.echo.stream();
58
+ input.push({ msg: 'abc', ignore: false });
59
+ const result = await iterNext(output);
60
+ assert(result.ok);
61
+ expect(clientTransport.connections.size).toEqual(1);
62
+ expect(serverTransport.connections.size).toEqual(1);
63
+ clientTransport.connections.forEach((conn) => conn.ws.close());
64
+ clientTransport.tryReconnecting = false;
65
+ const nextResPromise = iterNext(output);
66
+ // end procedure
67
+ // after we've disconnected, hit end of grace period
68
+ await vi.runOnlyPendingTimersAsync();
69
+ await vi.advanceTimersByTimeAsync(CONNECTION_GRACE_PERIOD_MS);
70
+ // we should get an error + expect the streams to be cleaned up
71
+ await expect(nextResPromise).resolves.toMatchObject(Err({
72
+ code: UNEXPECTED_DISCONNECT,
73
+ }));
74
+ waitFor(() => expect(clientTransport.connections.size).toEqual(0));
75
+ waitFor(() => expect(serverTransport.connections.size).toEqual(0));
76
+ await ensureServerIsClean(server);
77
+ });
78
+ test('subscription', async () => {
79
+ const serverTransport = new WebSocketServerTransport(webSocketServer, 'SERVER');
80
+ const client1Transport = new WebSocketClientTransport(() => createLocalWebSocketClient(port), 'client1', 'SERVER');
81
+ const client2Transport = new WebSocketClientTransport(() => createLocalWebSocketClient(port), 'client2', 'SERVER');
82
+ const serviceDefs = buildServiceDefs([SubscribableServiceConstructor()]);
83
+ const server = createServer(serverTransport, serviceDefs);
84
+ const client1 = createClient(client1Transport);
85
+ const client2 = createClient(client2Transport);
86
+ // start procedure
87
+ // client1 and client2 both subscribe
88
+ const [subscription1, close1] = await client1.subscribable.value.subscribe({});
89
+ let result = await iterNext(subscription1);
90
+ assert(result.ok);
91
+ expect(result.payload).toStrictEqual({ result: 0 });
92
+ const [subscription2, _close2] = await client2.subscribable.value.subscribe({});
93
+ result = await iterNext(subscription2);
94
+ assert(result.ok);
95
+ expect(result.payload).toStrictEqual({ result: 0 });
96
+ // client2 adds a value
97
+ const add1 = await client2.subscribable.add.rpc({ n: 1 });
98
+ assert(add1.ok);
99
+ // both clients should receive the updated value
100
+ result = await iterNext(subscription1);
101
+ assert(result.ok);
102
+ expect(result.payload).toStrictEqual({ result: 1 });
103
+ result = await iterNext(subscription2);
104
+ assert(result.ok);
105
+ expect(result.payload).toStrictEqual({ result: 1 });
106
+ // all clients are connected
107
+ expect(client1Transport.connections.size).toEqual(1);
108
+ expect(client2Transport.connections.size).toEqual(1);
109
+ expect(serverTransport.connections.size).toEqual(2);
110
+ // kill the connection for client2
111
+ client2Transport.connections.forEach((conn) => conn.ws.close());
112
+ client2Transport.tryReconnecting = false;
113
+ // client1 who is still connected can still add values and receive updates
114
+ const add2Promise = client1.subscribable.add.rpc({ n: 2 });
115
+ // after we've disconnected, hit end of grace period
116
+ await vi.runOnlyPendingTimersAsync();
117
+ await vi.advanceTimersByTimeAsync(CONNECTION_GRACE_PERIOD_MS);
118
+ // we should get an error from the subscription on client2
119
+ const nextResPromise = iterNext(subscription2);
120
+ await expect(nextResPromise).resolves.toMatchObject(Err({
121
+ code: UNEXPECTED_DISCONNECT,
122
+ }));
123
+ // client1 who is still connected can still add values and receive updates
124
+ assert((await add2Promise).ok);
125
+ result = await iterNext(subscription1);
126
+ assert(result.ok);
127
+ expect(result.payload).toStrictEqual({ result: 3 });
128
+ // at this point, only client1 is connected
129
+ expect(client1Transport.connections.size).toEqual(1);
130
+ expect(client2Transport.connections.size).toEqual(0);
131
+ expect(serverTransport.connections.size).toEqual(1);
132
+ // cleanup client1 (client2 is already disconnected)
133
+ close1();
134
+ await client1Transport.close();
135
+ await ensureServerIsClean(server);
136
+ });
137
+ test('upload', async () => {
138
+ const [clientTransport, serverTransport] = getTransports();
139
+ const serviceDefs = buildServiceDefs([UploadableServiceConstructor()]);
140
+ const server = createServer(serverTransport, serviceDefs);
141
+ const client = createClient(clientTransport);
142
+ // start procedure
143
+ const [addStream, addResult] = await client.uploadable.addMultiple.upload();
144
+ addStream.push({ n: 1 });
145
+ addStream.push({ n: 2 });
146
+ // end procedure
147
+ // need to wait for connection to be established
148
+ await waitFor(() => expect(clientTransport.connections.size).toEqual(1));
149
+ await waitFor(() => expect(serverTransport.connections.size).toEqual(1));
150
+ clientTransport.connections.forEach((conn) => conn.ws.close());
151
+ clientTransport.tryReconnecting = false;
152
+ // after we've disconnected, hit end of grace period
153
+ await vi.runOnlyPendingTimersAsync();
154
+ await vi.advanceTimersByTimeAsync(CONNECTION_GRACE_PERIOD_MS);
155
+ // we should get an error + expect the streams to be cleaned up
156
+ await expect(addResult).resolves.toMatchObject(Err({
157
+ code: UNEXPECTED_DISCONNECT,
158
+ }));
159
+ waitFor(() => expect(clientTransport.connections.size).toEqual(0));
160
+ waitFor(() => expect(serverTransport.connections.size).toEqual(0));
161
+ await ensureServerIsClean(server);
162
+ });
163
+ });
@@ -9,6 +9,7 @@ import { codecs } from '../codec/codec.test';
9
9
  import { WebSocketClientTransport } from '../transport/impls/ws/client';
10
10
  import { WebSocketServerTransport } from '../transport/impls/ws/server';
11
11
  import { testFinishesCleanly } from './fixtures/cleanup';
12
+ import { buildServiceDefs } from '../router/defs';
12
13
  describe.each(codecs)('client <-> server integration test ($name codec)', async ({ codec }) => {
13
14
  const httpServer = http.createServer();
14
15
  const port = await onServerReady(httpServer);
@@ -20,8 +21,8 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
20
21
  });
21
22
  test('rpc', async () => {
22
23
  const [clientTransport, serverTransport] = getTransports();
23
- const serviceDefs = { test: TestServiceConstructor() };
24
- const server = await createServer(serverTransport, serviceDefs);
24
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
25
+ const server = createServer(serverTransport, serviceDefs);
25
26
  const client = createClient(clientTransport);
26
27
  const result = await client.test.add.rpc({ n: 3 });
27
28
  assert(result.ok);
@@ -34,13 +35,13 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
34
35
  });
35
36
  test('fallible rpc', async () => {
36
37
  const [clientTransport, serverTransport] = getTransports();
37
- const serviceDefs = { test: FallibleServiceConstructor() };
38
- const server = await createServer(serverTransport, serviceDefs);
38
+ const serviceDefs = buildServiceDefs([FallibleServiceConstructor()]);
39
+ const server = createServer(serverTransport, serviceDefs);
39
40
  const client = createClient(clientTransport);
40
- const result = await client.test.divide.rpc({ a: 10, b: 2 });
41
+ const result = await client.fallible.divide.rpc({ a: 10, b: 2 });
41
42
  assert(result.ok);
42
43
  expect(result.payload).toStrictEqual({ result: 5 });
43
- const result2 = await client.test.divide.rpc({ a: 10, b: 0 });
44
+ const result2 = await client.fallible.divide.rpc({ a: 10, b: 0 });
44
45
  assert(!result2.ok);
45
46
  expect(result2.payload).toStrictEqual({
46
47
  code: DIV_BY_ZERO,
@@ -57,10 +58,10 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
57
58
  });
58
59
  test('rpc with binary (uint8array)', async () => {
59
60
  const [clientTransport, serverTransport] = getTransports();
60
- const serviceDefs = { test: BinaryFileServiceConstructor() };
61
- const server = await createServer(serverTransport, serviceDefs);
61
+ const serviceDefs = buildServiceDefs([BinaryFileServiceConstructor()]);
62
+ const server = createServer(serverTransport, serviceDefs);
62
63
  const client = createClient(clientTransport);
63
- const result = await client.test.getFile.rpc({ file: 'test.py' });
64
+ const result = await client.bin.getFile.rpc({ file: 'test.py' });
64
65
  assert(result.ok);
65
66
  assert(result.payload.contents instanceof Uint8Array);
66
67
  expect(new TextDecoder().decode(result.payload.contents)).toStrictEqual('contents for file test.py');
@@ -72,8 +73,8 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
72
73
  });
73
74
  test('stream', async () => {
74
75
  const [clientTransport, serverTransport] = getTransports();
75
- const serviceDefs = { test: TestServiceConstructor() };
76
- const server = await createServer(serverTransport, serviceDefs);
76
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
77
+ const server = createServer(serverTransport, serviceDefs);
77
78
  const client = createClient(clientTransport);
78
79
  const [input, output, close] = await client.test.echo.stream();
79
80
  input.push({ msg: 'abc', ignore: false });
@@ -102,8 +103,8 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
102
103
  });
103
104
  test('stream with init message', async () => {
104
105
  const [clientTransport, serverTransport] = getTransports();
105
- const serviceDefs = { test: TestServiceConstructor() };
106
- const server = await createServer(serverTransport, serviceDefs);
106
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
107
+ const server = createServer(serverTransport, serviceDefs);
107
108
  const client = createClient(clientTransport);
108
109
  const [input, output, close] = await client.test.echoWithPrefix.stream({
109
110
  prefix: 'test',
@@ -127,10 +128,10 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
127
128
  });
128
129
  test('fallible stream', async () => {
129
130
  const [clientTransport, serverTransport] = getTransports();
130
- const serviceDefs = { test: FallibleServiceConstructor() };
131
- const server = await createServer(serverTransport, serviceDefs);
131
+ const serviceDefs = buildServiceDefs([FallibleServiceConstructor()]);
132
+ const server = createServer(serverTransport, serviceDefs);
132
133
  const client = createClient(clientTransport);
133
- const [input, output, close] = await client.test.echo.stream();
134
+ const [input, output, close] = await client.fallible.echo.stream();
134
135
  input.push({ msg: 'abc', throwResult: false, throwError: false });
135
136
  const result1 = await iterNext(output);
136
137
  assert(result1 && result1.ok);
@@ -158,19 +159,19 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
158
159
  const serverTransport = new WebSocketServerTransport(webSocketServer, 'SERVER', options);
159
160
  const client1Transport = new WebSocketClientTransport(() => createLocalWebSocketClient(port), 'client1', 'SERVER', options);
160
161
  const client2Transport = new WebSocketClientTransport(() => createLocalWebSocketClient(port), 'client2', 'SERVER', options);
161
- const serviceDefs = { test: SubscribableServiceConstructor() };
162
- const server = await createServer(serverTransport, serviceDefs);
162
+ const serviceDefs = buildServiceDefs([SubscribableServiceConstructor()]);
163
+ const server = createServer(serverTransport, serviceDefs);
163
164
  const client1 = createClient(client1Transport);
164
165
  const client2 = createClient(client2Transport);
165
- const [subscription1, close1] = await client1.test.value.subscribe({});
166
+ const [subscription1, close1] = await client1.subscribable.value.subscribe({});
166
167
  let result = await iterNext(subscription1);
167
168
  assert(result.ok);
168
169
  expect(result.payload).toStrictEqual({ result: 0 });
169
- const [subscription2, close2] = await client2.test.value.subscribe({});
170
+ const [subscription2, close2] = await client2.subscribable.value.subscribe({});
170
171
  result = await iterNext(subscription2);
171
172
  assert(result.ok);
172
173
  expect(result.payload).toStrictEqual({ result: 0 });
173
- const add1 = await client1.test.add.rpc({ n: 1 });
174
+ const add1 = await client1.subscribable.add.rpc({ n: 1 });
174
175
  assert(add1.ok);
175
176
  result = await iterNext(subscription1);
176
177
  assert(result.ok);
@@ -178,7 +179,7 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
178
179
  result = await iterNext(subscription2);
179
180
  assert(result.ok);
180
181
  expect(result.payload).toStrictEqual({ result: 1 });
181
- const add2 = await client2.test.add.rpc({ n: 3 });
182
+ const add2 = await client2.subscribable.add.rpc({ n: 3 });
182
183
  assert(add2.ok);
183
184
  result = await iterNext(subscription1);
184
185
  assert(result.ok);
@@ -196,8 +197,8 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
196
197
  });
197
198
  test('upload', async () => {
198
199
  const [clientTransport, serverTransport] = getTransports();
199
- const serviceDefs = { uploadable: UploadableServiceConstructor() };
200
- const server = await createServer(serverTransport, serviceDefs);
200
+ const serviceDefs = buildServiceDefs([UploadableServiceConstructor()]);
201
+ const server = createServer(serverTransport, serviceDefs);
201
202
  const client = createClient(clientTransport);
202
203
  const [addStream, addResult] = await client.uploadable.addMultiple.upload();
203
204
  addStream.push({ n: 1 });
@@ -214,8 +215,8 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
214
215
  });
215
216
  test('upload with init message', async () => {
216
217
  const [clientTransport, serverTransport] = getTransports();
217
- const serviceDefs = { uploadable: UploadableServiceConstructor() };
218
- const server = await createServer(serverTransport, serviceDefs);
218
+ const serviceDefs = buildServiceDefs([UploadableServiceConstructor()]);
219
+ const server = createServer(serverTransport, serviceDefs);
219
220
  const client = createClient(clientTransport);
220
221
  const [addStream, addResult] = await client.uploadable.addMultipleWithPrefix.upload({
221
222
  prefix: 'test',
@@ -234,8 +235,8 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
234
235
  });
235
236
  test('message order is preserved in the face of disconnects', async () => {
236
237
  const [clientTransport, serverTransport] = getTransports();
237
- const serviceDefs = { test: OrderingServiceConstructor() };
238
- const server = await createServer(serverTransport, serviceDefs);
238
+ const serviceDefs = buildServiceDefs([OrderingServiceConstructor()]);
239
+ const server = createServer(serverTransport, serviceDefs);
239
240
  const client = createClient(clientTransport);
240
241
  const expected = [];
241
242
  for (let i = 0; i < 50; i++) {
@@ -262,8 +263,8 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
262
263
  const CONCURRENCY = 10;
263
264
  test('concurrent rpcs', async () => {
264
265
  const [clientTransport, serverTransport] = getTransports();
265
- const serviceDefs = { test: OrderingServiceConstructor() };
266
- const server = await createServer(serverTransport, serviceDefs);
266
+ const serviceDefs = buildServiceDefs([OrderingServiceConstructor()]);
267
+ const server = createServer(serverTransport, serviceDefs);
267
268
  const client = createClient(clientTransport);
268
269
  const promises = [];
269
270
  for (let i = 0; i < CONCURRENCY; i++) {
@@ -282,8 +283,8 @@ describe.each(codecs)('client <-> server integration test ($name codec)', async
282
283
  });
283
284
  test('concurrent streams', async () => {
284
285
  const [clientTransport, serverTransport] = getTransports();
285
- const serviceDefs = { test: TestServiceConstructor() };
286
- const server = await createServer(serverTransport, serviceDefs);
286
+ const serviceDefs = buildServiceDefs([TestServiceConstructor()]);
287
+ const server = createServer(serverTransport, serviceDefs);
287
288
  const client = createClient(clientTransport);
288
289
  const openStreams = [];
289
290
  for (let i = 0; i < CONCURRENCY; i++) {
@@ -1,9 +1,9 @@
1
1
  import { Connection, Transport } from '../../transport';
2
2
  import { Server } from '../../router';
3
3
  export declare function ensureTransportIsClean(t: Transport<Connection>): Promise<void>;
4
- export declare function waitUntil<T>(valueGetter: () => T, expected: T, message?: string): Promise<boolean>;
4
+ export declare function waitFor<T>(cb: () => T | Promise<T>): Promise<T>;
5
5
  export declare function ensureTransportQueuesAreEventuallyEmpty(t: Transport<Connection>): Promise<void>;
6
- export declare function ensureServerIsClean(s: Server<unknown>): Promise<boolean>;
6
+ export declare function ensureServerIsClean(s: Server<unknown>): Promise<void>;
7
7
  export declare function testFinishesCleanly({ clientTransports, serverTransport, server, }: Partial<{
8
8
  clientTransports: Array<Transport<Connection>>;
9
9
  serverTransport: Transport<Connection>;
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../../__tests__/fixtures/cleanup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAOtC,wBAAsB,sBAAsB,CAAC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,iBAapE;AAED,wBAAsB,SAAS,CAAC,CAAC,EAC/B,WAAW,EAAE,MAAM,CAAC,EACpB,QAAQ,EAAE,CAAC,EACX,OAAO,CAAC,EAAE,MAAM,oBAOjB;AAED,wBAAsB,uCAAuC,CAC3D,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,iBAazB;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,oBAM3D;AAED,wBAAsB,mBAAmB,CAAC,EACxC,gBAAgB,EAChB,eAAe,EACf,MAAM,GACP,EAAE,OAAO,CAAC;IACT,gBAAgB,EAAE,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/C,eAAe,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;CACzB,CAAC,iBAgBD"}
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../../__tests__/fixtures/cleanup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAOtC,wBAAsB,sBAAsB,CAAC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,iBAiBpE;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,cAElD;AAED,wBAAsB,uCAAuC,CAC3D,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,iBAczB;AAED,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,iBAO3D;AAED,wBAAsB,mBAAmB,CAAC,EACxC,gBAAgB,EAChB,eAAe,EACf,MAAM,GACP,EAAE,OAAO,CAAC;IACT,gBAAgB,EAAE,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/C,eAAe,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;CACzB,CAAC,iBAgBD"}
@@ -7,20 +7,17 @@ export async function ensureTransportIsClean(t) {
7
7
  expect(t.state, `transport ${t.clientId} should be closed after the test`).to.not.equal('open');
8
8
  expect(t.connections, `transport ${t.clientId} should not have open connections after the test`).toStrictEqual(new Map());
9
9
  expect(t.eventDispatcher.numberOfListeners('message'), `transport ${t.clientId} should not have open message handlers after the test`).equal(0);
10
+ expect(t.eventDispatcher.numberOfListeners('connectionStatus'), `transport ${t.clientId} should not have open connection handlers after the test`).equal(0);
10
11
  }
11
- export async function waitUntil(valueGetter, expected, message) {
12
- return vi
13
- .waitUntil(() => valueGetter() === expected, waitUntilOptions)
14
- .finally(() => {
15
- expect(valueGetter(), message).toEqual(expected);
16
- });
12
+ export function waitFor(cb) {
13
+ return vi.waitFor(cb, waitUntilOptions);
17
14
  }
18
15
  export async function ensureTransportQueuesAreEventuallyEmpty(t) {
19
- await waitUntil(() => t.sendQueue.size, 0, `transport ${t.clientId} should not have any messages waiting to send after the test`);
20
- await waitUntil(() => t.sendBuffer.size, 0, `transport ${t.clientId} should not have any un-acked messages after the test`);
16
+ await waitFor(() => expect(t.sendQueue.size, `transport ${t.clientId} should not have any messages waiting to send after the test`).toEqual(0));
17
+ await waitFor(() => expect(t.sendBuffer.size, `transport ${t.clientId} should not have any un-acked messages after the test`).toEqual(0));
21
18
  }
22
19
  export async function ensureServerIsClean(s) {
23
- return waitUntil(() => s.streams.size, 0, `server should not have any open streams after the test`);
20
+ return waitFor(() => expect(s.streams.size, `server should not have any open streams after the test`).toEqual(0));
24
21
  }
25
22
  export async function testFinishesCleanly({ clientTransports, serverTransport, server, }) {
26
23
  if (clientTransports) {