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
@@ -1,58 +1,74 @@
1
- import * as AtomIO from "atom.io"
1
+ import type * as AtomIO from "atom.io"
2
2
  import {
3
3
  IMPLICIT,
4
+ actUponStore,
5
+ assignTransactionToContinuity,
4
6
  findInStore,
5
7
  getFromStore,
6
8
  setIntoStore,
7
9
  subscribeToTransaction,
8
10
  } from "atom.io/internal"
11
+ import type { Json, JsonIO } from "atom.io/json"
9
12
 
10
13
  import type { ServerConfig } from "."
11
- import { usersOfSockets } from "./realtime-server-stores"
12
14
  import {
13
15
  completeUpdateAtoms,
14
16
  redactedUpdateSelectors,
15
- socketEpochSelectors,
16
- socketUnacknowledgedUpdatesSelectors,
17
17
  transactionRedactorAtoms,
18
- } from "./realtime-server-stores/server-sync-store"
18
+ userUnacknowledgedQueues,
19
+ usersOfSockets,
20
+ } from "./realtime-server-stores"
19
21
 
20
22
  export type ActionSynchronizer = ReturnType<typeof realtimeActionSynchronizer>
21
23
  export function realtimeActionSynchronizer({
22
24
  socket,
23
25
  store = IMPLICIT.STORE,
24
26
  }: ServerConfig) {
25
- return function actionSynchronizer<ƒ extends AtomIO.ƒn>(
27
+ return function actionSynchronizer<ƒ extends JsonIO>(
26
28
  tx: AtomIO.TransactionToken<ƒ>,
27
29
  filter?: (
28
30
  update: AtomIO.TransactionUpdateContent[],
29
31
  ) => AtomIO.TransactionUpdateContent[],
30
32
  ): () => void {
33
+ assignTransactionToContinuity(`default`, tx.key, store)
34
+
31
35
  const userKeyState = findInStore(
32
36
  usersOfSockets.states.userKeyOfSocket,
33
37
  socket.id,
34
38
  store,
35
39
  )
36
40
  const userKey = getFromStore(userKeyState, store)
37
- const socketUnacknowledgedUpdatesState = findInStore(
38
- socketUnacknowledgedUpdatesSelectors,
39
- socket.id,
41
+ if (!userKey) {
42
+ store.logger.error(
43
+ `❌`,
44
+ `transaction`,
45
+ tx.key,
46
+ `Tried to create a synchronizer for a socket (${socket.id}) that is not connected to a user.`,
47
+ )
48
+ return () => {}
49
+ }
50
+ const userUnacknowledgedQueue = findInStore(
51
+ userUnacknowledgedQueues,
52
+ userKey,
40
53
  store,
41
54
  )
42
- const socketUnacknowledgedUpdates = getFromStore(
43
- socketUnacknowledgedUpdatesState,
55
+ const userUnacknowledgedUpdates = getFromStore(
56
+ userUnacknowledgedQueue,
44
57
  store,
45
58
  )
46
59
  if (filter) {
47
60
  const redactorState = findInStore(transactionRedactorAtoms, tx.key, store)
48
61
  setIntoStore(redactorState, { filter }, store)
49
62
  }
50
- const fillTransactionRequest = (update: AtomIO.TransactionUpdate<ƒ>) => {
63
+
64
+ const fillTransactionRequest = (
65
+ update: Pick<AtomIO.TransactionUpdate<ƒ>, `id` | `params`>,
66
+ ) => {
51
67
  const performanceKey = `tx-run:${tx.key}:${update.id}`
52
68
  const performanceKeyStart = `${performanceKey}:start`
53
69
  const performanceKeyEnd = `${performanceKey}:end`
54
70
  performance.mark(performanceKeyStart)
55
- AtomIO.runTransaction<ƒ>(tx, update.id, store)(...update.params)
71
+ actUponStore<ƒ>(tx, update.id, store)(...update.params)
56
72
  performance.mark(performanceKeyEnd)
57
73
  const metric = performance.measure(
58
74
  performanceKey,
@@ -71,7 +87,10 @@ export function realtimeActionSynchronizer({
71
87
  (update) => {
72
88
  const updateState = findInStore(completeUpdateAtoms, update.id, store)
73
89
  setIntoStore(updateState, update, store)
74
- const toEmit = filter
90
+ const toEmit: Pick<
91
+ AtomIO.TransactionUpdate<ƒ>,
92
+ `epoch` | `id` | `key` | `output` | `updates`
93
+ > | null = filter
75
94
  ? getFromStore(
76
95
  findInStore(redactedUpdateSelectors, [tx.key, update.id], store),
77
96
  store,
@@ -83,7 +102,7 @@ export function realtimeActionSynchronizer({
83
102
  //
84
103
  // we need a client session that can persist between disconnects
85
104
  setIntoStore(
86
- socketUnacknowledgedUpdatesState,
105
+ userUnacknowledgedQueue,
87
106
  (updates) => {
88
107
  if (toEmit) {
89
108
  updates.push(toEmit)
@@ -94,7 +113,7 @@ export function realtimeActionSynchronizer({
94
113
  store,
95
114
  )
96
115
 
97
- socket.emit(`tx-new:${tx.key}`, toEmit)
116
+ socket.emit(`tx-new:${tx.key}`, toEmit as Json.Serializable)
98
117
  },
99
118
  `tx-sub:${tx.key}:${socket.id}`,
100
119
  store,
@@ -106,10 +125,9 @@ export function realtimeActionSynchronizer({
106
125
  let i = 1
107
126
  let next = 1
108
127
  const retry = setInterval(() => {
109
- const toEmit = socketUnacknowledgedUpdates[0]
110
- console.log(userKey, socketUnacknowledgedUpdates)
128
+ const toEmit = userUnacknowledgedUpdates[0]
111
129
  if (toEmit && i === next) {
112
- socket.emit(`tx-new:${tx.key}`, toEmit)
130
+ socket.emit(`tx-new:${tx.key}`, toEmit as Json.Serializable)
113
131
  next *= 2
114
132
  }
115
133
 
@@ -119,16 +137,10 @@ export function realtimeActionSynchronizer({
119
137
  const trackClientAcknowledgement = (epoch: number) => {
120
138
  i = 1
121
139
  next = 1
122
- const socketEpochState = findInStore(
123
- socketEpochSelectors,
124
- socket.id,
125
- store,
126
- )
127
-
128
- setIntoStore(socketEpochState, epoch, store)
129
- if (socketUnacknowledgedUpdates[0]?.epoch === epoch) {
140
+ 8
141
+ if (userUnacknowledgedUpdates[0]?.epoch === epoch) {
130
142
  setIntoStore(
131
- socketUnacknowledgedUpdatesState,
143
+ userUnacknowledgedQueue,
132
144
  (updates) => {
133
145
  updates.shift()
134
146
  return updates
@@ -0,0 +1,247 @@
1
+ import type * as AtomIO from "atom.io"
2
+ import {
3
+ IMPLICIT,
4
+ actUponStore,
5
+ findInStore,
6
+ getFromStore,
7
+ isRootStore,
8
+ setIntoStore,
9
+ subscribeToState,
10
+ subscribeToTransaction,
11
+ } from "atom.io/internal"
12
+ import type { Json, JsonIO } from "atom.io/json"
13
+ import type { ContinuityToken } from "atom.io/realtime"
14
+
15
+ import type { ServerConfig, Socket } from "."
16
+ import { socketAtoms, usersOfSockets } from "."
17
+ import { redactedPerspectiveUpdateSelectors } from "./realtime-server-stores"
18
+ import {
19
+ completeUpdateAtoms,
20
+ userUnacknowledgedQueues,
21
+ } from "./realtime-server-stores"
22
+
23
+ export type RealtimeContinuitySynchronizer = ReturnType<
24
+ typeof realtimeContinuitySynchronizer
25
+ >
26
+ export function realtimeContinuitySynchronizer({
27
+ socket: initialSocket,
28
+ store = IMPLICIT.STORE,
29
+ }: ServerConfig) {
30
+ return function synchronizer(continuity: ContinuityToken): () => void {
31
+ let socket: Socket | null = initialSocket
32
+
33
+ const continuityKey = continuity.key
34
+ const userKeyState = findInStore(
35
+ usersOfSockets.states.userKeyOfSocket,
36
+ socket.id,
37
+ store,
38
+ )
39
+ const userKey = getFromStore(userKeyState, store)
40
+ if (!userKey) {
41
+ store.logger.error(
42
+ `❌`,
43
+ `continuity`,
44
+ continuityKey,
45
+ `Tried to create a synchronizer for a socket (${socket.id}) that is not connected to a user.`,
46
+ )
47
+ return () => {}
48
+ }
49
+ const socketKeyState = findInStore(
50
+ usersOfSockets.states.socketKeyOfUser,
51
+ userKey,
52
+ store,
53
+ )
54
+ subscribeToState(
55
+ socketKeyState,
56
+ ({ newValue: newSocketKey }) => {
57
+ store.logger.info(
58
+ `👋`,
59
+ `continuity`,
60
+ continuityKey,
61
+ `seeing ${userKey} on new socket ${newSocketKey}`,
62
+ )
63
+ if (newSocketKey === null) {
64
+ store.logger.error(
65
+ `❌`,
66
+ `continuity`,
67
+ continuityKey,
68
+ `Tried to create a synchronizer for a user (${userKey}) that is not connected to a socket.`,
69
+ )
70
+ return
71
+ }
72
+ const newSocketState = findInStore(socketAtoms, newSocketKey, store)
73
+ const newSocket = getFromStore(newSocketState, store)
74
+ socket = newSocket
75
+ },
76
+ `sync-continuity:${continuityKey}:${userKey}`,
77
+ store,
78
+ )
79
+
80
+ const userUnacknowledgedQueue = findInStore(
81
+ userUnacknowledgedQueues,
82
+ userKey,
83
+ store,
84
+ )
85
+ const userUnacknowledgedUpdates = getFromStore(
86
+ userUnacknowledgedQueue,
87
+ store,
88
+ )
89
+ const unsubscribeFunctions: (() => void)[] = []
90
+
91
+ const sendInitialPayload = () => {
92
+ const initialPayload: Json.Serializable[] = []
93
+ for (const atom of continuity.globals) {
94
+ initialPayload.push(atom, getFromStore(atom, store))
95
+ }
96
+ for (const { perspectiveAtoms } of continuity.perspectives) {
97
+ const perspectiveTokensState = findInStore(
98
+ perspectiveAtoms,
99
+ userKey,
100
+ store,
101
+ )
102
+ const perspectiveTokens = getFromStore(perspectiveTokensState, store)
103
+ for (const perspectiveToken of perspectiveTokens) {
104
+ const resource = getFromStore(perspectiveToken, store)
105
+ initialPayload.push(perspectiveToken, resource)
106
+ }
107
+ }
108
+
109
+ const epoch = isRootStore(store)
110
+ ? store.transactionMeta.epoch.get(continuityKey) ?? null
111
+ : null
112
+ socket?.emit(`continuity-init:${continuityKey}`, epoch, initialPayload)
113
+
114
+ for (const transaction of continuity.actions) {
115
+ const unsubscribeFromTransaction = subscribeToTransaction(
116
+ transaction,
117
+ (update) => {
118
+ // store.logger.info(`userId`, userKey)
119
+ const updateState = findInStore(
120
+ completeUpdateAtoms,
121
+ update.id,
122
+ store,
123
+ )
124
+ setIntoStore(updateState, update, store)
125
+ const redactedUpdateKey = {
126
+ userId: userKey,
127
+ syncGroupKey: continuityKey,
128
+ updateId: update.id,
129
+ }
130
+ const redactedUpdateState = findInStore(
131
+ redactedPerspectiveUpdateSelectors,
132
+ redactedUpdateKey,
133
+ store,
134
+ )
135
+
136
+ const redactedUpdate = getFromStore(redactedUpdateState, store)
137
+
138
+ setIntoStore(
139
+ userUnacknowledgedQueue,
140
+ (updates) => {
141
+ if (redactedUpdate) {
142
+ updates.push(redactedUpdate)
143
+ updates.sort((a, b) => a.epoch - b.epoch)
144
+ }
145
+ return updates
146
+ },
147
+ store,
148
+ )
149
+
150
+ socket?.emit(
151
+ `tx-new:${continuityKey}`,
152
+ redactedUpdate as Json.Serializable,
153
+ )
154
+ },
155
+ `sync-continuity:${continuityKey}:${userKey}`,
156
+ store,
157
+ )
158
+ unsubscribeFunctions.push(unsubscribeFromTransaction)
159
+ }
160
+ }
161
+ socket.off(`get:${continuityKey}`, sendInitialPayload)
162
+ socket.on(`get:${continuityKey}`, sendInitialPayload)
163
+
164
+ const fillTransactionRequest = (
165
+ update: Pick<AtomIO.TransactionUpdate<JsonIO>, `id` | `key` | `params`>,
166
+ ) => {
167
+ const transactionKey = update.key
168
+ const updateId = update.id
169
+ const performanceKey = `tx-run:${transactionKey}:${updateId}`
170
+ const performanceKeyStart = `${performanceKey}:start`
171
+ const performanceKeyEnd = `${performanceKey}:end`
172
+ performance.mark(performanceKeyStart)
173
+ actUponStore(
174
+ { type: `transaction`, key: transactionKey },
175
+ updateId,
176
+ store,
177
+ )(...update.params)
178
+ performance.mark(performanceKeyEnd)
179
+ const metric = performance.measure(
180
+ performanceKey,
181
+ performanceKeyStart,
182
+ performanceKeyEnd,
183
+ )
184
+ store?.logger.info(
185
+ `🚀`,
186
+ `transaction`,
187
+ transactionKey,
188
+ updateId,
189
+ metric.duration,
190
+ )
191
+ }
192
+ socket.off(`tx-run:${continuityKey}`, fillTransactionRequest)
193
+ socket.on(`tx-run:${continuityKey}`, fillTransactionRequest)
194
+
195
+ let i = 1
196
+ let next = 1
197
+ const retry = setInterval(() => {
198
+ const toEmit = userUnacknowledgedUpdates[0]
199
+ store.logger.info(
200
+ `🔄`,
201
+ `continuity`,
202
+ continuityKey,
203
+ `${store.config.name} retrying ${userKey} (${i}/${next})`,
204
+ socket?.id,
205
+ userUnacknowledgedUpdates,
206
+ )
207
+ if (toEmit && i === next) {
208
+ socket?.emit(`tx-new:${continuityKey}`, toEmit as Json.Serializable)
209
+ next *= 2
210
+ }
211
+
212
+ i++
213
+ }, 250)
214
+ const trackClientAcknowledgement = (epoch: number) => {
215
+ store.logger.info(
216
+ `👍`,
217
+ `continuity`,
218
+ continuityKey,
219
+ `${userKey} acknowledged epoch ${epoch}`,
220
+ )
221
+ i = 1
222
+ next = 1
223
+ const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch
224
+
225
+ if (isUnacknowledged) {
226
+ setIntoStore(
227
+ userUnacknowledgedQueue,
228
+ (updates) => {
229
+ updates.shift()
230
+ return updates
231
+ },
232
+ store,
233
+ )
234
+ }
235
+ }
236
+ socket.off(`ack:${continuityKey}`, trackClientAcknowledgement)
237
+ socket.on(`ack:${continuityKey}`, trackClientAcknowledgement)
238
+
239
+ return () => {
240
+ clearInterval(retry)
241
+ for (const unsubscribe of unsubscribeFunctions) unsubscribe()
242
+ socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement)
243
+ socket?.off(`get:${continuityKey}`, sendInitialPayload)
244
+ socket?.off(`tx-run:${continuityKey}`, fillTransactionRequest)
245
+ }
246
+ }
247
+ }
@@ -5,13 +5,12 @@ import {
5
5
  getFromStore,
6
6
  subscribeToState,
7
7
  } from "atom.io/internal"
8
- import type { Json } from "atom.io/json"
9
- import { parseJson } from "atom.io/json"
8
+ import { type Json, stringifyJson } from "atom.io/json"
10
9
 
11
10
  import type { ServerConfig } from "."
12
11
 
13
- export type FamilyProvider = ReturnType<typeof realtimeFamilyProvider>
14
- export function realtimeFamilyProvider({
12
+ export type FamilyProvider = ReturnType<typeof realtimeAtomFamilyProvider>
13
+ export function realtimeAtomFamilyProvider({
15
14
  socket,
16
15
  store = IMPLICIT.STORE,
17
16
  }: ServerConfig) {
@@ -19,77 +18,40 @@ export function realtimeFamilyProvider({
19
18
  J extends Json.Serializable,
20
19
  K extends Json.Serializable,
21
20
  >(
22
- family: AtomIO.RegularAtomFamily<J, K>,
21
+ family: AtomIO.RegularAtomFamilyToken<J, K>,
23
22
  index: AtomIO.ReadableToken<Iterable<K>>,
24
23
  ): () => void {
25
- const unsubSingleCallbacksByKey = new Map<string, () => void>()
26
- const unsubFamilyCallbacksByKey = new Map<string, () => void>()
24
+ const unsubCallbacksByKey = new Map<string, () => void>()
27
25
 
28
- const fillFamilyUnsubRequest = () => {
29
- for (const [, unsub] of unsubFamilyCallbacksByKey) {
30
- unsub()
31
- }
32
- unsubFamilyCallbacksByKey.clear()
33
- socket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)
34
- }
35
-
36
- const fillSingleUnsubRequest = (key: string) => {
37
- socket.off(`unsub:${key}`, fillSingleUnsubRequest)
38
- const unsub = unsubSingleCallbacksByKey.get(key)
26
+ const fillUnsubRequest = (key: string) => {
27
+ socket.off(`unsub:${key}`, fillUnsubRequest)
28
+ const unsub = unsubCallbacksByKey.get(key)
39
29
  if (unsub) {
40
30
  unsub()
41
- unsubSingleCallbacksByKey.delete(key)
31
+ unsubCallbacksByKey.delete(key)
42
32
  }
43
33
  }
44
34
 
45
- const fillSubRequest = (subKey?: K) => {
46
- if (subKey === undefined) {
47
- const keys = getFromStore(index, store)
48
- for (const key of keys) {
49
- const token = findInStore(family, key, store)
50
- socket.emit(
51
- `serve:${family.key}`,
52
- parseJson(token.family?.subKey || `null`),
53
- getFromStore(token, store),
35
+ const fillSubRequest = (subKey: K) => {
36
+ const exposedSubKeys = getFromStore(index, store)
37
+ for (const exposedSubKey of exposedSubKeys) {
38
+ if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) {
39
+ const token = findInStore(family, subKey, store)
40
+ socket.emit(`serve:${token.key}`, getFromStore(token, store))
41
+ const unsubscribe = subscribeToState(
42
+ token,
43
+ ({ newValue }) => {
44
+ socket.emit(`serve:${token.key}`, newValue)
45
+ },
46
+ `expose-family:${family.key}:${socket.id}`,
47
+ store,
54
48
  )
49
+ unsubCallbacksByKey.set(token.key, unsubscribe)
50
+ socket.on(`unsub:${token.key}`, () => {
51
+ fillUnsubRequest(token.key)
52
+ })
53
+ break
55
54
  }
56
-
57
- const unsubscribeFromTokenCreation = family.subject.subscribe(
58
- `expose-family:${socket.id}`,
59
- (token: AtomIO.WritableToken<J>) => {
60
- const unsub = subscribeToState(
61
- token,
62
- ({ newValue }) => {
63
- socket.emit(
64
- `serve:${family.key}`,
65
- parseJson(token.family?.subKey || `null`),
66
- newValue,
67
- )
68
- },
69
- `expose-family:${family.key}:${socket.id}`,
70
- store,
71
- )
72
- unsubFamilyCallbacksByKey.set(token.key, unsub)
73
- },
74
- )
75
- unsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)
76
-
77
- socket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)
78
- } else {
79
- const token = family(subKey)
80
- socket.emit(`serve:${token.key}`, getFromStore(token, store))
81
- const unsubscribe = subscribeToState(
82
- token,
83
- ({ newValue }) => {
84
- socket.emit(`serve:${token.key}`, newValue)
85
- },
86
- `expose-family:${family.key}:${socket.id}`,
87
- store,
88
- )
89
- unsubSingleCallbacksByKey.set(token.key, unsubscribe)
90
- socket.on(`unsub:${token.key}`, () => {
91
- fillSingleUnsubRequest(token.key)
92
- })
93
55
  }
94
56
  }
95
57
 
@@ -97,14 +59,11 @@ export function realtimeFamilyProvider({
97
59
 
98
60
  return () => {
99
61
  socket.off(`sub:${family.key}`, fillSubRequest)
100
- for (const [, unsub] of unsubFamilyCallbacksByKey) {
101
- unsub()
102
- }
103
- for (const [, unsub] of unsubSingleCallbacksByKey) {
62
+
63
+ for (const [, unsub] of unsubCallbacksByKey) {
104
64
  unsub()
105
65
  }
106
- unsubFamilyCallbacksByKey.clear()
107
- unsubSingleCallbacksByKey.clear()
66
+ unsubCallbacksByKey.clear()
108
67
  }
109
68
  }
110
69
  }
@@ -9,7 +9,7 @@ import {
9
9
  subscribeToState,
10
10
  } from "atom.io/internal"
11
11
  import type { Json } from "atom.io/json"
12
- import { parseJson } from "atom.io/json"
12
+ import { stringifyJson } from "atom.io/json"
13
13
 
14
14
  import type { ServerConfig } from "."
15
15
 
@@ -25,100 +25,42 @@ export function realtimeMutableFamilyProvider({
25
25
  J extends Json.Serializable,
26
26
  K extends Json.Serializable,
27
27
  >(
28
- family: AtomIO.MutableAtomFamily<T, J, K>,
28
+ family: AtomIO.MutableAtomFamilyToken<T, J, K>,
29
29
  index: AtomIO.ReadableToken<Iterable<K>>,
30
30
  ): () => void {
31
- const unsubSingleCallbacksByKey = new Map<string, () => void>()
32
- const unsubFamilyCallbacksByKey = new Map<string, () => void>()
31
+ const unsubCallbacksByKey = new Map<string, () => void>()
33
32
 
34
- const fillFamilyUnsubRequest = () => {
35
- for (const [, unsub] of unsubFamilyCallbacksByKey) {
36
- unsub()
37
- }
38
- unsubFamilyCallbacksByKey.clear()
39
- socket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)
40
- }
41
-
42
- const fillSingleUnsubRequest = (key: string) => {
43
- socket.off(`unsub:${key}`, fillSingleUnsubRequest)
44
- const unsub = unsubSingleCallbacksByKey.get(key)
33
+ const fillUnsubRequest = (key: string) => {
34
+ socket.off(`unsub:${key}`, fillUnsubRequest)
35
+ const unsub = unsubCallbacksByKey.get(key)
45
36
  if (unsub) {
46
37
  unsub()
47
- unsubSingleCallbacksByKey.delete(key)
38
+ unsubCallbacksByKey.delete(key)
48
39
  }
49
40
  }
50
41
 
51
- const fillSubRequest = (subKey?: K) => {
52
- if (subKey === undefined) {
53
- const keys = getFromStore(index, store)
54
- for (const key of keys) {
55
- const token = findInStore(family, key, store)
42
+ const fillSubRequest = (subKey: K) => {
43
+ const exposedSubKeys = getFromStore(index, store)
44
+ for (const exposedSubKey of exposedSubKeys) {
45
+ if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) {
46
+ const token = findInStore(family, subKey, store)
56
47
  const jsonToken = getJsonToken(token)
57
- const trackerToken = getUpdateToken(token)
58
- socket.emit(
59
- `init:${family.key}`,
60
- parseJson(jsonToken.family?.subKey || `null`),
61
- getFromStore(jsonToken, store),
62
- )
63
- const unsubFromUpdates = subscribeToState(
64
- trackerToken,
48
+ const updateToken = getUpdateToken(token)
49
+ socket.emit(`init:${token.key}`, getFromStore(jsonToken, store))
50
+ const unsubscribe = subscribeToState(
51
+ updateToken,
65
52
  ({ newValue }) => {
66
- socket.emit(
67
- `next:${token.key}`,
68
- parseJson(jsonToken.family?.subKey || `null`),
69
- newValue,
70
- )
53
+ socket.emit(`next:${token.key}`, newValue)
71
54
  },
72
55
  `expose-family:${family.key}:${socket.id}`,
73
56
  store,
74
57
  )
75
- unsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)
58
+ unsubCallbacksByKey.set(token.key, unsubscribe)
59
+ socket.on(`unsub:${token.key}`, () => {
60
+ fillUnsubRequest(token.key)
61
+ })
62
+ break
76
63
  }
77
- const unsubscribeFromTokenCreation = family.subject.subscribe(
78
- `expose-family:${socket.id}`,
79
- (token) => {
80
- const jsonToken = getJsonToken(token)
81
- const trackerToken = getUpdateToken(token)
82
- socket.emit(
83
- `init:${family.key}`,
84
- parseJson(jsonToken.family?.subKey || `null`),
85
- getFromStore(jsonToken, store),
86
- )
87
- const unsubFromUpdates = subscribeToState(
88
- trackerToken,
89
- ({ newValue }) => {
90
- socket.emit(
91
- `next:${token.key}`,
92
- parseJson(jsonToken.family?.subKey || `null`),
93
- newValue,
94
- )
95
- },
96
- `expose-family:${family.key}:${socket.id}`,
97
- store,
98
- )
99
- unsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)
100
- },
101
- )
102
- unsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)
103
-
104
- socket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)
105
- } else {
106
- const token = family(subKey)
107
- const jsonToken = getJsonToken(token)
108
- const updateToken = getUpdateToken(token)
109
- socket.emit(`init:${token.key}`, getFromStore(jsonToken, store))
110
- const unsubscribe = subscribeToState(
111
- updateToken,
112
- ({ newValue }) => {
113
- socket.emit(`next:${token.key}`, newValue)
114
- },
115
- `expose-family:${family.key}:${socket.id}`,
116
- store,
117
- )
118
- unsubSingleCallbacksByKey.set(token.key, unsubscribe)
119
- socket.on(`unsub:${token.key}`, () => {
120
- fillSingleUnsubRequest(token.key)
121
- })
122
64
  }
123
65
  }
124
66
 
@@ -126,14 +68,10 @@ export function realtimeMutableFamilyProvider({
126
68
 
127
69
  return () => {
128
70
  socket.off(`sub:${family.key}`, fillSubRequest)
129
- for (const [, unsub] of unsubFamilyCallbacksByKey) {
130
- unsub()
131
- }
132
- for (const [, unsub] of unsubSingleCallbacksByKey) {
71
+ for (const [, unsub] of unsubCallbacksByKey) {
133
72
  unsub()
134
73
  }
135
- unsubFamilyCallbacksByKey.clear()
136
- unsubSingleCallbacksByKey.clear()
74
+ unsubCallbacksByKey.clear()
137
75
  }
138
76
  }
139
77
  }
@@ -1,2 +1,4 @@
1
- export * from "./server-user-store"
1
+ export * from "./realtime-continuity-store"
2
+ export * from "./server-room-store"
2
3
  export * from "./server-sync-store"
4
+ export * from "./server-user-store"