atom.io 0.18.0 → 0.18.1

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 (83) hide show
  1. package/dist/{chunk-OEVFAUPE.js → chunk-IZHOMSXA.js} +53 -11
  2. package/dist/chunk-IZHOMSXA.js.map +1 -0
  3. package/dist/chunk-JDUNWJFB.js +18 -0
  4. package/dist/chunk-JDUNWJFB.js.map +1 -0
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.ts +5 -1
  7. package/dist/index.js.map +1 -1
  8. package/internal/dist/index.cjs +64 -34
  9. package/internal/dist/index.cjs.map +1 -1
  10. package/internal/dist/index.d.ts +1 -1
  11. package/internal/dist/index.js +64 -34
  12. package/internal/dist/index.js.map +1 -1
  13. package/internal/src/atom/delete-atom.ts +7 -6
  14. package/internal/src/caching.ts +6 -6
  15. package/internal/src/ingest-updates/ingest-atom-update.ts +6 -2
  16. package/internal/src/set-state/copy-mutable-if-needed.ts +5 -0
  17. package/internal/src/set-state/emit-update.ts +25 -11
  18. package/internal/src/set-state/set-atom.ts +4 -3
  19. package/internal/src/transaction/apply-transaction.ts +0 -1
  20. package/internal/src/transaction/set-epoch-number.ts +0 -1
  21. package/json/src/index.ts +3 -3
  22. package/package.json +241 -241
  23. package/react-devtools/dist/index.cjs.map +1 -1
  24. package/react-devtools/dist/index.js +1 -15
  25. package/react-devtools/dist/index.js.map +1 -1
  26. package/react-devtools/src/StateEditor.tsx +6 -6
  27. package/react-devtools/src/StateIndex.tsx +2 -2
  28. package/react-devtools/src/Updates.tsx +1 -1
  29. package/react-devtools/src/index.ts +3 -3
  30. package/realtime/dist/index.cjs +50 -2
  31. package/realtime/dist/index.cjs.map +1 -1
  32. package/realtime/dist/index.d.ts +110 -3
  33. package/realtime/dist/index.js +47 -4
  34. package/realtime/dist/index.js.map +1 -1
  35. package/realtime/src/index.ts +1 -0
  36. package/realtime/src/realtime-continuity.ts +14 -4
  37. package/realtime/src/shared-room-store.ts +48 -0
  38. package/realtime-client/dist/index.cjs +113 -200
  39. package/realtime-client/dist/index.cjs.map +1 -1
  40. package/realtime-client/dist/index.d.ts +2 -5
  41. package/realtime-client/dist/index.js +17 -161
  42. package/realtime-client/dist/index.js.map +1 -1
  43. package/realtime-client/src/index.ts +0 -2
  44. package/realtime-client/src/pull-mutable-atom-family-member.ts +5 -5
  45. package/realtime-client/src/realtime-client-stores/client-main-store.ts +10 -0
  46. package/realtime-client/src/sync-continuity.ts +56 -9
  47. package/realtime-react/dist/index.cjs +51 -26
  48. package/realtime-react/dist/index.cjs.map +1 -1
  49. package/realtime-react/dist/index.d.ts +2 -6
  50. package/realtime-react/dist/index.js +2 -17
  51. package/realtime-react/dist/index.js.map +1 -1
  52. package/realtime-react/src/index.ts +0 -2
  53. package/realtime-server/dist/index.cjs +399 -327
  54. package/realtime-server/dist/index.cjs.map +1 -1
  55. package/realtime-server/dist/index.d.ts +55 -60
  56. package/realtime-server/dist/index.js +394 -319
  57. package/realtime-server/dist/index.js.map +1 -1
  58. package/realtime-server/src/index.ts +2 -4
  59. package/realtime-server/src/ipc-sockets/child-socket.ts +135 -0
  60. package/realtime-server/src/ipc-sockets/custom-socket.ts +90 -0
  61. package/realtime-server/src/ipc-sockets/index.ts +3 -0
  62. package/realtime-server/src/ipc-sockets/parent-socket.ts +185 -0
  63. package/realtime-server/src/realtime-continuity-synchronizer.ts +225 -96
  64. package/realtime-server/src/realtime-server-stores/index.ts +2 -1
  65. package/realtime-server/src/realtime-server-stores/realtime-continuity-store.ts +50 -31
  66. package/realtime-server/src/realtime-server-stores/server-room-external-actions.ts +64 -0
  67. package/realtime-server/src/realtime-server-stores/server-room-external-store.ts +42 -0
  68. package/realtime-server/src/realtime-server-stores/server-sync-store.ts +49 -26
  69. package/realtime-testing/dist/index.cjs +8 -6
  70. package/realtime-testing/dist/index.cjs.map +1 -1
  71. package/realtime-testing/dist/index.d.ts +1 -0
  72. package/realtime-testing/dist/index.js +7 -6
  73. package/realtime-testing/dist/index.js.map +1 -1
  74. package/realtime-testing/src/setup-realtime-test.tsx +8 -6
  75. package/src/logger.ts +5 -1
  76. package/dist/chunk-OEVFAUPE.js.map +0 -1
  77. package/realtime-client/src/sync-server-action.ts +0 -168
  78. package/realtime-client/src/sync-state.ts +0 -19
  79. package/realtime-react/src/use-sync-server-action.ts +0 -17
  80. package/realtime-react/src/use-sync.ts +0 -17
  81. package/realtime-server/src/ipc-socket.ts +0 -230
  82. package/realtime-server/src/realtime-action-synchronizer.ts +0 -164
  83. package/realtime-server/src/realtime-server-stores/server-room-store.ts +0 -97
@@ -0,0 +1,185 @@
1
+ import { IMPLICIT, Subject } from "atom.io/internal"
2
+ import { parseJson, stringifyJson } from "atom.io/json"
3
+ import type { Json } from "atom.io/json"
4
+
5
+ import { SetRTX } from "atom.io/transceivers/set-rtx"
6
+ import { CustomSocket } from "./custom-socket"
7
+ import type { EventBuffer, Events } from "./custom-socket"
8
+
9
+ export class SubjectSocket<
10
+ I extends Events,
11
+ O extends Events,
12
+ > extends CustomSocket<I, O> {
13
+ public in: Subject<[string, ...Json.Serializable[]]>
14
+ public out: Subject<[string, ...Json.Serializable[]]>
15
+ public id = `no_id_retrieved`
16
+ public disposalFunctions: (() => void)[] = []
17
+
18
+ public constructor(id: string) {
19
+ super((...args) => {
20
+ this.out.next(args as any)
21
+ return this
22
+ })
23
+ this.id = id
24
+ this.in = new Subject()
25
+ this.out = new Subject()
26
+ this.in.subscribe(`socket`, (event) => {
27
+ this.handleEvent(...(event as [string, ...I[keyof I]]))
28
+ })
29
+ }
30
+
31
+ public dispose(): void {
32
+ for (const dispose of this.disposalFunctions) {
33
+ dispose()
34
+ }
35
+ }
36
+ }
37
+
38
+ export class ParentSocket<
39
+ I extends Events & {
40
+ [id in string as `user:${id}`]: [string, ...Json.Serializable[]]
41
+ } & {
42
+ /* eslint-disable quotes */
43
+ "user-joins": [string]
44
+ "user-leaves": [string]
45
+ /* eslint-enable quotes */
46
+ },
47
+ O extends Events & {
48
+ [id in string as `relay:${id}`]: [string, ...Json.Serializable[]]
49
+ },
50
+ > extends CustomSocket<I, O> {
51
+ protected incompleteData = ``
52
+ protected unprocessedEvents: string[] = []
53
+ protected relays: Map<string, SubjectSocket<any, any>>
54
+ protected relayServices: ((
55
+ socket: SubjectSocket<any, any>,
56
+ ) => (() => void) | void)[]
57
+ protected process: NodeJS.Process
58
+
59
+ public id = `#####`
60
+
61
+ protected log(...args: any[]): void {
62
+ this.process.stderr.write(
63
+ stringifyJson(
64
+ args.map((arg) =>
65
+ arg instanceof SetRTX
66
+ ? `{ ${arg.toJSON().members.join(` | `)} }`
67
+ : arg,
68
+ ),
69
+ ) + `\x03`,
70
+ )
71
+ }
72
+ public logger = {
73
+ info: (...args: any[]): void => this.log(`i`, ...args),
74
+ warn: (...args: any[]): void => this.log(`w`, ...args),
75
+ error: (...args: any[]): void => this.log(`e`, ...args),
76
+ }
77
+
78
+ public constructor() {
79
+ super((event, ...args) => {
80
+ const stringifiedEvent = JSON.stringify([event, ...args])
81
+ this.process.stdout.write(stringifiedEvent + `\x03`)
82
+ return this
83
+ })
84
+ this.process = process
85
+ this.process.stdin.resume()
86
+ this.relays = new Map()
87
+ this.relayServices = []
88
+
89
+ this.process.stdin.on(
90
+ `data`,
91
+ <Event extends keyof I>(buffer: EventBuffer<string, I[Event]>) => {
92
+ const chunk = buffer.toString()
93
+ this.unprocessedEvents.push(...chunk.split(`\x03`))
94
+ const newInput = this.unprocessedEvents.shift()
95
+ this.incompleteData += newInput || ``
96
+
97
+ try {
98
+ const parsedEvent = parseJson(this.incompleteData)
99
+ this.logger.info(`🎰`, `received`, parsedEvent)
100
+ this.handleEvent(...(parsedEvent as [string, ...I[keyof I]]))
101
+ while (this.unprocessedEvents.length > 0) {
102
+ const event = this.unprocessedEvents.shift()
103
+ if (event) {
104
+ if (this.unprocessedEvents.length === 0) {
105
+ this.incompleteData = event
106
+ }
107
+ const parsedEvent = parseJson(event)
108
+ this.handleEvent(...(parsedEvent as [string, ...I[keyof I]]))
109
+ }
110
+ }
111
+ this.incompleteData = ``
112
+ } catch (thrown) {
113
+ if (thrown instanceof Error) {
114
+ this.logger.error(`❗`, thrown.message, thrown.cause, thrown.stack)
115
+ }
116
+ }
117
+ },
118
+ )
119
+
120
+ this.on(`exit`, () => {
121
+ process.exit(0)
122
+ })
123
+ process.on(`exit`, () => {
124
+ this.logger.info(`🔥`, this.id, `exited`)
125
+ process.exit(0)
126
+ })
127
+ process.on(`end`, () => {
128
+ this.logger.info(`🔥`, this.id, `ended`)
129
+ process.exit(0)
130
+ })
131
+ process.on(`SIGTERM`, () => {
132
+ this.logger.error(`🔥`, this.id, `terminated`)
133
+ process.exit(0)
134
+ })
135
+ process.on(`SIGINT`, () => {
136
+ this.logger.error(`🔥`, this.id, `interrupted`)
137
+ process.exit(0)
138
+ })
139
+
140
+ if (process.pid) {
141
+ this.id = process.pid?.toString()
142
+ }
143
+
144
+ this.on(`user-joins`, (username) => {
145
+ this.logger.info(`👤`, `user`, username, `joined`)
146
+ const relay = new SubjectSocket(`user:${username}`)
147
+ this.relays.set(username, relay)
148
+ this.logger.info(
149
+ `🔗`,
150
+ `attaching services:`,
151
+ `[${[...this.relayServices.keys()].join(`, `)}]`,
152
+ )
153
+ for (const attachServices of this.relayServices) {
154
+ const cleanup = attachServices(relay)
155
+ if (cleanup) {
156
+ relay.disposalFunctions.push(cleanup)
157
+ }
158
+ }
159
+ this.on(`user:${username}`, (...data) => {
160
+ relay.in.next(data)
161
+ })
162
+ relay.out.subscribe(`socket`, (data) => {
163
+ this.emit(...(data as [string, ...O[keyof O]]))
164
+ })
165
+ })
166
+
167
+ this.on(`user-leaves`, (username) => {
168
+ const relay = this.relays.get(username)
169
+ this.off(`relay:${username}`)
170
+ if (relay) {
171
+ relay.dispose()
172
+ this.relays.delete(username)
173
+ }
174
+ })
175
+
176
+ process.stdout.write(`✨`)
177
+ }
178
+
179
+ public relay(
180
+ attachServices: (socket: SubjectSocket<any, any>) => (() => void) | void,
181
+ ): void {
182
+ this.logger.info(`🔗`, `running relay method`)
183
+ this.relayServices.push(attachServices)
184
+ }
185
+ }
@@ -4,8 +4,8 @@ import {
4
4
  actUponStore,
5
5
  findInStore,
6
6
  getFromStore,
7
+ getJsonToken,
7
8
  isRootStore,
8
- setIntoStore,
9
9
  subscribeToState,
10
10
  subscribeToTransaction,
11
11
  } from "atom.io/internal"
@@ -14,9 +14,9 @@ import type { ContinuityToken } from "atom.io/realtime"
14
14
 
15
15
  import type { ServerConfig, Socket } from "."
16
16
  import { socketAtoms, usersOfSockets } from "."
17
- import { redactedPerspectiveUpdateSelectors } from "./realtime-server-stores"
17
+
18
18
  import {
19
- completeUpdateAtoms,
19
+ redactTransactionUpdateContent,
20
20
  userUnacknowledgedQueues,
21
21
  } from "./realtime-server-stores"
22
22
 
@@ -88,69 +88,148 @@ export function realtimeContinuitySynchronizer({
88
88
  )
89
89
  const unsubscribeFunctions: (() => void)[] = []
90
90
 
91
+ const revealPerspectives = (): (() => void) => {
92
+ const unsubscribeFunctions: (() => void)[] = []
93
+ for (const perspective of continuity.perspectives) {
94
+ const { viewAtoms } = perspective
95
+ const userViewState = findInStore(viewAtoms, userKey, store)
96
+ const unsubscribe = subscribeToState(
97
+ userViewState,
98
+ ({ oldValue, newValue }) => {
99
+ const oldKeys = oldValue.map((token) => token.key)
100
+ const newKeys = newValue.map((token) => token.key)
101
+ const concealed = oldValue.filter(
102
+ (token) => !newKeys.includes(token.key),
103
+ )
104
+ const revealed = newValue
105
+ .filter((token) => !oldKeys.includes(token.key))
106
+ .flatMap((token) => {
107
+ const resourceToken =
108
+ token.type === `mutable_atom` ? getJsonToken(token) : token
109
+ const resource = getFromStore(resourceToken, store)
110
+ return [resourceToken, resource]
111
+ })
112
+ store.logger.info(
113
+ `👁`,
114
+ `atom`,
115
+ perspective.resourceAtoms.key,
116
+ `${userKey} has a new perspective`,
117
+ { oldKeys, newKeys, revealed, concealed },
118
+ )
119
+ if (revealed.length > 0) {
120
+ socket?.emit(`reveal:${continuityKey}`, revealed)
121
+ }
122
+ if (concealed.length > 0) {
123
+ socket?.emit(`conceal:${continuityKey}`, concealed)
124
+ }
125
+ },
126
+ `sync-continuity:${continuityKey}:${userKey}:perspective:${perspective.resourceAtoms.key}`,
127
+ store,
128
+ )
129
+ unsubscribeFunctions.push(unsubscribe)
130
+ }
131
+ return () => {
132
+ for (const unsubscribe of unsubscribeFunctions) unsubscribe()
133
+ }
134
+ }
135
+ const unsubscribeFromPerspectives = revealPerspectives()
136
+
91
137
  const sendInitialPayload = () => {
92
138
  const initialPayload: Json.Serializable[] = []
93
139
  for (const atom of continuity.globals) {
94
- initialPayload.push(atom, getFromStore(atom, store))
140
+ const resourceToken =
141
+ atom.type === `mutable_atom` ? getJsonToken(atom) : atom
142
+ initialPayload.push(resourceToken, getFromStore(atom, store))
95
143
  }
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)
144
+ for (const perspective of continuity.perspectives) {
145
+ const { viewAtoms, resourceAtoms } = perspective
146
+ const userViewState = findInStore(viewAtoms, userKey, store)
147
+ const userView = getFromStore(userViewState, store)
148
+ store.logger.info(`👁`, `atom`, resourceAtoms.key, `${userKey} can see`, {
149
+ viewAtoms,
150
+ resourceAtoms,
151
+ userView,
152
+ })
153
+ for (const visibleToken of userView) {
154
+ const resourceToken =
155
+ visibleToken.type === `mutable_atom`
156
+ ? getJsonToken(visibleToken)
157
+ : visibleToken
158
+ const resource = getFromStore(resourceToken, store)
159
+
160
+ initialPayload.push(resourceToken, resource)
106
161
  }
107
162
  }
108
163
 
109
164
  const epoch = isRootStore(store)
110
165
  ? store.transactionMeta.epoch.get(continuityKey) ?? null
111
166
  : null
167
+
112
168
  socket?.emit(`continuity-init:${continuityKey}`, epoch, initialPayload)
113
169
 
114
170
  for (const transaction of continuity.actions) {
115
171
  const unsubscribeFromTransaction = subscribeToTransaction(
116
172
  transaction,
117
173
  (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
- )
174
+ try {
175
+ const visibleKeys = continuity.globals
176
+ .map((atom) => atom.key)
177
+ .concat(
178
+ continuity.perspectives.flatMap((perspective) => {
179
+ const { viewAtoms } = perspective
180
+ const userPerspectiveTokenState = findInStore(
181
+ viewAtoms,
182
+ userKey,
183
+ store,
184
+ )
185
+ const visibleTokens = getFromStore(
186
+ userPerspectiveTokenState,
187
+ store,
188
+ )
189
+ return visibleTokens.map((token) => {
190
+ const key =
191
+ token.type === `mutable_atom`
192
+ ? `*` + token.key
193
+ : token.key
194
+ return key
195
+ })
196
+ }),
197
+ )
198
+ const redactedUpdates = redactTransactionUpdateContent(
199
+ visibleKeys,
200
+ update.updates,
201
+ )
202
+ const redactedUpdate = {
203
+ ...update,
204
+ updates: redactedUpdates,
205
+ }
206
+ // setIntoStore(
207
+ // userUnacknowledgedQueue,
208
+ // (updates) => {
209
+ // if (redactedUpdate) {
210
+ // updates.push(redactedUpdate)
211
+ // updates.sort((a, b) => a.epoch - b.epoch)
212
+ // }
213
+ // return updates
214
+ // },
215
+ // store,
216
+ // )
135
217
 
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
- )
218
+ socket?.emit(
219
+ `tx-new:${continuityKey}`,
220
+ redactedUpdate as Json.Serializable,
221
+ )
222
+ } catch (thrown) {
223
+ if (thrown instanceof Error) {
224
+ store.logger.error(
225
+ `❌`,
226
+ `continuity`,
227
+ continuityKey,
228
+ `failed to send update from transaction ${transaction.key} to ${userKey}`,
229
+ thrown.message,
230
+ )
231
+ }
232
+ }
154
233
  },
155
234
  `sync-continuity:${continuityKey}:${userKey}`,
156
235
  store,
@@ -164,17 +243,30 @@ export function realtimeContinuitySynchronizer({
164
243
  const fillTransactionRequest = (
165
244
  update: Pick<AtomIO.TransactionUpdate<JsonIO>, `id` | `key` | `params`>,
166
245
  ) => {
246
+ store.logger.info(`🛎️`, `continuity`, continuityKey, `received`, update)
167
247
  const transactionKey = update.key
168
248
  const updateId = update.id
169
249
  const performanceKey = `tx-run:${transactionKey}:${updateId}`
170
250
  const performanceKeyStart = `${performanceKey}:start`
171
251
  const performanceKeyEnd = `${performanceKey}:end`
172
252
  performance.mark(performanceKeyStart)
173
- actUponStore(
174
- { type: `transaction`, key: transactionKey },
175
- updateId,
176
- store,
177
- )(...update.params)
253
+ try {
254
+ actUponStore(
255
+ { type: `transaction`, key: transactionKey },
256
+ updateId,
257
+ store,
258
+ )(...update.params)
259
+ } catch (thrown) {
260
+ if (thrown instanceof Error) {
261
+ store.logger.error(
262
+ `❌`,
263
+ `continuity`,
264
+ continuityKey,
265
+ `failed to run transaction ${transactionKey} with update ${updateId}`,
266
+ thrown.message,
267
+ )
268
+ }
269
+ }
178
270
  performance.mark(performanceKeyEnd)
179
271
  const metric = performance.measure(
180
272
  performanceKey,
@@ -188,58 +280,95 @@ export function realtimeContinuitySynchronizer({
188
280
  updateId,
189
281
  metric.duration,
190
282
  )
191
- }
192
- socket.off(`tx-run:${continuityKey}`, fillTransactionRequest)
193
- socket.on(`tx-run:${continuityKey}`, fillTransactionRequest)
194
283
 
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
- }
284
+ const valuesOfCardsViewKey = `valuesOfCardsView("${userKey}")`
285
+ const rootsOfCardValueView =
286
+ store.selectorAtoms.getRelatedKeys(valuesOfCardsViewKey)
287
+ const myCardValueView = store.valueMap.get(valuesOfCardsViewKey)
211
288
 
212
- i++
213
- }, 250)
214
- const trackClientAcknowledgement = (epoch: number) => {
215
289
  store.logger.info(
216
- `👍`,
290
+ `👁`,
217
291
  `continuity`,
218
292
  continuityKey,
219
- `${userKey} acknowledged epoch ${epoch}`,
293
+ `seeing ${userKey} card values`,
294
+ {
295
+ valuesOfCardsViewKey,
296
+ rootsOfCardValueView,
297
+ myCardValueView,
298
+ },
220
299
  )
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
300
  }
236
- socket.off(`ack:${continuityKey}`, trackClientAcknowledgement)
237
- socket.on(`ack:${continuityKey}`, trackClientAcknowledgement)
301
+ socket.off(`tx-run:${continuityKey}`, fillTransactionRequest)
302
+ socket.on(`tx-run:${continuityKey}`, fillTransactionRequest)
303
+
304
+ // let i = 0
305
+ // let n = 1
306
+ // let retryTimeout: NodeJS.Timeout | undefined
307
+ // const trackClientAcknowledgement = (epoch: number) => {
308
+ // store.logger.info(
309
+ // `👍`,
310
+ // `continuity`,
311
+ // continuityKey,
312
+ // `${userKey} acknowledged epoch ${epoch}`,
313
+ // )
314
+ // const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch
315
+ // if (isUnacknowledged) {
316
+ // setIntoStore(
317
+ // userUnacknowledgedQueue,
318
+ // (updates) => {
319
+ // updates.shift()
320
+ // return updates
321
+ // },
322
+ // store,
323
+ // )
324
+ // }
325
+ // }
326
+ // subscribeToState(
327
+ // userUnacknowledgedQueue,
328
+ // ({ newValue }) => {
329
+ // if (newValue.length === 0) {
330
+ // clearInterval(retryTimeout)
331
+ // socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement)
332
+ // retryTimeout = undefined
333
+ // }
334
+ // if (newValue.length > 0) {
335
+ // if (retryTimeout) {
336
+ // return
337
+ // }
338
+
339
+ // socket?.on(`ack:${continuityKey}`, trackClientAcknowledgement)
340
+
341
+ // retryTimeout = setInterval(() => {
342
+ // i++
343
+ // if (i === n) {
344
+ // n += i
345
+ // const toEmit = newValue[0]
346
+ // if (!toEmit) return
347
+ // store.logger.info(
348
+ // `🔄`,
349
+ // `continuity`,
350
+ // continuityKey,
351
+ // `${store.config.name} retrying ${userKey}`,
352
+ // socket?.id,
353
+ // newValue,
354
+ // )
355
+ // socket?.emit(
356
+ // `tx-new:${continuityKey}`,
357
+ // toEmit as Json.Serializable,
358
+ // )
359
+ // }
360
+ // }, 250)
361
+ // }
362
+ // },
363
+ // `sync-continuity:${continuityKey}:${userKey}`,
364
+ // store,
365
+ // )
238
366
 
239
367
  return () => {
240
- clearInterval(retry)
368
+ // clearInterval(retryTimeout)
241
369
  for (const unsubscribe of unsubscribeFunctions) unsubscribe()
242
- socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement)
370
+ // socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement)
371
+ unsubscribeFromPerspectives()
243
372
  socket?.off(`get:${continuityKey}`, sendInitialPayload)
244
373
  socket?.off(`tx-run:${continuityKey}`, fillTransactionRequest)
245
374
  }
@@ -1,4 +1,5 @@
1
1
  export * from "./realtime-continuity-store"
2
- export * from "./server-room-store"
2
+ export * from "./server-room-external-actions"
3
+ export * from "./server-room-external-store"
3
4
  export * from "./server-sync-store"
4
5
  export * from "./server-user-store"
@@ -1,8 +1,9 @@
1
1
  import { selectorFamily } from "atom.io"
2
2
  import type { TransactionUpdate } from "atom.io"
3
+ import { IMPLICIT, getJsonToken, getUpdateToken } from "atom.io/internal"
3
4
  import type { JsonIO } from "atom.io/json"
4
5
  import { SyncGroup } from "atom.io/realtime"
5
- import { completeUpdateAtoms } from "atom.io/realtime-server"
6
+ // import { completeUpdateAtoms } from "atom.io/realtime-server"
6
7
 
7
8
  const redactorAtoms = selectorFamily<
8
9
  (update: TransactionUpdate<any>) => TransactionUpdate<any>,
@@ -20,14 +21,21 @@ const redactorAtoms = selectorFamily<
20
21
  }
21
22
 
22
23
  const userPerspectiveTokens = syncGroup.perspectives.flatMap(
23
- ({ perspectiveAtoms, resourceAtoms }) => {
24
- const userPerspectiveToken = find(perspectiveAtoms, userId)
24
+ ({ viewAtoms }) => {
25
+ const userPerspectiveToken = find(viewAtoms, userId)
25
26
  const userPerspective = get(userPerspectiveToken)
26
- const visibleTokens = [...userPerspective].map((subKey) => {
27
- const resourceToken = find(resourceAtoms, subKey)
28
- return resourceToken.key
27
+ const visibleTokens = [...userPerspective].map((token) => {
28
+ return token.type === `mutable_atom`
29
+ ? getUpdateToken(token).key
30
+ : token.key
29
31
  })
30
-
32
+ IMPLICIT.STORE.logger.info(
33
+ `🔭`,
34
+ `continuity`,
35
+ syncGroupKey,
36
+ `${userId} can see ${visibleTokens.length} tokens in ${viewAtoms.key}`,
37
+ visibleTokens,
38
+ )
31
39
  return visibleTokens
32
40
  },
33
41
  )
@@ -36,6 +44,14 @@ const redactorAtoms = selectorFamily<
36
44
  visible: string[],
37
45
  transactionUpdate: TransactionUpdate<any>,
38
46
  ): TransactionUpdate<any> => {
47
+ IMPLICIT.STORE.logger.info(
48
+ `🖌`,
49
+ `continuity`,
50
+ syncGroupKey,
51
+ `redacting updates from ${transactionUpdate.epoch}:${transactionUpdate.key}:${transactionUpdate.id}`,
52
+ visible,
53
+ transactionUpdate.updates,
54
+ )
39
55
  const updates = transactionUpdate.updates
40
56
  .filter((update) => {
41
57
  if (`newValue` in update) {
@@ -57,8 +73,10 @@ const redactorAtoms = selectorFamily<
57
73
  }
58
74
  const filter: (updates: TransactionUpdate<any>) => TransactionUpdate<any> =
59
75
  (update) => {
60
- const visibleKeys: string[] = syncGroup.globals.map(
61
- (atomToken) => atomToken.key,
76
+ const visibleKeys: string[] = syncGroup.globals.map((atomToken) =>
77
+ atomToken.type === `mutable_atom`
78
+ ? getUpdateToken(atomToken).key
79
+ : atomToken.key,
62
80
  )
63
81
  visibleKeys.push(...userPerspectiveTokens)
64
82
  return filterTransactionUpdate(visibleKeys, update)
@@ -66,25 +84,26 @@ const redactorAtoms = selectorFamily<
66
84
  return filter
67
85
  },
68
86
  })
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
- })
87
+ // export const occludedUpdateSelectors = selectorFamily<
88
+ // Pick<
89
+ // TransactionUpdate<JsonIO>,
90
+ // `epoch` | `id` | `key` | `output` | `updates`
91
+ // > | null,
92
+ // { userId: string; syncGroupKey: string; updateId: string }
93
+ // >({
94
+ // key: `occludedUpdate`,
95
+ // get:
96
+ // ({ userId, syncGroupKey, updateId }) =>
97
+ // ({ get, find }) => {
98
+ // const updateState = find(completeUpdateAtoms, updateId)
99
+ // const update = get(updateState)
100
+ // const redactorKey = { userId, syncGroupKey }
101
+ // const redactorState = find(redactorAtoms, redactorKey)
102
+ // const redact = get(redactorState)
103
+ // if (update) {
104
+ // // return redact(update)
105
+ // return update
106
+ // }
107
+ // return null
108
+ // },
109
+ // })