atom.io 0.17.0 → 0.18.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 (140) hide show
  1. package/data/dist/index.cjs +62 -40
  2. package/data/dist/index.cjs.map +1 -1
  3. package/data/dist/index.d.ts +8 -2
  4. package/data/dist/index.js +64 -42
  5. package/data/dist/index.js.map +1 -1
  6. package/data/src/dict.ts +8 -4
  7. package/data/src/join.ts +74 -33
  8. package/data/src/struct-family.ts +18 -17
  9. package/dist/chunk-OEVFAUPE.js +289 -0
  10. package/dist/chunk-OEVFAUPE.js.map +1 -0
  11. package/dist/index.cjs +4 -10
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +62 -51
  14. package/dist/index.js +5 -11
  15. package/dist/index.js.map +1 -1
  16. package/internal/dist/index.cjs +163 -64
  17. package/internal/dist/index.cjs.map +1 -1
  18. package/internal/dist/index.d.ts +94 -70
  19. package/internal/dist/index.js +155 -59
  20. package/internal/dist/index.js.map +1 -1
  21. package/internal/src/arbitrary.ts +3 -0
  22. package/internal/src/caching.ts +8 -6
  23. package/internal/src/families/find-in-store.ts +16 -0
  24. package/internal/src/get-environment-data.ts +4 -7
  25. package/internal/src/index.ts +6 -5
  26. package/internal/src/ingest-updates/ingest-transaction-update.ts +0 -1
  27. package/internal/src/selector/create-standalone-selector.ts +0 -2
  28. package/internal/src/set-state/set-atom.ts +14 -18
  29. package/internal/src/store/store.ts +14 -2
  30. package/internal/src/store/withdraw.ts +72 -2
  31. package/internal/src/subscribe/subscribe-to-timeline.ts +2 -2
  32. package/internal/src/subscribe/subscribe-to-transaction.ts +2 -2
  33. package/internal/src/timeline/create-timeline.ts +12 -1
  34. package/internal/src/transaction/act-upon-store.ts +19 -0
  35. package/internal/src/transaction/apply-transaction.ts +7 -1
  36. package/internal/src/transaction/assign-transaction-to-continuity.ts +18 -0
  37. package/internal/src/transaction/build-transaction.ts +7 -6
  38. package/internal/src/transaction/create-transaction.ts +1 -1
  39. package/internal/src/transaction/get-epoch-number.ts +40 -0
  40. package/internal/src/transaction/index.ts +10 -1
  41. package/internal/src/transaction/set-epoch-number.ts +31 -0
  42. package/introspection/dist/index.cjs.map +1 -1
  43. package/introspection/dist/index.d.ts +3 -3
  44. package/introspection/dist/index.js.map +1 -1
  45. package/introspection/src/attach-introspection-states.ts +6 -2
  46. package/introspection/src/attach-timeline-family.ts +5 -2
  47. package/introspection/src/attach-transaction-logs.ts +2 -2
  48. package/json/dist/index.d.ts +3 -1
  49. package/json/src/index.ts +6 -2
  50. package/package.json +24 -13
  51. package/react/dist/index.cjs.map +1 -1
  52. package/react/dist/index.d.ts +1 -1
  53. package/react/dist/index.js.map +1 -1
  54. package/react/src/use-json.ts +1 -1
  55. package/react-devtools/dist/index.cjs +131 -134
  56. package/react-devtools/dist/index.cjs.map +1 -1
  57. package/react-devtools/dist/index.css +2 -2
  58. package/react-devtools/dist/index.css.map +1 -1
  59. package/react-devtools/dist/index.d.ts +3 -3
  60. package/react-devtools/dist/index.js +103 -106
  61. package/react-devtools/dist/index.js.map +1 -1
  62. package/react-devtools/src/StateEditor.tsx +6 -6
  63. package/react-devtools/src/StateIndex.tsx +2 -5
  64. package/react-devtools/src/TimelineIndex.tsx +3 -3
  65. package/react-devtools/src/TransactionIndex.tsx +9 -8
  66. package/react-devtools/src/Updates.tsx +1 -1
  67. package/react-devtools/src/index.ts +4 -4
  68. package/realtime/dist/index.cjs +72 -0
  69. package/realtime/dist/index.cjs.map +1 -0
  70. package/realtime/dist/index.d.ts +39 -0
  71. package/realtime/dist/index.js +68 -0
  72. package/realtime/dist/index.js.map +1 -0
  73. package/realtime/package.json +16 -0
  74. package/realtime/src/index.ts +1 -0
  75. package/realtime/src/realtime-continuity.ts +152 -0
  76. package/realtime-client/dist/index.cjs +389 -48
  77. package/realtime-client/dist/index.cjs.map +1 -1
  78. package/realtime-client/dist/index.d.ts +16 -9
  79. package/realtime-client/dist/index.js +100 -37
  80. package/realtime-client/dist/index.js.map +1 -1
  81. package/realtime-client/src/index.ts +8 -5
  82. package/realtime-client/src/{pull-family-member.ts → pull-atom-family-member.ts} +2 -2
  83. package/realtime-client/src/{pull-state.ts → pull-atom.ts} +2 -2
  84. package/realtime-client/src/{pull-mutable-family-member.ts → pull-mutable-atom-family-member.ts} +1 -1
  85. package/realtime-client/src/{pull-mutable.ts → pull-mutable-atom.ts} +1 -1
  86. package/realtime-client/src/pull-selector-family-member.ts +42 -0
  87. package/realtime-client/src/pull-selector.ts +38 -0
  88. package/realtime-client/src/realtime-client-stores/client-main-store.ts +2 -2
  89. package/realtime-client/src/realtime-client-stores/client-sync-store.ts +7 -7
  90. package/realtime-client/src/sync-continuity.ts +321 -0
  91. package/realtime-client/src/sync-server-action.ts +18 -20
  92. package/realtime-react/dist/index.cjs +330 -15
  93. package/realtime-react/dist/index.cjs.map +1 -1
  94. package/realtime-react/dist/index.d.ts +26 -6
  95. package/realtime-react/dist/index.js +43 -12
  96. package/realtime-react/dist/index.js.map +1 -1
  97. package/realtime-react/src/index.ts +6 -3
  98. package/realtime-react/src/use-pull-atom-family-member.ts +21 -0
  99. package/realtime-react/src/{use-pull-family-member.ts → use-pull-atom.ts} +6 -5
  100. package/realtime-react/src/{use-pull-mutable.ts → use-pull-mutable-atom.ts} +4 -3
  101. package/realtime-react/src/use-pull-mutable-family-member.ts +9 -4
  102. package/realtime-react/src/use-pull-selector-family-member.ts +21 -0
  103. package/realtime-react/src/{use-pull.ts → use-pull-selector.ts} +7 -5
  104. package/realtime-react/src/use-push.ts +3 -2
  105. package/realtime-react/src/use-server-action.ts +3 -2
  106. package/realtime-react/src/use-sync-continuity.ts +12 -0
  107. package/realtime-react/src/use-sync-server-action.ts +3 -2
  108. package/realtime-server/dist/index.cjs +568 -242
  109. package/realtime-server/dist/index.cjs.map +1 -1
  110. package/realtime-server/dist/index.d.ts +124 -49
  111. package/realtime-server/dist/index.js +555 -238
  112. package/realtime-server/dist/index.js.map +1 -1
  113. package/realtime-server/src/index.ts +18 -2
  114. package/realtime-server/src/ipc-socket.ts +230 -0
  115. package/realtime-server/src/realtime-action-receiver.ts +8 -5
  116. package/realtime-server/src/realtime-action-synchronizer.ts +40 -28
  117. package/realtime-server/src/realtime-continuity-synchronizer.ts +247 -0
  118. package/realtime-server/src/realtime-family-provider.ts +30 -71
  119. package/realtime-server/src/realtime-mutable-family-provider.ts +24 -86
  120. package/realtime-server/src/realtime-server-stores/index.ts +3 -1
  121. package/realtime-server/src/realtime-server-stores/realtime-continuity-store.ts +90 -0
  122. package/realtime-server/src/realtime-server-stores/server-room-store.ts +97 -0
  123. package/realtime-server/src/realtime-server-stores/server-sync-store.ts +2 -72
  124. package/realtime-server/src/realtime-server-stores/server-user-store.ts +14 -29
  125. package/realtime-server/src/realtime-state-receiver.ts +0 -1
  126. package/realtime-testing/dist/index.cjs +28 -28
  127. package/realtime-testing/dist/index.cjs.map +1 -1
  128. package/realtime-testing/dist/index.js +28 -27
  129. package/realtime-testing/dist/index.js.map +1 -1
  130. package/realtime-testing/src/setup-realtime-test.tsx +38 -28
  131. package/src/atom.ts +49 -31
  132. package/src/logger.ts +10 -5
  133. package/src/selector.ts +44 -25
  134. package/src/subscribe.ts +2 -1
  135. package/src/timeline.ts +4 -4
  136. package/src/transaction.ts +13 -17
  137. package/src/validators.ts +15 -9
  138. package/dist/chunk-H4Q5FTPZ.js +0 -11
  139. package/dist/chunk-H4Q5FTPZ.js.map +0 -1
  140. package/internal/src/set-state/copy-mutable-in-transaction.ts +0 -19
@@ -0,0 +1,90 @@
1
+ import { selectorFamily } from "atom.io"
2
+ import type { TransactionUpdate } from "atom.io"
3
+ import type { JsonIO } from "atom.io/json"
4
+ import { SyncGroup } from "atom.io/realtime"
5
+ import { completeUpdateAtoms } from "atom.io/realtime-server"
6
+
7
+ const redactorAtoms = selectorFamily<
8
+ (update: TransactionUpdate<any>) => TransactionUpdate<any>,
9
+ { userId: string; syncGroupKey: string }
10
+ >({
11
+ key: `perspectiveRedactor`,
12
+ get:
13
+ ({ userId, syncGroupKey }) =>
14
+ ({ get, find }) => {
15
+ const syncGroup = SyncGroup.existing.get(syncGroupKey)
16
+ if (!syncGroup) {
17
+ throw new Error(
18
+ `Tried to create a synchronizer for a sync group that does not exist.`,
19
+ )
20
+ }
21
+
22
+ const userPerspectiveTokens = syncGroup.perspectives.flatMap(
23
+ ({ perspectiveAtoms, resourceAtoms }) => {
24
+ const userPerspectiveToken = find(perspectiveAtoms, userId)
25
+ const userPerspective = get(userPerspectiveToken)
26
+ const visibleTokens = [...userPerspective].map((subKey) => {
27
+ const resourceToken = find(resourceAtoms, subKey)
28
+ return resourceToken.key
29
+ })
30
+
31
+ return visibleTokens
32
+ },
33
+ )
34
+
35
+ const filterTransactionUpdate = (
36
+ visible: string[],
37
+ transactionUpdate: TransactionUpdate<any>,
38
+ ): TransactionUpdate<any> => {
39
+ const updates = transactionUpdate.updates
40
+ .filter((update) => {
41
+ if (`newValue` in update) {
42
+ return visible.includes(update.key)
43
+ }
44
+ return true
45
+ })
46
+ .map((update) => {
47
+ if (`updates` in update) {
48
+ return filterTransactionUpdate(visible, update)
49
+ }
50
+ return update
51
+ })
52
+ const filtered: TransactionUpdate<any> = {
53
+ ...transactionUpdate,
54
+ updates,
55
+ }
56
+ return filtered
57
+ }
58
+ const filter: (updates: TransactionUpdate<any>) => TransactionUpdate<any> =
59
+ (update) => {
60
+ const visibleKeys: string[] = syncGroup.globals.map(
61
+ (atomToken) => atomToken.key,
62
+ )
63
+ visibleKeys.push(...userPerspectiveTokens)
64
+ return filterTransactionUpdate(visibleKeys, update)
65
+ }
66
+ return filter
67
+ },
68
+ })
69
+ export const redactedPerspectiveUpdateSelectors = selectorFamily<
70
+ Pick<
71
+ TransactionUpdate<JsonIO>,
72
+ `epoch` | `id` | `key` | `output` | `updates`
73
+ > | null,
74
+ { userId: string; syncGroupKey: string; updateId: string }
75
+ >({
76
+ key: `redactedPerspectiveUpdate`,
77
+ get:
78
+ ({ userId, syncGroupKey, updateId }) =>
79
+ ({ get, find }) => {
80
+ const updateState = find(completeUpdateAtoms, updateId)
81
+ const update = get(updateState)
82
+ const redactorKey = { userId, syncGroupKey }
83
+ const redactorState = find(redactorAtoms, redactorKey)
84
+ const redact = get(redactorState)
85
+ if (update) {
86
+ return redact(update)
87
+ }
88
+ return null
89
+ },
90
+ })
@@ -0,0 +1,97 @@
1
+ import type { ChildProcessWithoutNullStreams } from "child_process"
2
+ import { spawn } from "child_process"
3
+
4
+ import * as AtomIO from "atom.io"
5
+ import type { Loadable } from "atom.io/data"
6
+ import { join } from "atom.io/data"
7
+ import { SetRTX } from "atom.io/transceivers/set-rtx"
8
+
9
+ export type RoomArguments =
10
+ | [script: string, options: string[]]
11
+ | [script: string]
12
+
13
+ export const roomIndex = AtomIO.atom({
14
+ key: `roomIndex`,
15
+ default: () => new SetRTX<string>(),
16
+ mutable: true,
17
+ toJson: (set) => set.toJSON(),
18
+ fromJson: (json) => SetRTX.fromJSON(json),
19
+ })
20
+
21
+ export type UserInRoomMeta = {
22
+ enteredAtEpoch: number
23
+ }
24
+ export const DEFAULT_USER_IN_ROOM_META: UserInRoomMeta = {
25
+ enteredAtEpoch: 0,
26
+ }
27
+ export const usersInRooms = join(
28
+ {
29
+ key: `usersInRooms`,
30
+ between: [`room`, `user`],
31
+ cardinality: `1:n`,
32
+ },
33
+ DEFAULT_USER_IN_ROOM_META,
34
+ )
35
+
36
+ export const roomArgumentsAtoms = AtomIO.atomFamily<RoomArguments, string>({
37
+ key: `roomArguments`,
38
+ default: [`echo Hello World!`],
39
+ })
40
+
41
+ export const roomSelectors = AtomIO.selectorFamily<
42
+ Loadable<ChildProcessWithoutNullStreams>,
43
+ string
44
+ >({
45
+ key: `room`,
46
+ get:
47
+ (roomId) =>
48
+ ({ get, find }) => {
49
+ const argumentsState = find(roomArgumentsAtoms, roomId)
50
+ const args = get(argumentsState)
51
+ const [script, options] = args
52
+ return new Promise((resolve) => {
53
+ const room = spawn(script, options, { env: process.env })
54
+ const resolver = (data: Buffer) => {
55
+ if (data.toString() === `✨`) {
56
+ room.stdout.off(`data`, resolver)
57
+ resolve(room)
58
+ }
59
+ }
60
+ room.stdout.on(`data`, resolver)
61
+ })
62
+ },
63
+ })
64
+
65
+ export const createRoomTX = AtomIO.transaction<
66
+ (
67
+ roomId: string,
68
+ script: string,
69
+ options?: string[],
70
+ ) => Loadable<ChildProcessWithoutNullStreams>
71
+ >({
72
+ key: `createRoom`,
73
+ do: ({ get, set, find }, roomId, script, options) => {
74
+ const args: RoomArguments = options ? [script, options] : [script]
75
+ const roomArgumentsState = find(roomArgumentsAtoms, roomId)
76
+ set(roomArgumentsState, args)
77
+ set(roomIndex, (s) => s.add(roomId))
78
+ const roomState = find(roomSelectors, roomId)
79
+ const room = get(roomState)
80
+ return room
81
+ },
82
+ })
83
+ export type CreateRoomIO = AtomIO.TransactionIO<typeof createRoomTX>
84
+
85
+ export const joinRoomTX = AtomIO.transaction<
86
+ (roomId: string, userId: string, enteredAtEpoch: number) => UserInRoomMeta
87
+ >({
88
+ key: `joinRoom`,
89
+ do: (transactors, roomId, userId, enteredAtEpoch) => {
90
+ const meta = { enteredAtEpoch }
91
+ usersInRooms.transact(transactors, ({ relations }) => {
92
+ relations.set(roomId, userId, meta)
93
+ })
94
+ return meta
95
+ },
96
+ })
97
+ export type JoinRoomIO = AtomIO.TransactionIO<typeof joinRoomTX>
@@ -1,7 +1,5 @@
1
1
  import type { TransactionUpdate, TransactionUpdateContent } from "atom.io"
2
2
  import { atomFamily, selectorFamily } from "atom.io"
3
- import { SyncGroup } from "~/packages/atom.io/__unstable__/realtime-server-next/create-realtime-sync-group"
4
- import { usersOfSockets } from "./server-user-store"
5
3
 
6
4
  export const completeUpdateAtoms = atomFamily<
7
5
  TransactionUpdate<any> | null,
@@ -38,78 +36,10 @@ export const redactedUpdateSelectors = selectorFamily<
38
36
  },
39
37
  })
40
38
 
41
- export const userUnacknowledgedUpdatesAtoms = atomFamily<
42
- TransactionUpdate<any>[],
39
+ export const userUnacknowledgedQueues = atomFamily<
40
+ Pick<TransactionUpdate<any>, `epoch` | `id` | `key` | `output` | `updates`>[],
43
41
  string
44
42
  >({
45
43
  key: `unacknowledgedUpdates`,
46
44
  default: () => [],
47
45
  })
48
-
49
- export const socketUnacknowledgedUpdatesSelectors = selectorFamily<
50
- TransactionUpdate<any>[],
51
- string
52
- >({
53
- key: `socketUnacknowledgedUpdates`,
54
- get:
55
- (socketId) =>
56
- ({ get, find }) => {
57
- const userKeyState = find(usersOfSockets.states.userKeyOfSocket, socketId)
58
- const userKey = get(userKeyState)
59
- if (!userKey) {
60
- return []
61
- }
62
- const unacknowledgedUpdatesState = find(
63
- userUnacknowledgedUpdatesAtoms,
64
- userKey,
65
- )
66
- const unacknowledgedUpdates = get(unacknowledgedUpdatesState)
67
- return unacknowledgedUpdates
68
- },
69
- set:
70
- (socketId) =>
71
- ({ set, get, find }, newUpdates) => {
72
- const userKeyState = find(usersOfSockets.states.userKeyOfSocket, socketId)
73
- const userKey = get(userKeyState)
74
- if (!userKey) {
75
- return
76
- }
77
- const unacknowledgedUpdatesState = find(
78
- userUnacknowledgedUpdatesAtoms,
79
- userKey,
80
- )
81
- set(unacknowledgedUpdatesState, newUpdates)
82
- },
83
- })
84
-
85
- export const userEpochAtoms = atomFamily<number | null, string>({
86
- key: `clientEpoch`,
87
- default: null,
88
- })
89
-
90
- export const socketEpochSelectors = selectorFamily<number | null, string>({
91
- key: `socketEpoch`,
92
- get:
93
- (socketId) =>
94
- ({ get, find }) => {
95
- const userKeyState = find(usersOfSockets.states.userKeyOfSocket, socketId)
96
- const userKey = get(userKeyState)
97
- if (!userKey) {
98
- return null
99
- }
100
- const userEpochState = find(userEpochAtoms, userKey)
101
- const userEpoch = get(userEpochState)
102
- return userEpoch
103
- },
104
- set:
105
- (socketId) =>
106
- ({ set, get, find }, newEpoch) => {
107
- const userKeyState = find(usersOfSockets.states.userKeyOfSocket, socketId)
108
- const userKey = get(userKeyState)
109
- if (!userKey) {
110
- return
111
- }
112
- const userEpochState = find(userEpochAtoms, userKey)
113
- set(userEpochState, newEpoch)
114
- },
115
- })
@@ -2,6 +2,20 @@ import { atom, atomFamily } from "atom.io"
2
2
  import { join } from "atom.io/data"
3
3
  import { SetRTX } from "atom.io/transceivers/set-rtx"
4
4
 
5
+ import type { Socket } from ".."
6
+
7
+ export const socketAtoms = atomFamily<Socket | null, string>({
8
+ key: `sockets`,
9
+ default: null,
10
+ })
11
+
12
+ export const socketIndex = atom({
13
+ key: `socketsIndex`,
14
+ mutable: true,
15
+ default: () => new SetRTX<string>(),
16
+ toJson: (set) => set.toJSON(),
17
+ fromJson: (json) => SetRTX.fromJSON(json),
18
+ })
5
19
  export const userIndex = atom({
6
20
  key: `usersIndex`,
7
21
  mutable: true,
@@ -14,32 +28,3 @@ export const usersOfSockets = join({
14
28
  between: [`user`, `socket`],
15
29
  cardinality: `1:1`,
16
30
  })
17
-
18
- export const roomIndex = atom({
19
- key: `conclaveIndex`,
20
- default: () => new SetRTX<string>(),
21
- mutable: true,
22
- toJson: (set) => set.toJSON(),
23
- fromJson: (json) => SetRTX.fromJSON(json),
24
- })
25
- export type UserInRoomMeta = {
26
- enteredAtEpoch: number
27
- }
28
- export const DEFAULT_USER_IN_ROOM_META: UserInRoomMeta = {
29
- enteredAtEpoch: 0,
30
- }
31
- export const usersInRooms = join(
32
- {
33
- key: `usersInRooms`,
34
- between: [`room`, `user`],
35
- cardinality: `1:n`,
36
- },
37
- DEFAULT_USER_IN_ROOM_META,
38
- )
39
-
40
- // export const roomStoreAtoms = atomFamily<
41
- // { [key: string]: any },
42
- // { conclave: string }
43
- // >({
44
-
45
- // })
@@ -1,4 +1,3 @@
1
- import { setState } from "atom.io"
2
1
  import type { WritableToken } from "atom.io"
3
2
  import { IMPLICIT, setIntoStore } from "atom.io/internal"
4
3
  import type { Json } from "atom.io/json"
@@ -3,7 +3,7 @@
3
3
  var http = require('http');
4
4
  var react = require('@testing-library/react');
5
5
  var AtomIO = require('atom.io');
6
- var Internal = require('atom.io/internal');
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
9
  var RTS = require('atom.io/realtime-server');
@@ -32,7 +32,6 @@ function _interopNamespace(e) {
32
32
 
33
33
  var http__namespace = /*#__PURE__*/_interopNamespace(http);
34
34
  var AtomIO__namespace = /*#__PURE__*/_interopNamespace(AtomIO);
35
- var Internal__namespace = /*#__PURE__*/_interopNamespace(Internal);
36
35
  var AR__namespace = /*#__PURE__*/_interopNamespace(AR);
37
36
  var RTR__namespace = /*#__PURE__*/_interopNamespace(RTR);
38
37
  var RTS__namespace = /*#__PURE__*/_interopNamespace(RTS);
@@ -61,8 +60,10 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
61
60
 
62
61
  // ../anvl/src/object/entries.ts
63
62
  var recordToEntries = (obj) => Object.entries(obj);
63
+ var testNumber = 0;
64
64
  var setupRealtimeTestServer = (options) => {
65
- const silo = new AtomIO__namespace.Silo(`SERVER`, Internal__namespace.IMPLICIT.STORE);
65
+ ++testNumber;
66
+ const silo = new AtomIO__namespace.Silo(`SERVER-${testNumber}`, internal.IMPLICIT.STORE);
66
67
  const httpServer = http__namespace.createServer((_, res) => res.end(`Hello World!`));
67
68
  const address = httpServer.listen().address();
68
69
  const port = typeof address === `string` ? 80 : address === null ? null : address.port;
@@ -71,26 +72,12 @@ var setupRealtimeTestServer = (options) => {
71
72
  const server = new SocketIO__namespace.Server(httpServer).use((socket, next) => {
72
73
  const { token, username } = socket.handshake.auth;
73
74
  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
- Internal__namespace.setIntoStore(
85
- socketRelatedKeysState,
86
- (keys) => (keys.clear(), keys.add(username)),
87
- silo.store
88
- );
89
- Internal__namespace.setIntoStore(
90
- clientRelatedKeysState,
91
- (keys) => (keys.clear(), keys.add(socket.id)),
92
- silo.store
93
- );
75
+ const socketState = internal.findInStore(RTS__namespace.socketAtoms, socket.id, silo.store);
76
+ internal.setIntoStore(socketState, socket, silo.store);
77
+ const usersOfSockets2 = RTS__namespace.usersOfSockets.in(silo.store);
78
+ usersOfSockets2.relations.set(socket.id, username);
79
+ internal.setIntoStore(RTS__namespace.userIndex, (index) => index.add(username), silo.store);
80
+ internal.setIntoStore(RTS__namespace.socketIndex, (index) => index.add(socket.id), silo.store);
94
81
  console.log(`${username} connected on ${socket.id}`);
95
82
  next();
96
83
  } else {
@@ -102,7 +89,15 @@ var setupRealtimeTestServer = (options) => {
102
89
  });
103
90
  const dispose = () => {
104
91
  server.close();
105
- Internal__namespace.clearStore(silo.store);
92
+ const roomKeys = internal.getFromStore(RTS__namespace.roomIndex, silo.store);
93
+ for (const roomKey of roomKeys) {
94
+ const roomState = internal.findInStore(RTS__namespace.roomSelectors, roomKey, silo.store);
95
+ const room = internal.getFromStore(roomState, silo.store);
96
+ if (room && !(room instanceof Promise)) {
97
+ room.kill();
98
+ }
99
+ }
100
+ silo.store.valueMap.clear();
106
101
  };
107
102
  return {
108
103
  name: `SERVER`,
@@ -116,9 +111,9 @@ var setupRealtimeTestClient = (options, name, port) => {
116
111
  } };
117
112
  const init = () => {
118
113
  const socket = socket_ioClient.io(`http://localhost:${port}/`, {
119
- auth: { token: `test`, username: name }
114
+ auth: { token: `test`, username: `${name}-${testNumber}` }
120
115
  });
121
- const silo = new AtomIO__namespace.Silo(name, Internal__namespace.IMPLICIT.STORE);
116
+ const silo = new AtomIO__namespace.Silo(name, internal.IMPLICIT.STORE);
122
117
  for (const [key, value] of silo.store.valueMap.entries()) {
123
118
  if (Array.isArray(value)) {
124
119
  silo.store.valueMap.set(key, [...value]);
@@ -134,8 +129,9 @@ var setupRealtimeTestClient = (options, name, port) => {
134
129
  );
135
130
  const prettyPrint = () => console.log(react.prettyDOM(renderResult.container));
136
131
  const dispose = () => {
132
+ renderResult.unmount();
137
133
  socket.disconnect();
138
- Internal__namespace.clearStore(silo.store);
134
+ internal.clearStore(silo.store);
139
135
  };
140
136
  testClient.dispose = dispose;
141
137
  return {
@@ -150,7 +146,11 @@ var setupRealtimeTestClient = (options, name, port) => {
150
146
  };
151
147
  var singleClient = (options) => {
152
148
  const server = setupRealtimeTestServer(options);
153
- const client = setupRealtimeTestClient(options, `CLIENT`, server.port);
149
+ const client = setupRealtimeTestClient(
150
+ options,
151
+ `CLIENT-${testNumber}`,
152
+ server.port
153
+ );
154
154
  return {
155
155
  client,
156
156
  server,
@@ -1 +1 @@
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\tInternal.setIntoStore(\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\tInternal.setIntoStore(\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"]}
1
+ {"version":3,"sources":["../src/setup-realtime-test.tsx","../../../anvl/src/object/entries.ts"],"names":["usersOfSockets","clients"],"mappings":"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","sourcesContent":["import * as http from \"http\"\n\nimport { prettyDOM, render } from \"@testing-library/react\"\nimport type { RenderResult } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport {\n\tIMPLICIT,\n\tclearStore,\n\tfindInStore,\n\tgetFromStore,\n\tsetIntoStore,\n} 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\nlet testNumber = 0\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\t++testNumber\n\tconst silo = new AtomIO.Silo(`SERVER-${testNumber}`, 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\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 socketState = findInStore(RTS.socketAtoms, socket.id, silo.store)\n\t\t\tsetIntoStore(socketState, socket, silo.store)\n\t\t\tconst usersOfSockets = RTS.usersOfSockets.in(silo.store)\n\t\t\tusersOfSockets.relations.set(socket.id, username)\n\t\t\tsetIntoStore(RTS.userIndex, (index) => index.add(username), silo.store)\n\t\t\tsetIntoStore(RTS.socketIndex, (index) => index.add(socket.id), silo.store)\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\tconst roomKeys = getFromStore(RTS.roomIndex, silo.store)\n\t\tfor (const roomKey of roomKeys) {\n\t\t\tconst roomState = findInStore(RTS.roomSelectors, roomKey, silo.store)\n\t\t\tconst room = getFromStore(roomState, silo.store)\n\t\t\tif (room && !(room instanceof Promise)) {\n\t\t\t\troom.kill()\n\t\t\t}\n\t\t}\n\t\tsilo.store.valueMap.clear()\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}-${testNumber}` },\n\t\t})\n\t\tconst silo = new AtomIO.Silo(name, 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\trenderResult.unmount()\n\t\t\tsocket.disconnect()\n\t\t\tclearStore(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(\n\t\toptions,\n\t\t`CLIENT-${testNumber}`,\n\t\tserver.port,\n\t)\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"]}
@@ -3,7 +3,7 @@ import { __spreadProps, __spreadValues } from '../../dist/chunk-PZLG2HP3.js';
3
3
  import * as http from 'http';
4
4
  import { render, prettyDOM } from '@testing-library/react';
5
5
  import * as AtomIO from 'atom.io';
6
- import * as Internal from 'atom.io/internal';
6
+ import { IMPLICIT, findInStore, setIntoStore, getFromStore, clearStore } from 'atom.io/internal';
7
7
  import * as AR from 'atom.io/react';
8
8
  import * as RTR from 'atom.io/realtime-react';
9
9
  import * as RTS from 'atom.io/realtime-server';
@@ -12,8 +12,10 @@ import * as SocketIO from 'socket.io';
12
12
  import { io } from 'socket.io-client';
13
13
  import { jsx } from 'react/jsx-runtime';
14
14
 
15
+ var testNumber = 0;
15
16
  var setupRealtimeTestServer = (options) => {
16
- const silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE);
17
+ ++testNumber;
18
+ const silo = new AtomIO.Silo(`SERVER-${testNumber}`, IMPLICIT.STORE);
17
19
  const httpServer = http.createServer((_, res) => res.end(`Hello World!`));
18
20
  const address = httpServer.listen().address();
19
21
  const port = typeof address === `string` ? 80 : address === null ? null : address.port;
@@ -22,26 +24,12 @@ var setupRealtimeTestServer = (options) => {
22
24
  const server = new SocketIO.Server(httpServer).use((socket, next) => {
23
25
  const { token, username } = socket.handshake.auth;
24
26
  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
- Internal.setIntoStore(
36
- socketRelatedKeysState,
37
- (keys) => (keys.clear(), keys.add(username)),
38
- silo.store
39
- );
40
- Internal.setIntoStore(
41
- clientRelatedKeysState,
42
- (keys) => (keys.clear(), keys.add(socket.id)),
43
- silo.store
44
- );
27
+ const socketState = findInStore(RTS.socketAtoms, socket.id, silo.store);
28
+ setIntoStore(socketState, socket, silo.store);
29
+ const usersOfSockets2 = RTS.usersOfSockets.in(silo.store);
30
+ usersOfSockets2.relations.set(socket.id, username);
31
+ setIntoStore(RTS.userIndex, (index) => index.add(username), silo.store);
32
+ setIntoStore(RTS.socketIndex, (index) => index.add(socket.id), silo.store);
45
33
  console.log(`${username} connected on ${socket.id}`);
46
34
  next();
47
35
  } else {
@@ -53,7 +41,15 @@ var setupRealtimeTestServer = (options) => {
53
41
  });
54
42
  const dispose = () => {
55
43
  server.close();
56
- Internal.clearStore(silo.store);
44
+ const roomKeys = getFromStore(RTS.roomIndex, silo.store);
45
+ for (const roomKey of roomKeys) {
46
+ const roomState = findInStore(RTS.roomSelectors, roomKey, silo.store);
47
+ const room = getFromStore(roomState, silo.store);
48
+ if (room && !(room instanceof Promise)) {
49
+ room.kill();
50
+ }
51
+ }
52
+ silo.store.valueMap.clear();
57
53
  };
58
54
  return {
59
55
  name: `SERVER`,
@@ -67,9 +63,9 @@ var setupRealtimeTestClient = (options, name, port) => {
67
63
  } };
68
64
  const init = () => {
69
65
  const socket = io(`http://localhost:${port}/`, {
70
- auth: { token: `test`, username: name }
66
+ auth: { token: `test`, username: `${name}-${testNumber}` }
71
67
  });
72
- const silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE);
68
+ const silo = new AtomIO.Silo(name, IMPLICIT.STORE);
73
69
  for (const [key, value] of silo.store.valueMap.entries()) {
74
70
  if (Array.isArray(value)) {
75
71
  silo.store.valueMap.set(key, [...value]);
@@ -85,8 +81,9 @@ var setupRealtimeTestClient = (options, name, port) => {
85
81
  );
86
82
  const prettyPrint = () => console.log(prettyDOM(renderResult.container));
87
83
  const dispose = () => {
84
+ renderResult.unmount();
88
85
  socket.disconnect();
89
- Internal.clearStore(silo.store);
86
+ clearStore(silo.store);
90
87
  };
91
88
  testClient.dispose = dispose;
92
89
  return {
@@ -101,7 +98,11 @@ var setupRealtimeTestClient = (options, name, port) => {
101
98
  };
102
99
  var singleClient = (options) => {
103
100
  const server = setupRealtimeTestServer(options);
104
- const client = setupRealtimeTestClient(options, `CLIENT`, server.port);
101
+ const client = setupRealtimeTestClient(
102
+ options,
103
+ `CLIENT-${testNumber}`,
104
+ server.port
105
+ );
105
106
  return {
106
107
  client,
107
108
  server,
@@ -1 +1 @@
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\tInternal.setIntoStore(\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\tInternal.setIntoStore(\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"]}
1
+ {"version":3,"sources":["../src/setup-realtime-test.tsx"],"names":["usersOfSockets","clients"],"mappings":"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","sourcesContent":["import * as http from \"http\"\n\nimport { prettyDOM, render } from \"@testing-library/react\"\nimport type { RenderResult } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport {\n\tIMPLICIT,\n\tclearStore,\n\tfindInStore,\n\tgetFromStore,\n\tsetIntoStore,\n} 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\nlet testNumber = 0\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\t++testNumber\n\tconst silo = new AtomIO.Silo(`SERVER-${testNumber}`, 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\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 socketState = findInStore(RTS.socketAtoms, socket.id, silo.store)\n\t\t\tsetIntoStore(socketState, socket, silo.store)\n\t\t\tconst usersOfSockets = RTS.usersOfSockets.in(silo.store)\n\t\t\tusersOfSockets.relations.set(socket.id, username)\n\t\t\tsetIntoStore(RTS.userIndex, (index) => index.add(username), silo.store)\n\t\t\tsetIntoStore(RTS.socketIndex, (index) => index.add(socket.id), silo.store)\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\tconst roomKeys = getFromStore(RTS.roomIndex, silo.store)\n\t\tfor (const roomKey of roomKeys) {\n\t\t\tconst roomState = findInStore(RTS.roomSelectors, roomKey, silo.store)\n\t\t\tconst room = getFromStore(roomState, silo.store)\n\t\t\tif (room && !(room instanceof Promise)) {\n\t\t\t\troom.kill()\n\t\t\t}\n\t\t}\n\t\tsilo.store.valueMap.clear()\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}-${testNumber}` },\n\t\t})\n\t\tconst silo = new AtomIO.Silo(name, 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\trenderResult.unmount()\n\t\t\tsocket.disconnect()\n\t\t\tclearStore(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(\n\t\toptions,\n\t\t`CLIENT-${testNumber}`,\n\t\tserver.port,\n\t)\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"]}