atom.io 0.16.1 → 0.16.3
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/data/dist/index.cjs +31 -14
- package/data/dist/index.cjs.map +1 -1
- package/data/dist/index.js +31 -14
- package/data/dist/index.js.map +1 -1
- package/data/src/join.ts +31 -14
- package/dist/chunk-H4Q5FTPZ.js +11 -0
- package/dist/chunk-H4Q5FTPZ.js.map +1 -0
- package/dist/index.cjs +7 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +8 -14
- package/dist/index.js.map +1 -1
- package/internal/dist/index.cjs +240 -193
- package/internal/dist/index.cjs.map +1 -1
- package/internal/dist/index.d.ts +30 -9
- package/internal/dist/index.js +233 -194
- package/internal/dist/index.js.map +1 -1
- package/internal/src/families/find-in-store.ts +74 -0
- package/internal/src/families/index.ts +1 -0
- package/internal/src/ingest-updates/ingest-transaction-update.ts +1 -0
- package/internal/src/mutable/tracker.ts +37 -32
- package/internal/src/mutable/transceiver.ts +1 -1
- package/internal/src/not-found-error.ts +14 -3
- package/internal/src/operation.ts +2 -1
- package/internal/src/selector/create-writable-selector.ts +2 -1
- package/internal/src/selector/register-selector.ts +5 -4
- package/internal/src/set-state/set-atom.ts +23 -6
- package/internal/src/set-state/stow-update.ts +2 -4
- package/internal/src/store/store.ts +13 -4
- package/internal/src/timeline/add-atom-to-timeline.ts +5 -5
- package/internal/src/transaction/abort-transaction.ts +2 -1
- package/internal/src/transaction/apply-transaction.ts +5 -3
- package/internal/src/transaction/build-transaction.ts +17 -10
- package/internal/src/transaction/create-transaction.ts +2 -3
- package/internal/src/transaction/index.ts +3 -2
- package/internal/src/transaction/is-root-store.ts +23 -0
- package/package.json +10 -10
- package/react/dist/index.cjs +27 -21
- package/react/dist/index.cjs.map +1 -1
- package/react/dist/index.d.ts +8 -2
- package/react/dist/index.js +27 -21
- package/react/dist/index.js.map +1 -1
- package/react/src/index.ts +4 -1
- package/react/src/use-i.ts +36 -0
- package/react/src/use-json.ts +38 -0
- package/react/src/use-o.ts +34 -0
- package/react/src/use-tl.ts +45 -0
- package/realtime-client/dist/index.cjs +163 -62
- package/realtime-client/dist/index.cjs.map +1 -1
- package/realtime-client/dist/index.d.ts +10 -6
- package/realtime-client/dist/index.js +153 -60
- package/realtime-client/dist/index.js.map +1 -1
- package/realtime-client/src/index.ts +2 -1
- package/realtime-client/src/pull-state.ts +4 -3
- package/realtime-client/src/{realtime-client-store.ts → realtime-client-stores/client-main-store.ts} +0 -8
- package/realtime-client/src/realtime-client-stores/client-sync-store.ts +15 -0
- package/realtime-client/src/realtime-client-stores/index.ts +2 -0
- package/realtime-client/src/sync-server-action.ts +131 -39
- package/realtime-client/src/sync-state.ts +19 -0
- package/realtime-react/dist/index.cjs +43 -26
- package/realtime-react/dist/index.cjs.map +1 -1
- package/realtime-react/dist/index.d.ts +3 -1
- package/realtime-react/dist/index.js +41 -25
- package/realtime-react/dist/index.js.map +1 -1
- package/realtime-react/src/index.ts +1 -0
- package/realtime-react/src/on-mount.ts +3 -21
- package/realtime-react/src/use-realtime-service.ts +1 -1
- package/realtime-react/src/use-server-action.ts +1 -1
- package/realtime-react/src/use-single-effect.ts +29 -0
- package/realtime-react/src/use-sync-server-action.ts +5 -8
- package/realtime-react/src/use-sync.ts +17 -0
- package/realtime-server/dist/index.cjs +242 -48
- package/realtime-server/dist/index.cjs.map +1 -1
- package/realtime-server/dist/index.d.ts +147 -9
- package/realtime-server/dist/index.js +232 -51
- package/realtime-server/dist/index.js.map +1 -1
- package/realtime-server/src/index.ts +2 -0
- package/realtime-server/src/realtime-action-receiver.ts +4 -3
- package/realtime-server/src/realtime-action-synchronizer.ts +100 -13
- package/realtime-server/src/realtime-family-provider.ts +10 -6
- package/realtime-server/src/realtime-mutable-family-provider.ts +15 -18
- package/realtime-server/src/realtime-mutable-provider.ts +1 -0
- package/realtime-server/src/realtime-server-stores/index.ts +2 -0
- package/realtime-server/src/realtime-server-stores/server-sync-store.ts +115 -0
- package/realtime-server/src/realtime-server-stores/server-user-store.ts +45 -0
- package/realtime-server/src/realtime-state-provider.ts +16 -8
- package/realtime-server/src/realtime-state-receiver.ts +1 -0
- package/realtime-server/src/realtime-state-synchronizer.ts +23 -0
- package/realtime-testing/dist/index.cjs +65 -26
- package/realtime-testing/dist/index.cjs.map +1 -1
- package/realtime-testing/dist/index.d.ts +11 -7
- package/realtime-testing/dist/index.js +64 -26
- package/realtime-testing/dist/index.js.map +1 -1
- package/realtime-testing/src/setup-realtime-test.tsx +83 -43
- package/src/find-state.ts +8 -16
- package/src/logger.ts +16 -11
- package/src/transaction.ts +4 -4
- package/react/src/store-hooks.ts +0 -87
- package/realtime-server/src/realtime-server-store.ts +0 -39
|
@@ -4,6 +4,7 @@ import type { Json } from "atom.io/json"
|
|
|
4
4
|
|
|
5
5
|
import type { ServerConfig } from "."
|
|
6
6
|
|
|
7
|
+
export type StateProvider = ReturnType<typeof realtimeStateProvider>
|
|
7
8
|
export function realtimeStateProvider({
|
|
8
9
|
socket,
|
|
9
10
|
store = IMPLICIT.STORE,
|
|
@@ -11,16 +12,11 @@ export function realtimeStateProvider({
|
|
|
11
12
|
return function stateProvider<J extends Json.Serializable>(
|
|
12
13
|
token: AtomIO.WritableToken<J>,
|
|
13
14
|
): () => void {
|
|
14
|
-
let unsubscribeFromStateUpdates: (() => void) |
|
|
15
|
-
|
|
16
|
-
const fillUnsubRequest = () => {
|
|
17
|
-
socket.off(`unsub:${token.key}`, fillUnsubRequest)
|
|
18
|
-
unsubscribeFromStateUpdates?.()
|
|
19
|
-
unsubscribeFromStateUpdates = null
|
|
20
|
-
}
|
|
15
|
+
let unsubscribeFromStateUpdates: (() => void) | undefined
|
|
21
16
|
|
|
22
17
|
const fillSubRequest = () => {
|
|
23
18
|
socket.emit(`serve:${token.key}`, AtomIO.getState(token, store))
|
|
19
|
+
|
|
24
20
|
unsubscribeFromStateUpdates = subscribeToState(
|
|
25
21
|
token,
|
|
26
22
|
({ newValue }) => {
|
|
@@ -29,6 +25,15 @@ export function realtimeStateProvider({
|
|
|
29
25
|
`expose-single:${socket.id}`,
|
|
30
26
|
store,
|
|
31
27
|
)
|
|
28
|
+
|
|
29
|
+
const fillUnsubRequest = () => {
|
|
30
|
+
socket.off(`unsub:${token.key}`, fillUnsubRequest)
|
|
31
|
+
if (unsubscribeFromStateUpdates) {
|
|
32
|
+
unsubscribeFromStateUpdates()
|
|
33
|
+
unsubscribeFromStateUpdates = undefined
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
socket.on(`unsub:${token.key}`, fillUnsubRequest)
|
|
33
38
|
}
|
|
34
39
|
|
|
@@ -36,7 +41,10 @@ export function realtimeStateProvider({
|
|
|
36
41
|
|
|
37
42
|
return () => {
|
|
38
43
|
socket.off(`sub:${token.key}`, fillSubRequest)
|
|
39
|
-
unsubscribeFromStateUpdates
|
|
44
|
+
if (unsubscribeFromStateUpdates) {
|
|
45
|
+
unsubscribeFromStateUpdates()
|
|
46
|
+
unsubscribeFromStateUpdates = undefined
|
|
47
|
+
}
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as AtomIO from "atom.io"
|
|
2
|
+
import { IMPLICIT } from "atom.io/internal"
|
|
3
|
+
import type { Json } from "atom.io/json"
|
|
4
|
+
|
|
5
|
+
import type { ServerConfig } from "."
|
|
6
|
+
|
|
7
|
+
export function realtimeStateSynchronizer({
|
|
8
|
+
socket,
|
|
9
|
+
store = IMPLICIT.STORE,
|
|
10
|
+
}: ServerConfig) {
|
|
11
|
+
return function stateSynchronizer<J extends Json.Serializable>(
|
|
12
|
+
token: AtomIO.WritableToken<J>,
|
|
13
|
+
): () => void {
|
|
14
|
+
const fillGetRequest = () => {
|
|
15
|
+
socket.emit(`value:${token.key}`, AtomIO.getState(token, store))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
socket.on(`get:${token.key}`, fillGetRequest)
|
|
19
|
+
return () => {
|
|
20
|
+
socket.off(`get:${token.key}`, fillGetRequest)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -6,6 +6,7 @@ var AtomIO = require('atom.io');
|
|
|
6
6
|
var Internal = require('atom.io/internal');
|
|
7
7
|
var AR = require('atom.io/react');
|
|
8
8
|
var RTR = require('atom.io/realtime-react');
|
|
9
|
+
var RTS = require('atom.io/realtime-server');
|
|
9
10
|
var Happy = require('happy-dom');
|
|
10
11
|
var SocketIO = require('socket.io');
|
|
11
12
|
var socket_ioClient = require('socket.io-client');
|
|
@@ -34,6 +35,7 @@ var AtomIO__namespace = /*#__PURE__*/_interopNamespace(AtomIO);
|
|
|
34
35
|
var Internal__namespace = /*#__PURE__*/_interopNamespace(Internal);
|
|
35
36
|
var AR__namespace = /*#__PURE__*/_interopNamespace(AR);
|
|
36
37
|
var RTR__namespace = /*#__PURE__*/_interopNamespace(RTR);
|
|
38
|
+
var RTS__namespace = /*#__PURE__*/_interopNamespace(RTS);
|
|
37
39
|
var Happy__namespace = /*#__PURE__*/_interopNamespace(Happy);
|
|
38
40
|
var SocketIO__namespace = /*#__PURE__*/_interopNamespace(SocketIO);
|
|
39
41
|
|
|
@@ -60,13 +62,41 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
|
60
62
|
// ../anvl/src/object/entries.ts
|
|
61
63
|
var recordToEntries = (obj) => Object.entries(obj);
|
|
62
64
|
var setupRealtimeTestServer = (options) => {
|
|
65
|
+
const silo = new AtomIO__namespace.Silo(`SERVER`, Internal__namespace.IMPLICIT.STORE);
|
|
63
66
|
const httpServer = http__namespace.createServer((_, res) => res.end(`Hello World!`));
|
|
64
67
|
const address = httpServer.listen().address();
|
|
65
68
|
const port = typeof address === `string` ? 80 : address === null ? null : address.port;
|
|
66
69
|
if (port === null)
|
|
67
70
|
throw new Error(`Could not determine port for test server`);
|
|
68
|
-
const server = new SocketIO__namespace.Server(httpServer)
|
|
69
|
-
|
|
71
|
+
const server = new SocketIO__namespace.Server(httpServer).use((socket, next) => {
|
|
72
|
+
const { token, username } = socket.handshake.auth;
|
|
73
|
+
if (token === `test` && socket.id) {
|
|
74
|
+
const socketRelatedKeysState = Internal__namespace.findInStore(
|
|
75
|
+
RTS__namespace.usersOfSockets.core.findRelatedKeysState,
|
|
76
|
+
socket.id,
|
|
77
|
+
silo.store
|
|
78
|
+
);
|
|
79
|
+
const clientRelatedKeysState = Internal__namespace.findInStore(
|
|
80
|
+
RTS__namespace.usersOfSockets.core.findRelatedKeysState,
|
|
81
|
+
username,
|
|
82
|
+
silo.store
|
|
83
|
+
);
|
|
84
|
+
AtomIO__namespace.setState(
|
|
85
|
+
socketRelatedKeysState,
|
|
86
|
+
(keys) => (keys.clear(), keys.add(username)),
|
|
87
|
+
silo.store
|
|
88
|
+
);
|
|
89
|
+
AtomIO__namespace.setState(
|
|
90
|
+
clientRelatedKeysState,
|
|
91
|
+
(keys) => (keys.clear(), keys.add(socket.id)),
|
|
92
|
+
silo.store
|
|
93
|
+
);
|
|
94
|
+
console.log(`${username} connected on ${socket.id}`);
|
|
95
|
+
next();
|
|
96
|
+
} else {
|
|
97
|
+
next(new Error(`Authentication error`));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
70
100
|
server.on(`connection`, (socket) => {
|
|
71
101
|
options.server({ socket, silo });
|
|
72
102
|
});
|
|
@@ -82,32 +112,41 @@ var setupRealtimeTestServer = (options) => {
|
|
|
82
112
|
};
|
|
83
113
|
};
|
|
84
114
|
var setupRealtimeTestClient = (options, name, port) => {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
115
|
+
const testClient = { dispose: () => {
|
|
116
|
+
} };
|
|
117
|
+
const init = () => {
|
|
118
|
+
const socket = socket_ioClient.io(`http://localhost:${port}/`, {
|
|
119
|
+
auth: { token: `test`, username: name }
|
|
120
|
+
});
|
|
121
|
+
const silo = new AtomIO__namespace.Silo(name, Internal__namespace.IMPLICIT.STORE);
|
|
122
|
+
for (const [key, value] of silo.store.valueMap.entries()) {
|
|
123
|
+
if (Array.isArray(value)) {
|
|
124
|
+
silo.store.valueMap.set(key, [...value]);
|
|
125
|
+
}
|
|
93
126
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
const { document } = new Happy__namespace.Window();
|
|
128
|
+
document.body.innerHTML = `<div id="app"></div>`;
|
|
129
|
+
const renderResult = react.render(
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx(AR__namespace.StoreProvider, { store: silo.store, children: /* @__PURE__ */ jsxRuntime.jsx(RTR__namespace.RealtimeProvider, { socket, children: /* @__PURE__ */ jsxRuntime.jsx(options.client, {}) }) }),
|
|
131
|
+
{
|
|
132
|
+
container: document.querySelector(`#app`)
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
const prettyPrint = () => console.log(react.prettyDOM(renderResult.container));
|
|
136
|
+
const dispose = () => {
|
|
137
|
+
socket.disconnect();
|
|
138
|
+
Internal__namespace.clearStore(silo.store);
|
|
139
|
+
};
|
|
140
|
+
testClient.dispose = dispose;
|
|
141
|
+
return {
|
|
142
|
+
name,
|
|
143
|
+
silo,
|
|
144
|
+
socket,
|
|
145
|
+
renderResult,
|
|
146
|
+
prettyPrint
|
|
147
|
+
};
|
|
110
148
|
};
|
|
149
|
+
return Object.assign(testClient, { init });
|
|
111
150
|
};
|
|
112
151
|
var singleClient = (options) => {
|
|
113
152
|
const server = setupRealtimeTestServer(options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/setup-realtime-test.tsx","../../../anvl/src/object/entries.ts"],"names":["clients"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,YAAY,UAAU;AAEtB,SAA4B,WAAW,cAAc;AACrD,YAAY,YAAY;AACxB,YAAY,cAAc;AAC1B,YAAY,QAAQ;AACpB,YAAY,SAAS;AACrB,YAAY,WAAW;AAEvB,YAAY,cAAc;AAE1B,SAAS,UAAU;;;
|
|
1
|
+
{"version":3,"sources":["../src/setup-realtime-test.tsx","../../../anvl/src/object/entries.ts"],"names":["clients"],"mappings":"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","sourcesContent":["import * as http from \"http\"\n\nimport { type RenderResult, prettyDOM, render } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport * as AR from \"atom.io/react\"\nimport * as RTR from \"atom.io/realtime-react\"\nimport * as RTS from \"atom.io/realtime-server\"\nimport * as Happy from \"happy-dom\"\nimport * as React from \"react\"\nimport * as SocketIO from \"socket.io\"\nimport type { Socket as ClientSocket } from \"socket.io-client\"\nimport { io } from \"socket.io-client\"\n\nimport { recordToEntries } from \"~/packages/anvl/src/object\"\n\nexport type TestSetupOptions = {\n\tserver: (tools: { socket: SocketIO.Socket; silo: AtomIO.Silo }) => void\n}\nexport type TestSetupOptions__SingleClient = TestSetupOptions & {\n\tclient: React.FC\n}\nexport type TestSetupOptions__MultiClient<ClientNames extends string> =\n\tTestSetupOptions & {\n\t\tclients: {\n\t\t\t[K in ClientNames]: React.FC\n\t\t}\n\t}\n\nexport type RealtimeTestTools = {\n\tname: string\n\tsilo: AtomIO.Silo\n}\nexport type RealtimeTestClient = RealtimeTestTools & {\n\trenderResult: RenderResult\n\tprettyPrint: () => void\n\tsocket: ClientSocket\n}\nexport type RealtimeTestClientBuilder = {\n\tdispose: () => void\n\tinit: () => RealtimeTestClient\n}\n\nexport type RealtimeTestServer = RealtimeTestTools & {\n\tdispose: () => void\n\tport: number\n}\n\nexport type RealtimeTestAPI = {\n\tserver: RealtimeTestServer\n\tteardown: () => void\n}\nexport type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {\n\tclient: RealtimeTestClientBuilder\n}\nexport type RealtimeTestAPI__MultiClient<ClientNames extends string> =\n\tRealtimeTestAPI & {\n\t\tclients: Record<ClientNames, RealtimeTestClientBuilder>\n\t}\n\nexport const setupRealtimeTestServer = (\n\toptions: TestSetupOptions,\n): RealtimeTestServer => {\n\tconst silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE)\n\n\tconst httpServer = http.createServer((_, res) => res.end(`Hello World!`))\n\tconst address = httpServer.listen().address()\n\tconst port =\n\t\ttypeof address === `string` ? 80 : address === null ? null : address.port\n\tif (port === null) throw new Error(`Could not determine port for test server`)\n\tconst server = new SocketIO.Server(httpServer).use((socket, next) => {\n\t\tconst { token, username } = socket.handshake.auth\n\t\tif (token === `test` && socket.id) {\n\t\t\tconst socketRelatedKeysState = Internal.findInStore(\n\t\t\t\tRTS.usersOfSockets.core.findRelatedKeysState,\n\t\t\t\tsocket.id,\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tconst clientRelatedKeysState = Internal.findInStore(\n\t\t\t\tRTS.usersOfSockets.core.findRelatedKeysState,\n\t\t\t\tusername,\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tAtomIO.setState(\n\t\t\t\tsocketRelatedKeysState,\n\t\t\t\t(keys) => (keys.clear(), keys.add(username)),\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tAtomIO.setState(\n\t\t\t\tclientRelatedKeysState,\n\t\t\t\t(keys) => (keys.clear(), keys.add(socket.id)),\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tconsole.log(`${username} connected on ${socket.id}`)\n\t\t\tnext()\n\t\t} else {\n\t\t\tnext(new Error(`Authentication error`))\n\t\t}\n\t})\n\n\tserver.on(`connection`, (socket: SocketIO.Socket) => {\n\t\toptions.server({ socket, silo })\n\t})\n\n\tconst dispose = () => {\n\t\tserver.close()\n\t\tInternal.clearStore(silo.store)\n\t}\n\n\treturn {\n\t\tname: `SERVER`,\n\t\tsilo,\n\t\tdispose,\n\t\tport,\n\t}\n}\nexport const setupRealtimeTestClient = (\n\toptions: TestSetupOptions__SingleClient,\n\tname: string,\n\tport: number,\n): RealtimeTestClientBuilder => {\n\tconst testClient = { dispose: () => {} }\n\tconst init = () => {\n\t\tconst socket: ClientSocket = io(`http://localhost:${port}/`, {\n\t\t\tauth: { token: `test`, username: name },\n\t\t})\n\t\tconst silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE)\n\t\tfor (const [key, value] of silo.store.valueMap.entries()) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tsilo.store.valueMap.set(key, [...value])\n\t\t\t}\n\t\t}\n\n\t\tconst { document } = new Happy.Window()\n\t\tdocument.body.innerHTML = `<div id=\"app\"></div>`\n\t\tconst renderResult = render(\n\t\t\t<AR.StoreProvider store={silo.store}>\n\t\t\t\t<RTR.RealtimeProvider socket={socket}>\n\t\t\t\t\t<options.client />\n\t\t\t\t</RTR.RealtimeProvider>\n\t\t\t</AR.StoreProvider>,\n\t\t\t{\n\t\t\t\tcontainer: document.querySelector(`#app`) as unknown as HTMLElement,\n\t\t\t},\n\t\t)\n\n\t\tconst prettyPrint = () => console.log(prettyDOM(renderResult.container))\n\n\t\tconst dispose = () => {\n\t\t\tsocket.disconnect()\n\t\t\tInternal.clearStore(silo.store)\n\t\t}\n\t\ttestClient.dispose = dispose\n\n\t\treturn {\n\t\t\tname,\n\t\t\tsilo,\n\t\t\tsocket,\n\t\t\trenderResult,\n\t\t\tprettyPrint,\n\t\t}\n\t}\n\treturn Object.assign(testClient, { init })\n}\n\nexport const singleClient = (\n\toptions: TestSetupOptions__SingleClient,\n): RealtimeTestAPI__SingleClient => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst client = setupRealtimeTestClient(options, `CLIENT`, server.port)\n\n\treturn {\n\t\tclient,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tclient.dispose()\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n\nexport const multiClient = <ClientNames extends string>(\n\toptions: TestSetupOptions__MultiClient<ClientNames>,\n): RealtimeTestAPI__MultiClient<ClientNames> => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst clients = recordToEntries(options.clients).reduce(\n\t\t(clients, [name, client]) => {\n\t\t\tclients[name] = setupRealtimeTestClient(\n\t\t\t\t{ ...options, client },\n\t\t\t\tname,\n\t\t\t\tserver.port,\n\t\t\t)\n\t\t\treturn clients\n\t\t},\n\t\t{} as Record<ClientNames, RealtimeTestClientBuilder>,\n\t)\n\n\treturn {\n\t\tclients,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tfor (const [, client] of recordToEntries(clients)) {\n\t\t\t\tclient.dispose()\n\t\t\t}\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n","export type Entries<K extends keyof any, V> = [key: K, value: V][]\n\nexport const recordToEntries = <K extends keyof any, V>(\n\tobj: Record<K, V>,\n): Entries<K, V> => Object.entries(obj) as Entries<K, V>\n\nexport const entriesToRecord = <K extends keyof any, V>(\n\tentries: Entries<K, V>,\n): Record<K, V> => Object.fromEntries(entries) as Record<K, V>\n"]}
|
|
@@ -2,6 +2,7 @@ import { RenderResult } from '@testing-library/react';
|
|
|
2
2
|
import * as AtomIO from 'atom.io';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import * as SocketIO from 'socket.io';
|
|
5
|
+
import { Socket } from 'socket.io-client';
|
|
5
6
|
|
|
6
7
|
type TestSetupOptions = {
|
|
7
8
|
server: (tools: {
|
|
@@ -20,15 +21,18 @@ type TestSetupOptions__MultiClient<ClientNames extends string> = TestSetupOption
|
|
|
20
21
|
type RealtimeTestTools = {
|
|
21
22
|
name: string;
|
|
22
23
|
silo: AtomIO.Silo;
|
|
23
|
-
dispose: () => void;
|
|
24
24
|
};
|
|
25
25
|
type RealtimeTestClient = RealtimeTestTools & {
|
|
26
26
|
renderResult: RenderResult;
|
|
27
27
|
prettyPrint: () => void;
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
socket: Socket;
|
|
29
|
+
};
|
|
30
|
+
type RealtimeTestClientBuilder = {
|
|
31
|
+
dispose: () => void;
|
|
32
|
+
init: () => RealtimeTestClient;
|
|
30
33
|
};
|
|
31
34
|
type RealtimeTestServer = RealtimeTestTools & {
|
|
35
|
+
dispose: () => void;
|
|
32
36
|
port: number;
|
|
33
37
|
};
|
|
34
38
|
type RealtimeTestAPI = {
|
|
@@ -36,14 +40,14 @@ type RealtimeTestAPI = {
|
|
|
36
40
|
teardown: () => void;
|
|
37
41
|
};
|
|
38
42
|
type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {
|
|
39
|
-
client:
|
|
43
|
+
client: RealtimeTestClientBuilder;
|
|
40
44
|
};
|
|
41
45
|
type RealtimeTestAPI__MultiClient<ClientNames extends string> = RealtimeTestAPI & {
|
|
42
|
-
clients: Record<ClientNames,
|
|
46
|
+
clients: Record<ClientNames, RealtimeTestClientBuilder>;
|
|
43
47
|
};
|
|
44
48
|
declare const setupRealtimeTestServer: (options: TestSetupOptions) => RealtimeTestServer;
|
|
45
|
-
declare const setupRealtimeTestClient: (options: TestSetupOptions__SingleClient, name: string, port: number) =>
|
|
49
|
+
declare const setupRealtimeTestClient: (options: TestSetupOptions__SingleClient, name: string, port: number) => RealtimeTestClientBuilder;
|
|
46
50
|
declare const singleClient: (options: TestSetupOptions__SingleClient) => RealtimeTestAPI__SingleClient;
|
|
47
51
|
declare const multiClient: <ClientNames extends string>(options: TestSetupOptions__MultiClient<ClientNames>) => RealtimeTestAPI__MultiClient<ClientNames>;
|
|
48
52
|
|
|
49
|
-
export { type RealtimeTestAPI, type RealtimeTestAPI__MultiClient, type RealtimeTestAPI__SingleClient, type RealtimeTestClient, type RealtimeTestServer, type RealtimeTestTools, type TestSetupOptions, type TestSetupOptions__MultiClient, type TestSetupOptions__SingleClient, multiClient, setupRealtimeTestClient, setupRealtimeTestServer, singleClient };
|
|
53
|
+
export { type RealtimeTestAPI, type RealtimeTestAPI__MultiClient, type RealtimeTestAPI__SingleClient, type RealtimeTestClient, type RealtimeTestClientBuilder, type RealtimeTestServer, type RealtimeTestTools, type TestSetupOptions, type TestSetupOptions__MultiClient, type TestSetupOptions__SingleClient, multiClient, setupRealtimeTestClient, setupRealtimeTestServer, singleClient };
|
|
@@ -6,19 +6,48 @@ import * as AtomIO from 'atom.io';
|
|
|
6
6
|
import * as Internal from 'atom.io/internal';
|
|
7
7
|
import * as AR from 'atom.io/react';
|
|
8
8
|
import * as RTR from 'atom.io/realtime-react';
|
|
9
|
+
import * as RTS from 'atom.io/realtime-server';
|
|
9
10
|
import * as Happy from 'happy-dom';
|
|
10
11
|
import * as SocketIO from 'socket.io';
|
|
11
12
|
import { io } from 'socket.io-client';
|
|
12
13
|
import { jsx } from 'react/jsx-runtime';
|
|
13
14
|
|
|
14
15
|
var setupRealtimeTestServer = (options) => {
|
|
16
|
+
const silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE);
|
|
15
17
|
const httpServer = http.createServer((_, res) => res.end(`Hello World!`));
|
|
16
18
|
const address = httpServer.listen().address();
|
|
17
19
|
const port = typeof address === `string` ? 80 : address === null ? null : address.port;
|
|
18
20
|
if (port === null)
|
|
19
21
|
throw new Error(`Could not determine port for test server`);
|
|
20
|
-
const server = new SocketIO.Server(httpServer)
|
|
21
|
-
|
|
22
|
+
const server = new SocketIO.Server(httpServer).use((socket, next) => {
|
|
23
|
+
const { token, username } = socket.handshake.auth;
|
|
24
|
+
if (token === `test` && socket.id) {
|
|
25
|
+
const socketRelatedKeysState = Internal.findInStore(
|
|
26
|
+
RTS.usersOfSockets.core.findRelatedKeysState,
|
|
27
|
+
socket.id,
|
|
28
|
+
silo.store
|
|
29
|
+
);
|
|
30
|
+
const clientRelatedKeysState = Internal.findInStore(
|
|
31
|
+
RTS.usersOfSockets.core.findRelatedKeysState,
|
|
32
|
+
username,
|
|
33
|
+
silo.store
|
|
34
|
+
);
|
|
35
|
+
AtomIO.setState(
|
|
36
|
+
socketRelatedKeysState,
|
|
37
|
+
(keys) => (keys.clear(), keys.add(username)),
|
|
38
|
+
silo.store
|
|
39
|
+
);
|
|
40
|
+
AtomIO.setState(
|
|
41
|
+
clientRelatedKeysState,
|
|
42
|
+
(keys) => (keys.clear(), keys.add(socket.id)),
|
|
43
|
+
silo.store
|
|
44
|
+
);
|
|
45
|
+
console.log(`${username} connected on ${socket.id}`);
|
|
46
|
+
next();
|
|
47
|
+
} else {
|
|
48
|
+
next(new Error(`Authentication error`));
|
|
49
|
+
}
|
|
50
|
+
});
|
|
22
51
|
server.on(`connection`, (socket) => {
|
|
23
52
|
options.server({ socket, silo });
|
|
24
53
|
});
|
|
@@ -34,32 +63,41 @@ var setupRealtimeTestServer = (options) => {
|
|
|
34
63
|
};
|
|
35
64
|
};
|
|
36
65
|
var setupRealtimeTestClient = (options, name, port) => {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
const testClient = { dispose: () => {
|
|
67
|
+
} };
|
|
68
|
+
const init = () => {
|
|
69
|
+
const socket = io(`http://localhost:${port}/`, {
|
|
70
|
+
auth: { token: `test`, username: name }
|
|
71
|
+
});
|
|
72
|
+
const silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE);
|
|
73
|
+
for (const [key, value] of silo.store.valueMap.entries()) {
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
silo.store.valueMap.set(key, [...value]);
|
|
76
|
+
}
|
|
45
77
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
78
|
+
const { document } = new Happy.Window();
|
|
79
|
+
document.body.innerHTML = `<div id="app"></div>`;
|
|
80
|
+
const renderResult = render(
|
|
81
|
+
/* @__PURE__ */ jsx(AR.StoreProvider, { store: silo.store, children: /* @__PURE__ */ jsx(RTR.RealtimeProvider, { socket, children: /* @__PURE__ */ jsx(options.client, {}) }) }),
|
|
82
|
+
{
|
|
83
|
+
container: document.querySelector(`#app`)
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
const prettyPrint = () => console.log(prettyDOM(renderResult.container));
|
|
87
|
+
const dispose = () => {
|
|
88
|
+
socket.disconnect();
|
|
89
|
+
Internal.clearStore(silo.store);
|
|
90
|
+
};
|
|
91
|
+
testClient.dispose = dispose;
|
|
92
|
+
return {
|
|
93
|
+
name,
|
|
94
|
+
silo,
|
|
95
|
+
socket,
|
|
96
|
+
renderResult,
|
|
97
|
+
prettyPrint
|
|
98
|
+
};
|
|
62
99
|
};
|
|
100
|
+
return Object.assign(testClient, { init });
|
|
63
101
|
};
|
|
64
102
|
var singleClient = (options) => {
|
|
65
103
|
const server = setupRealtimeTestServer(options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/setup-realtime-test.tsx"],"names":["clients"],"mappings":";;;;;;;;;AAAA,YAAY,UAAU;AAEtB,SAA4B,WAAW,cAAc;AACrD,YAAY,YAAY;AACxB,YAAY,cAAc;AAC1B,YAAY,QAAQ;AACpB,YAAY,SAAS;AACrB,YAAY,WAAW;AAEvB,YAAY,cAAc;AAE1B,SAAS,UAAU;
|
|
1
|
+
{"version":3,"sources":["../src/setup-realtime-test.tsx"],"names":["clients"],"mappings":"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","sourcesContent":["import * as http from \"http\"\n\nimport { type RenderResult, prettyDOM, render } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport * as AR from \"atom.io/react\"\nimport * as RTR from \"atom.io/realtime-react\"\nimport * as RTS from \"atom.io/realtime-server\"\nimport * as Happy from \"happy-dom\"\nimport * as React from \"react\"\nimport * as SocketIO from \"socket.io\"\nimport type { Socket as ClientSocket } from \"socket.io-client\"\nimport { io } from \"socket.io-client\"\n\nimport { recordToEntries } from \"~/packages/anvl/src/object\"\n\nexport type TestSetupOptions = {\n\tserver: (tools: { socket: SocketIO.Socket; silo: AtomIO.Silo }) => void\n}\nexport type TestSetupOptions__SingleClient = TestSetupOptions & {\n\tclient: React.FC\n}\nexport type TestSetupOptions__MultiClient<ClientNames extends string> =\n\tTestSetupOptions & {\n\t\tclients: {\n\t\t\t[K in ClientNames]: React.FC\n\t\t}\n\t}\n\nexport type RealtimeTestTools = {\n\tname: string\n\tsilo: AtomIO.Silo\n}\nexport type RealtimeTestClient = RealtimeTestTools & {\n\trenderResult: RenderResult\n\tprettyPrint: () => void\n\tsocket: ClientSocket\n}\nexport type RealtimeTestClientBuilder = {\n\tdispose: () => void\n\tinit: () => RealtimeTestClient\n}\n\nexport type RealtimeTestServer = RealtimeTestTools & {\n\tdispose: () => void\n\tport: number\n}\n\nexport type RealtimeTestAPI = {\n\tserver: RealtimeTestServer\n\tteardown: () => void\n}\nexport type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {\n\tclient: RealtimeTestClientBuilder\n}\nexport type RealtimeTestAPI__MultiClient<ClientNames extends string> =\n\tRealtimeTestAPI & {\n\t\tclients: Record<ClientNames, RealtimeTestClientBuilder>\n\t}\n\nexport const setupRealtimeTestServer = (\n\toptions: TestSetupOptions,\n): RealtimeTestServer => {\n\tconst silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE)\n\n\tconst httpServer = http.createServer((_, res) => res.end(`Hello World!`))\n\tconst address = httpServer.listen().address()\n\tconst port =\n\t\ttypeof address === `string` ? 80 : address === null ? null : address.port\n\tif (port === null) throw new Error(`Could not determine port for test server`)\n\tconst server = new SocketIO.Server(httpServer).use((socket, next) => {\n\t\tconst { token, username } = socket.handshake.auth\n\t\tif (token === `test` && socket.id) {\n\t\t\tconst socketRelatedKeysState = Internal.findInStore(\n\t\t\t\tRTS.usersOfSockets.core.findRelatedKeysState,\n\t\t\t\tsocket.id,\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tconst clientRelatedKeysState = Internal.findInStore(\n\t\t\t\tRTS.usersOfSockets.core.findRelatedKeysState,\n\t\t\t\tusername,\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tAtomIO.setState(\n\t\t\t\tsocketRelatedKeysState,\n\t\t\t\t(keys) => (keys.clear(), keys.add(username)),\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tAtomIO.setState(\n\t\t\t\tclientRelatedKeysState,\n\t\t\t\t(keys) => (keys.clear(), keys.add(socket.id)),\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tconsole.log(`${username} connected on ${socket.id}`)\n\t\t\tnext()\n\t\t} else {\n\t\t\tnext(new Error(`Authentication error`))\n\t\t}\n\t})\n\n\tserver.on(`connection`, (socket: SocketIO.Socket) => {\n\t\toptions.server({ socket, silo })\n\t})\n\n\tconst dispose = () => {\n\t\tserver.close()\n\t\tInternal.clearStore(silo.store)\n\t}\n\n\treturn {\n\t\tname: `SERVER`,\n\t\tsilo,\n\t\tdispose,\n\t\tport,\n\t}\n}\nexport const setupRealtimeTestClient = (\n\toptions: TestSetupOptions__SingleClient,\n\tname: string,\n\tport: number,\n): RealtimeTestClientBuilder => {\n\tconst testClient = { dispose: () => {} }\n\tconst init = () => {\n\t\tconst socket: ClientSocket = io(`http://localhost:${port}/`, {\n\t\t\tauth: { token: `test`, username: name },\n\t\t})\n\t\tconst silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE)\n\t\tfor (const [key, value] of silo.store.valueMap.entries()) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tsilo.store.valueMap.set(key, [...value])\n\t\t\t}\n\t\t}\n\n\t\tconst { document } = new Happy.Window()\n\t\tdocument.body.innerHTML = `<div id=\"app\"></div>`\n\t\tconst renderResult = render(\n\t\t\t<AR.StoreProvider store={silo.store}>\n\t\t\t\t<RTR.RealtimeProvider socket={socket}>\n\t\t\t\t\t<options.client />\n\t\t\t\t</RTR.RealtimeProvider>\n\t\t\t</AR.StoreProvider>,\n\t\t\t{\n\t\t\t\tcontainer: document.querySelector(`#app`) as unknown as HTMLElement,\n\t\t\t},\n\t\t)\n\n\t\tconst prettyPrint = () => console.log(prettyDOM(renderResult.container))\n\n\t\tconst dispose = () => {\n\t\t\tsocket.disconnect()\n\t\t\tInternal.clearStore(silo.store)\n\t\t}\n\t\ttestClient.dispose = dispose\n\n\t\treturn {\n\t\t\tname,\n\t\t\tsilo,\n\t\t\tsocket,\n\t\t\trenderResult,\n\t\t\tprettyPrint,\n\t\t}\n\t}\n\treturn Object.assign(testClient, { init })\n}\n\nexport const singleClient = (\n\toptions: TestSetupOptions__SingleClient,\n): RealtimeTestAPI__SingleClient => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst client = setupRealtimeTestClient(options, `CLIENT`, server.port)\n\n\treturn {\n\t\tclient,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tclient.dispose()\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n\nexport const multiClient = <ClientNames extends string>(\n\toptions: TestSetupOptions__MultiClient<ClientNames>,\n): RealtimeTestAPI__MultiClient<ClientNames> => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst clients = recordToEntries(options.clients).reduce(\n\t\t(clients, [name, client]) => {\n\t\t\tclients[name] = setupRealtimeTestClient(\n\t\t\t\t{ ...options, client },\n\t\t\t\tname,\n\t\t\t\tserver.port,\n\t\t\t)\n\t\t\treturn clients\n\t\t},\n\t\t{} as Record<ClientNames, RealtimeTestClientBuilder>,\n\t)\n\n\treturn {\n\t\tclients,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tfor (const [, client] of recordToEntries(clients)) {\n\t\t\t\tclient.dispose()\n\t\t\t}\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n"]}
|
|
@@ -5,6 +5,7 @@ import * as AtomIO from "atom.io"
|
|
|
5
5
|
import * as Internal from "atom.io/internal"
|
|
6
6
|
import * as AR from "atom.io/react"
|
|
7
7
|
import * as RTR from "atom.io/realtime-react"
|
|
8
|
+
import * as RTS from "atom.io/realtime-server"
|
|
8
9
|
import * as Happy from "happy-dom"
|
|
9
10
|
import * as React from "react"
|
|
10
11
|
import * as SocketIO from "socket.io"
|
|
@@ -29,15 +30,19 @@ export type TestSetupOptions__MultiClient<ClientNames extends string> =
|
|
|
29
30
|
export type RealtimeTestTools = {
|
|
30
31
|
name: string
|
|
31
32
|
silo: AtomIO.Silo
|
|
32
|
-
dispose: () => void
|
|
33
33
|
}
|
|
34
34
|
export type RealtimeTestClient = RealtimeTestTools & {
|
|
35
35
|
renderResult: RenderResult
|
|
36
36
|
prettyPrint: () => void
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
socket: ClientSocket
|
|
38
|
+
}
|
|
39
|
+
export type RealtimeTestClientBuilder = {
|
|
40
|
+
dispose: () => void
|
|
41
|
+
init: () => RealtimeTestClient
|
|
39
42
|
}
|
|
43
|
+
|
|
40
44
|
export type RealtimeTestServer = RealtimeTestTools & {
|
|
45
|
+
dispose: () => void
|
|
41
46
|
port: number
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -46,24 +51,52 @@ export type RealtimeTestAPI = {
|
|
|
46
51
|
teardown: () => void
|
|
47
52
|
}
|
|
48
53
|
export type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {
|
|
49
|
-
client:
|
|
54
|
+
client: RealtimeTestClientBuilder
|
|
50
55
|
}
|
|
51
56
|
export type RealtimeTestAPI__MultiClient<ClientNames extends string> =
|
|
52
57
|
RealtimeTestAPI & {
|
|
53
|
-
clients: Record<ClientNames,
|
|
58
|
+
clients: Record<ClientNames, RealtimeTestClientBuilder>
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
export const setupRealtimeTestServer = (
|
|
57
62
|
options: TestSetupOptions,
|
|
58
63
|
): RealtimeTestServer => {
|
|
64
|
+
const silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE)
|
|
65
|
+
|
|
59
66
|
const httpServer = http.createServer((_, res) => res.end(`Hello World!`))
|
|
60
67
|
const address = httpServer.listen().address()
|
|
61
68
|
const port =
|
|
62
69
|
typeof address === `string` ? 80 : address === null ? null : address.port
|
|
63
70
|
if (port === null) throw new Error(`Could not determine port for test server`)
|
|
64
|
-
const server = new SocketIO.Server(httpServer)
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
const server = new SocketIO.Server(httpServer).use((socket, next) => {
|
|
72
|
+
const { token, username } = socket.handshake.auth
|
|
73
|
+
if (token === `test` && socket.id) {
|
|
74
|
+
const socketRelatedKeysState = Internal.findInStore(
|
|
75
|
+
RTS.usersOfSockets.core.findRelatedKeysState,
|
|
76
|
+
socket.id,
|
|
77
|
+
silo.store,
|
|
78
|
+
)
|
|
79
|
+
const clientRelatedKeysState = Internal.findInStore(
|
|
80
|
+
RTS.usersOfSockets.core.findRelatedKeysState,
|
|
81
|
+
username,
|
|
82
|
+
silo.store,
|
|
83
|
+
)
|
|
84
|
+
AtomIO.setState(
|
|
85
|
+
socketRelatedKeysState,
|
|
86
|
+
(keys) => (keys.clear(), keys.add(username)),
|
|
87
|
+
silo.store,
|
|
88
|
+
)
|
|
89
|
+
AtomIO.setState(
|
|
90
|
+
clientRelatedKeysState,
|
|
91
|
+
(keys) => (keys.clear(), keys.add(socket.id)),
|
|
92
|
+
silo.store,
|
|
93
|
+
)
|
|
94
|
+
console.log(`${username} connected on ${socket.id}`)
|
|
95
|
+
next()
|
|
96
|
+
} else {
|
|
97
|
+
next(new Error(`Authentication error`))
|
|
98
|
+
}
|
|
99
|
+
})
|
|
67
100
|
|
|
68
101
|
server.on(`connection`, (socket: SocketIO.Socket) => {
|
|
69
102
|
options.server({ socket, silo })
|
|
@@ -85,42 +118,49 @@ export const setupRealtimeTestClient = (
|
|
|
85
118
|
options: TestSetupOptions__SingleClient,
|
|
86
119
|
name: string,
|
|
87
120
|
port: number,
|
|
88
|
-
):
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
{
|
|
101
|
-
container: document.querySelector(`#app`) as unknown as HTMLElement,
|
|
102
|
-
},
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
const prettyPrint = () => console.log(prettyDOM(renderResult.container))
|
|
106
|
-
|
|
107
|
-
const disconnect = () => socket.disconnect()
|
|
108
|
-
const reconnect = () => socket.connect()
|
|
109
|
-
|
|
110
|
-
const dispose = () => {
|
|
111
|
-
socket.disconnect()
|
|
112
|
-
Internal.clearStore(silo.store)
|
|
113
|
-
}
|
|
121
|
+
): RealtimeTestClientBuilder => {
|
|
122
|
+
const testClient = { dispose: () => {} }
|
|
123
|
+
const init = () => {
|
|
124
|
+
const socket: ClientSocket = io(`http://localhost:${port}/`, {
|
|
125
|
+
auth: { token: `test`, username: name },
|
|
126
|
+
})
|
|
127
|
+
const silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE)
|
|
128
|
+
for (const [key, value] of silo.store.valueMap.entries()) {
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
silo.store.valueMap.set(key, [...value])
|
|
131
|
+
}
|
|
132
|
+
}
|
|
114
133
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
134
|
+
const { document } = new Happy.Window()
|
|
135
|
+
document.body.innerHTML = `<div id="app"></div>`
|
|
136
|
+
const renderResult = render(
|
|
137
|
+
<AR.StoreProvider store={silo.store}>
|
|
138
|
+
<RTR.RealtimeProvider socket={socket}>
|
|
139
|
+
<options.client />
|
|
140
|
+
</RTR.RealtimeProvider>
|
|
141
|
+
</AR.StoreProvider>,
|
|
142
|
+
{
|
|
143
|
+
container: document.querySelector(`#app`) as unknown as HTMLElement,
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
const prettyPrint = () => console.log(prettyDOM(renderResult.container))
|
|
148
|
+
|
|
149
|
+
const dispose = () => {
|
|
150
|
+
socket.disconnect()
|
|
151
|
+
Internal.clearStore(silo.store)
|
|
152
|
+
}
|
|
153
|
+
testClient.dispose = dispose
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
name,
|
|
157
|
+
silo,
|
|
158
|
+
socket,
|
|
159
|
+
renderResult,
|
|
160
|
+
prettyPrint,
|
|
161
|
+
}
|
|
123
162
|
}
|
|
163
|
+
return Object.assign(testClient, { init })
|
|
124
164
|
}
|
|
125
165
|
|
|
126
166
|
export const singleClient = (
|
|
@@ -152,7 +192,7 @@ export const multiClient = <ClientNames extends string>(
|
|
|
152
192
|
)
|
|
153
193
|
return clients
|
|
154
194
|
},
|
|
155
|
-
{} as Record<ClientNames,
|
|
195
|
+
{} as Record<ClientNames, RealtimeTestClientBuilder>,
|
|
156
196
|
)
|
|
157
197
|
|
|
158
198
|
return {
|