atom.io 0.16.3 → 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 (162) 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 +36 -57
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +64 -53
  14. package/dist/index.js +15 -36
  15. package/dist/index.js.map +1 -1
  16. package/internal/dist/index.cjs +211 -81
  17. package/internal/dist/index.cjs.map +1 -1
  18. package/internal/dist/index.d.ts +100 -72
  19. package/internal/dist/index.js +200 -75
  20. package/internal/dist/index.js.map +1 -1
  21. package/internal/src/arbitrary.ts +3 -0
  22. package/internal/src/atom/create-regular-atom.ts +2 -3
  23. package/internal/src/caching.ts +8 -6
  24. package/internal/src/families/find-in-store.ts +16 -0
  25. package/internal/src/get-environment-data.ts +4 -7
  26. package/internal/src/get-state/get-from-store.ts +14 -0
  27. package/internal/src/get-state/index.ts +2 -0
  28. package/internal/src/{read-or-compute-value.ts → get-state/read-or-compute-value.ts} +3 -3
  29. package/internal/src/index.ts +7 -6
  30. package/internal/src/ingest-updates/ingest-atom-update.ts +2 -2
  31. package/internal/src/ingest-updates/ingest-transaction-update.ts +0 -1
  32. package/internal/src/mutable/create-mutable-atom.ts +3 -4
  33. package/internal/src/mutable/tracker.ts +18 -13
  34. package/internal/src/selector/create-standalone-selector.ts +0 -2
  35. package/internal/src/selector/register-selector.ts +1 -1
  36. package/internal/src/set-state/index.ts +1 -0
  37. package/internal/src/set-state/set-atom.ts +15 -19
  38. package/internal/src/set-state/set-into-store.ts +24 -0
  39. package/internal/src/store/store.ts +14 -2
  40. package/internal/src/store/withdraw.ts +72 -2
  41. package/internal/src/subscribe/subscribe-to-root-atoms.ts +1 -1
  42. package/internal/src/subscribe/subscribe-to-timeline.ts +2 -2
  43. package/internal/src/subscribe/subscribe-to-transaction.ts +2 -2
  44. package/internal/src/timeline/create-timeline.ts +12 -1
  45. package/internal/src/transaction/act-upon-store.ts +19 -0
  46. package/internal/src/transaction/apply-transaction.ts +7 -1
  47. package/internal/src/transaction/assign-transaction-to-continuity.ts +18 -0
  48. package/internal/src/transaction/build-transaction.ts +11 -8
  49. package/internal/src/transaction/create-transaction.ts +1 -1
  50. package/internal/src/transaction/get-epoch-number.ts +40 -0
  51. package/internal/src/transaction/index.ts +10 -1
  52. package/internal/src/transaction/set-epoch-number.ts +31 -0
  53. package/introspection/dist/index.cjs.map +1 -1
  54. package/introspection/dist/index.d.ts +3 -3
  55. package/introspection/dist/index.js.map +1 -1
  56. package/introspection/src/attach-introspection-states.ts +6 -2
  57. package/introspection/src/attach-timeline-family.ts +5 -2
  58. package/introspection/src/attach-transaction-logs.ts +2 -2
  59. package/json/dist/index.d.ts +3 -1
  60. package/json/src/index.ts +6 -2
  61. package/package.json +24 -13
  62. package/react/dist/index.cjs +3 -3
  63. package/react/dist/index.cjs.map +1 -1
  64. package/react/dist/index.d.ts +1 -1
  65. package/react/dist/index.js +5 -5
  66. package/react/dist/index.js.map +1 -1
  67. package/react/src/use-i.ts +2 -3
  68. package/react/src/use-json.ts +1 -1
  69. package/react/src/use-o.ts +3 -4
  70. package/react-devtools/dist/index.cjs +131 -134
  71. package/react-devtools/dist/index.cjs.map +1 -1
  72. package/react-devtools/dist/index.css +2 -2
  73. package/react-devtools/dist/index.css.map +1 -1
  74. package/react-devtools/dist/index.d.ts +3 -3
  75. package/react-devtools/dist/index.js +103 -106
  76. package/react-devtools/dist/index.js.map +1 -1
  77. package/react-devtools/src/StateEditor.tsx +6 -6
  78. package/react-devtools/src/StateIndex.tsx +2 -5
  79. package/react-devtools/src/TimelineIndex.tsx +3 -3
  80. package/react-devtools/src/TransactionIndex.tsx +9 -8
  81. package/react-devtools/src/Updates.tsx +1 -1
  82. package/react-devtools/src/index.ts +4 -4
  83. package/realtime/dist/index.cjs +72 -0
  84. package/realtime/dist/index.cjs.map +1 -0
  85. package/realtime/dist/index.d.ts +39 -0
  86. package/realtime/dist/index.js +68 -0
  87. package/realtime/dist/index.js.map +1 -0
  88. package/realtime/package.json +16 -0
  89. package/realtime/src/index.ts +1 -0
  90. package/realtime/src/realtime-continuity.ts +152 -0
  91. package/realtime-client/dist/index.cjs +403 -59
  92. package/realtime-client/dist/index.cjs.map +1 -1
  93. package/realtime-client/dist/index.d.ts +16 -9
  94. package/realtime-client/dist/index.js +114 -48
  95. package/realtime-client/dist/index.js.map +1 -1
  96. package/realtime-client/src/index.ts +8 -5
  97. package/realtime-client/src/{pull-family-member.ts → pull-atom-family-member.ts} +5 -5
  98. package/realtime-client/src/{pull-state.ts → pull-atom.ts} +5 -5
  99. package/realtime-client/src/{pull-mutable-family-member.ts → pull-mutable-atom-family-member.ts} +5 -5
  100. package/realtime-client/src/{pull-mutable.ts → pull-mutable-atom.ts} +5 -5
  101. package/realtime-client/src/pull-selector-family-member.ts +42 -0
  102. package/realtime-client/src/pull-selector.ts +38 -0
  103. package/realtime-client/src/realtime-client-stores/client-main-store.ts +2 -2
  104. package/realtime-client/src/realtime-client-stores/client-sync-store.ts +7 -7
  105. package/realtime-client/src/sync-continuity.ts +321 -0
  106. package/realtime-client/src/sync-server-action.ts +22 -21
  107. package/realtime-client/src/sync-state.ts +3 -3
  108. package/realtime-react/dist/index.cjs +330 -15
  109. package/realtime-react/dist/index.cjs.map +1 -1
  110. package/realtime-react/dist/index.d.ts +26 -6
  111. package/realtime-react/dist/index.js +43 -12
  112. package/realtime-react/dist/index.js.map +1 -1
  113. package/realtime-react/src/index.ts +6 -3
  114. package/realtime-react/src/use-pull-atom-family-member.ts +21 -0
  115. package/realtime-react/src/{use-pull.ts → use-pull-atom.ts} +6 -5
  116. package/realtime-react/src/{use-pull-mutable.ts → use-pull-mutable-atom.ts} +4 -3
  117. package/realtime-react/src/use-pull-mutable-family-member.ts +9 -4
  118. package/realtime-react/src/use-pull-selector-family-member.ts +21 -0
  119. package/realtime-react/src/{use-pull-family-member.ts → use-pull-selector.ts} +7 -5
  120. package/realtime-react/src/use-push.ts +3 -2
  121. package/realtime-react/src/use-server-action.ts +3 -2
  122. package/realtime-react/src/use-sync-continuity.ts +12 -0
  123. package/realtime-react/src/use-sync-server-action.ts +3 -2
  124. package/realtime-server/dist/index.cjs +582 -256
  125. package/realtime-server/dist/index.cjs.map +1 -1
  126. package/realtime-server/dist/index.d.ts +124 -49
  127. package/realtime-server/dist/index.js +566 -249
  128. package/realtime-server/dist/index.js.map +1 -1
  129. package/realtime-server/src/index.ts +18 -2
  130. package/realtime-server/src/ipc-socket.ts +230 -0
  131. package/realtime-server/src/realtime-action-receiver.ts +8 -5
  132. package/realtime-server/src/realtime-action-synchronizer.ts +53 -35
  133. package/realtime-server/src/realtime-continuity-synchronizer.ts +247 -0
  134. package/realtime-server/src/realtime-family-provider.ts +37 -73
  135. package/realtime-server/src/realtime-mutable-family-provider.ts +26 -87
  136. package/realtime-server/src/realtime-mutable-provider.ts +3 -2
  137. package/realtime-server/src/realtime-server-stores/index.ts +3 -1
  138. package/realtime-server/src/realtime-server-stores/realtime-continuity-store.ts +90 -0
  139. package/realtime-server/src/realtime-server-stores/server-room-store.ts +97 -0
  140. package/realtime-server/src/realtime-server-stores/server-sync-store.ts +2 -72
  141. package/realtime-server/src/realtime-server-stores/server-user-store.ts +14 -29
  142. package/realtime-server/src/realtime-state-provider.ts +3 -3
  143. package/realtime-server/src/realtime-state-receiver.ts +2 -3
  144. package/realtime-server/src/realtime-state-synchronizer.ts +3 -3
  145. package/realtime-testing/dist/index.cjs +28 -28
  146. package/realtime-testing/dist/index.cjs.map +1 -1
  147. package/realtime-testing/dist/index.js +28 -27
  148. package/realtime-testing/dist/index.js.map +1 -1
  149. package/realtime-testing/src/setup-realtime-test.tsx +38 -28
  150. package/src/atom.ts +49 -31
  151. package/src/get-state.ts +2 -11
  152. package/src/logger.ts +10 -5
  153. package/src/selector.ts +44 -25
  154. package/src/set-state.ts +1 -13
  155. package/src/silo.ts +7 -3
  156. package/src/subscribe.ts +2 -1
  157. package/src/timeline.ts +4 -4
  158. package/src/transaction.ts +13 -17
  159. package/src/validators.ts +15 -9
  160. package/dist/chunk-H4Q5FTPZ.js +0 -11
  161. package/dist/chunk-H4Q5FTPZ.js.map +0 -1
  162. package/internal/src/set-state/copy-mutable-in-transaction.ts +0 -19
@@ -1,52 +1,74 @@
1
- import * as AtomIO from "atom.io"
2
- import { IMPLICIT, findInStore, subscribeToTransaction } from "atom.io/internal"
1
+ import type * as AtomIO from "atom.io"
2
+ import {
3
+ IMPLICIT,
4
+ actUponStore,
5
+ assignTransactionToContinuity,
6
+ findInStore,
7
+ getFromStore,
8
+ setIntoStore,
9
+ subscribeToTransaction,
10
+ } from "atom.io/internal"
11
+ import type { Json, JsonIO } from "atom.io/json"
3
12
 
4
13
  import type { ServerConfig } from "."
5
- import { usersOfSockets } from "./realtime-server-stores"
6
14
  import {
7
15
  completeUpdateAtoms,
8
16
  redactedUpdateSelectors,
9
- socketEpochSelectors,
10
- socketUnacknowledgedUpdatesSelectors,
11
17
  transactionRedactorAtoms,
12
- } from "./realtime-server-stores/server-sync-store"
18
+ userUnacknowledgedQueues,
19
+ usersOfSockets,
20
+ } from "./realtime-server-stores"
13
21
 
14
22
  export type ActionSynchronizer = ReturnType<typeof realtimeActionSynchronizer>
15
23
  export function realtimeActionSynchronizer({
16
24
  socket,
17
25
  store = IMPLICIT.STORE,
18
26
  }: ServerConfig) {
19
- return function actionSynchronizer<ƒ extends AtomIO.ƒn>(
27
+ return function actionSynchronizer<ƒ extends JsonIO>(
20
28
  tx: AtomIO.TransactionToken<ƒ>,
21
29
  filter?: (
22
30
  update: AtomIO.TransactionUpdateContent[],
23
31
  ) => AtomIO.TransactionUpdateContent[],
24
32
  ): () => void {
33
+ assignTransactionToContinuity(`default`, tx.key, store)
34
+
25
35
  const userKeyState = findInStore(
26
36
  usersOfSockets.states.userKeyOfSocket,
27
37
  socket.id,
28
38
  store,
29
39
  )
30
- const userKey = AtomIO.getState(userKeyState, store)
31
- const socketUnacknowledgedUpdatesState = findInStore(
32
- socketUnacknowledgedUpdatesSelectors,
33
- socket.id,
40
+ const userKey = getFromStore(userKeyState, store)
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,
34
53
  store,
35
54
  )
36
- const socketUnacknowledgedUpdates = AtomIO.getState(
37
- socketUnacknowledgedUpdatesState,
55
+ const userUnacknowledgedUpdates = getFromStore(
56
+ userUnacknowledgedQueue,
38
57
  store,
39
58
  )
40
59
  if (filter) {
41
60
  const redactorState = findInStore(transactionRedactorAtoms, tx.key, store)
42
- AtomIO.setState(redactorState, { filter }, store)
61
+ setIntoStore(redactorState, { filter }, store)
43
62
  }
44
- const fillTransactionRequest = (update: AtomIO.TransactionUpdate<ƒ>) => {
63
+
64
+ const fillTransactionRequest = (
65
+ update: Pick<AtomIO.TransactionUpdate<ƒ>, `id` | `params`>,
66
+ ) => {
45
67
  const performanceKey = `tx-run:${tx.key}:${update.id}`
46
68
  const performanceKeyStart = `${performanceKey}:start`
47
69
  const performanceKeyEnd = `${performanceKey}:end`
48
70
  performance.mark(performanceKeyStart)
49
- AtomIO.runTransaction<ƒ>(tx, update.id, store)(...update.params)
71
+ actUponStore<ƒ>(tx, update.id, store)(...update.params)
50
72
  performance.mark(performanceKeyEnd)
51
73
  const metric = performance.measure(
52
74
  performanceKey,
@@ -64,9 +86,12 @@ export function realtimeActionSynchronizer({
64
86
  tx,
65
87
  (update) => {
66
88
  const updateState = findInStore(completeUpdateAtoms, update.id, store)
67
- AtomIO.setState(updateState, update, store)
68
- const toEmit = filter
69
- ? AtomIO.getState(
89
+ setIntoStore(updateState, update, store)
90
+ const toEmit: Pick<
91
+ AtomIO.TransactionUpdate<ƒ>,
92
+ `epoch` | `id` | `key` | `output` | `updates`
93
+ > | null = filter
94
+ ? getFromStore(
70
95
  findInStore(redactedUpdateSelectors, [tx.key, update.id], store),
71
96
  store,
72
97
  )
@@ -76,8 +101,8 @@ export function realtimeActionSynchronizer({
76
101
  // updates be set in the queue for that socket's client.
77
102
  //
78
103
  // we need a client session that can persist between disconnects
79
- AtomIO.setState(
80
- socketUnacknowledgedUpdatesState,
104
+ setIntoStore(
105
+ userUnacknowledgedQueue,
81
106
  (updates) => {
82
107
  if (toEmit) {
83
108
  updates.push(toEmit)
@@ -88,7 +113,7 @@ export function realtimeActionSynchronizer({
88
113
  store,
89
114
  )
90
115
 
91
- socket.emit(`tx-new:${tx.key}`, toEmit)
116
+ socket.emit(`tx-new:${tx.key}`, toEmit as Json.Serializable)
92
117
  },
93
118
  `tx-sub:${tx.key}:${socket.id}`,
94
119
  store,
@@ -100,10 +125,9 @@ export function realtimeActionSynchronizer({
100
125
  let i = 1
101
126
  let next = 1
102
127
  const retry = setInterval(() => {
103
- const toEmit = socketUnacknowledgedUpdates[0]
104
- console.log(userKey, socketUnacknowledgedUpdates)
128
+ const toEmit = userUnacknowledgedUpdates[0]
105
129
  if (toEmit && i === next) {
106
- socket.emit(`tx-new:${tx.key}`, toEmit)
130
+ socket.emit(`tx-new:${tx.key}`, toEmit as Json.Serializable)
107
131
  next *= 2
108
132
  }
109
133
 
@@ -113,16 +137,10 @@ export function realtimeActionSynchronizer({
113
137
  const trackClientAcknowledgement = (epoch: number) => {
114
138
  i = 1
115
139
  next = 1
116
- const socketEpochState = findInStore(
117
- socketEpochSelectors,
118
- socket.id,
119
- store,
120
- )
121
-
122
- AtomIO.setState(socketEpochState, epoch, store)
123
- if (socketUnacknowledgedUpdates[0]?.epoch === epoch) {
124
- AtomIO.setState(
125
- socketUnacknowledgedUpdatesState,
140
+ 8
141
+ if (userUnacknowledgedUpdates[0]?.epoch === epoch) {
142
+ setIntoStore(
143
+ userUnacknowledgedQueue,
126
144
  (updates) => {
127
145
  updates.shift()
128
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
+ }
@@ -1,12 +1,16 @@
1
- import * as AtomIO from "atom.io"
2
- import { IMPLICIT, findInStore, subscribeToState } from "atom.io/internal"
3
- import type { Json } from "atom.io/json"
4
- import { parseJson } from "atom.io/json"
1
+ import type * as AtomIO from "atom.io"
2
+ import {
3
+ IMPLICIT,
4
+ findInStore,
5
+ getFromStore,
6
+ subscribeToState,
7
+ } from "atom.io/internal"
8
+ import { type Json, stringifyJson } from "atom.io/json"
5
9
 
6
10
  import type { ServerConfig } from "."
7
11
 
8
- export type FamilyProvider = ReturnType<typeof realtimeFamilyProvider>
9
- export function realtimeFamilyProvider({
12
+ export type FamilyProvider = ReturnType<typeof realtimeAtomFamilyProvider>
13
+ export function realtimeAtomFamilyProvider({
10
14
  socket,
11
15
  store = IMPLICIT.STORE,
12
16
  }: ServerConfig) {
@@ -14,77 +18,40 @@ export function realtimeFamilyProvider({
14
18
  J extends Json.Serializable,
15
19
  K extends Json.Serializable,
16
20
  >(
17
- family: AtomIO.RegularAtomFamily<J, K>,
21
+ family: AtomIO.RegularAtomFamilyToken<J, K>,
18
22
  index: AtomIO.ReadableToken<Iterable<K>>,
19
23
  ): () => void {
20
- const unsubSingleCallbacksByKey = new Map<string, () => void>()
21
- const unsubFamilyCallbacksByKey = new Map<string, () => void>()
24
+ const unsubCallbacksByKey = new Map<string, () => void>()
22
25
 
23
- const fillFamilyUnsubRequest = () => {
24
- for (const [, unsub] of unsubFamilyCallbacksByKey) {
25
- unsub()
26
- }
27
- unsubFamilyCallbacksByKey.clear()
28
- socket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)
29
- }
30
-
31
- const fillSingleUnsubRequest = (key: string) => {
32
- socket.off(`unsub:${key}`, fillSingleUnsubRequest)
33
- const unsub = unsubSingleCallbacksByKey.get(key)
26
+ const fillUnsubRequest = (key: string) => {
27
+ socket.off(`unsub:${key}`, fillUnsubRequest)
28
+ const unsub = unsubCallbacksByKey.get(key)
34
29
  if (unsub) {
35
30
  unsub()
36
- unsubSingleCallbacksByKey.delete(key)
31
+ unsubCallbacksByKey.delete(key)
37
32
  }
38
33
  }
39
34
 
40
- const fillSubRequest = (subKey?: K) => {
41
- if (subKey === undefined) {
42
- const keys = AtomIO.getState(index, store)
43
- for (const key of keys) {
44
- const token = findInStore(family, key, store)
45
- socket.emit(
46
- `serve:${family.key}`,
47
- parseJson(token.family?.subKey || `null`),
48
- AtomIO.getState(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,
49
48
  )
49
+ unsubCallbacksByKey.set(token.key, unsubscribe)
50
+ socket.on(`unsub:${token.key}`, () => {
51
+ fillUnsubRequest(token.key)
52
+ })
53
+ break
50
54
  }
51
-
52
- const unsubscribeFromTokenCreation = family.subject.subscribe(
53
- `expose-family:${socket.id}`,
54
- (token: AtomIO.WritableToken<J>) => {
55
- const unsub = subscribeToState(
56
- token,
57
- ({ newValue }) => {
58
- socket.emit(
59
- `serve:${family.key}`,
60
- parseJson(token.family?.subKey || `null`),
61
- newValue,
62
- )
63
- },
64
- `expose-family:${family.key}:${socket.id}`,
65
- store,
66
- )
67
- unsubFamilyCallbacksByKey.set(token.key, unsub)
68
- },
69
- )
70
- unsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)
71
-
72
- socket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)
73
- } else {
74
- const token = family(subKey)
75
- socket.emit(`serve:${token.key}`, AtomIO.getState(token, store))
76
- const unsubscribe = subscribeToState(
77
- token,
78
- ({ newValue }) => {
79
- socket.emit(`serve:${token.key}`, newValue)
80
- },
81
- `expose-family:${family.key}:${socket.id}`,
82
- store,
83
- )
84
- unsubSingleCallbacksByKey.set(token.key, unsubscribe)
85
- socket.on(`unsub:${token.key}`, () => {
86
- fillSingleUnsubRequest(token.key)
87
- })
88
55
  }
89
56
  }
90
57
 
@@ -92,14 +59,11 @@ export function realtimeFamilyProvider({
92
59
 
93
60
  return () => {
94
61
  socket.off(`sub:${family.key}`, fillSubRequest)
95
- for (const [, unsub] of unsubFamilyCallbacksByKey) {
96
- unsub()
97
- }
98
- for (const [, unsub] of unsubSingleCallbacksByKey) {
62
+
63
+ for (const [, unsub] of unsubCallbacksByKey) {
99
64
  unsub()
100
65
  }
101
- unsubFamilyCallbacksByKey.clear()
102
- unsubSingleCallbacksByKey.clear()
66
+ unsubCallbacksByKey.clear()
103
67
  }
104
68
  }
105
69
  }
@@ -1,14 +1,15 @@
1
- import * as AtomIO from "atom.io"
1
+ import type * as AtomIO from "atom.io"
2
2
  import type { Transceiver } from "atom.io/internal"
3
3
  import {
4
4
  IMPLICIT,
5
5
  findInStore,
6
+ getFromStore,
6
7
  getJsonToken,
7
8
  getUpdateToken,
8
9
  subscribeToState,
9
10
  } from "atom.io/internal"
10
11
  import type { Json } from "atom.io/json"
11
- import { parseJson } from "atom.io/json"
12
+ import { stringifyJson } from "atom.io/json"
12
13
 
13
14
  import type { ServerConfig } from "."
14
15
 
@@ -24,100 +25,42 @@ export function realtimeMutableFamilyProvider({
24
25
  J extends Json.Serializable,
25
26
  K extends Json.Serializable,
26
27
  >(
27
- family: AtomIO.MutableAtomFamily<T, J, K>,
28
+ family: AtomIO.MutableAtomFamilyToken<T, J, K>,
28
29
  index: AtomIO.ReadableToken<Iterable<K>>,
29
30
  ): () => void {
30
- const unsubSingleCallbacksByKey = new Map<string, () => void>()
31
- const unsubFamilyCallbacksByKey = new Map<string, () => void>()
31
+ const unsubCallbacksByKey = new Map<string, () => void>()
32
32
 
33
- const fillFamilyUnsubRequest = () => {
34
- for (const [, unsub] of unsubFamilyCallbacksByKey) {
35
- unsub()
36
- }
37
- unsubFamilyCallbacksByKey.clear()
38
- socket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)
39
- }
40
-
41
- const fillSingleUnsubRequest = (key: string) => {
42
- socket.off(`unsub:${key}`, fillSingleUnsubRequest)
43
- const unsub = unsubSingleCallbacksByKey.get(key)
33
+ const fillUnsubRequest = (key: string) => {
34
+ socket.off(`unsub:${key}`, fillUnsubRequest)
35
+ const unsub = unsubCallbacksByKey.get(key)
44
36
  if (unsub) {
45
37
  unsub()
46
- unsubSingleCallbacksByKey.delete(key)
38
+ unsubCallbacksByKey.delete(key)
47
39
  }
48
40
  }
49
41
 
50
- const fillSubRequest = (subKey?: K) => {
51
- if (subKey === undefined) {
52
- const keys = AtomIO.getState(index, store)
53
- for (const key of keys) {
54
- 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)
55
47
  const jsonToken = getJsonToken(token)
56
- const trackerToken = getUpdateToken(token)
57
- socket.emit(
58
- `init:${family.key}`,
59
- parseJson(jsonToken.family?.subKey || `null`),
60
- AtomIO.getState(jsonToken, store),
61
- )
62
- const unsubFromUpdates = subscribeToState(
63
- trackerToken,
48
+ const updateToken = getUpdateToken(token)
49
+ socket.emit(`init:${token.key}`, getFromStore(jsonToken, store))
50
+ const unsubscribe = subscribeToState(
51
+ updateToken,
64
52
  ({ newValue }) => {
65
- socket.emit(
66
- `next:${token.key}`,
67
- parseJson(jsonToken.family?.subKey || `null`),
68
- newValue,
69
- )
53
+ socket.emit(`next:${token.key}`, newValue)
70
54
  },
71
55
  `expose-family:${family.key}:${socket.id}`,
72
56
  store,
73
57
  )
74
- unsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)
58
+ unsubCallbacksByKey.set(token.key, unsubscribe)
59
+ socket.on(`unsub:${token.key}`, () => {
60
+ fillUnsubRequest(token.key)
61
+ })
62
+ break
75
63
  }
76
- const unsubscribeFromTokenCreation = family.subject.subscribe(
77
- `expose-family:${socket.id}`,
78
- (token) => {
79
- const jsonToken = getJsonToken(token)
80
- const trackerToken = getUpdateToken(token)
81
- socket.emit(
82
- `init:${family.key}`,
83
- parseJson(jsonToken.family?.subKey || `null`),
84
- AtomIO.getState(jsonToken, store),
85
- )
86
- const unsubFromUpdates = subscribeToState(
87
- trackerToken,
88
- ({ newValue }) => {
89
- socket.emit(
90
- `next:${token.key}`,
91
- parseJson(jsonToken.family?.subKey || `null`),
92
- newValue,
93
- )
94
- },
95
- `expose-family:${family.key}:${socket.id}`,
96
- store,
97
- )
98
- unsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)
99
- },
100
- )
101
- unsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)
102
-
103
- socket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)
104
- } else {
105
- const token = family(subKey)
106
- const jsonToken = getJsonToken(token)
107
- const updateToken = getUpdateToken(token)
108
- socket.emit(`init:${token.key}`, AtomIO.getState(jsonToken, store))
109
- const unsubscribe = subscribeToState(
110
- updateToken,
111
- ({ newValue }) => {
112
- socket.emit(`next:${token.key}`, newValue)
113
- },
114
- `expose-family:${family.key}:${socket.id}`,
115
- store,
116
- )
117
- unsubSingleCallbacksByKey.set(token.key, unsubscribe)
118
- socket.on(`unsub:${token.key}`, () => {
119
- fillSingleUnsubRequest(token.key)
120
- })
121
64
  }
122
65
  }
123
66
 
@@ -125,14 +68,10 @@ export function realtimeMutableFamilyProvider({
125
68
 
126
69
  return () => {
127
70
  socket.off(`sub:${family.key}`, fillSubRequest)
128
- for (const [, unsub] of unsubFamilyCallbacksByKey) {
129
- unsub()
130
- }
131
- for (const [, unsub] of unsubSingleCallbacksByKey) {
71
+ for (const [, unsub] of unsubCallbacksByKey) {
132
72
  unsub()
133
73
  }
134
- unsubFamilyCallbacksByKey.clear()
135
- unsubSingleCallbacksByKey.clear()
74
+ unsubCallbacksByKey.clear()
136
75
  }
137
76
  }
138
77
  }