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
package/data/src/join.ts CHANGED
@@ -14,7 +14,10 @@ import {
14
14
  createMutableAtomFamily,
15
15
  createRegularAtomFamily,
16
16
  createSelectorFamily,
17
+ findInStore,
18
+ getFromStore,
17
19
  getJsonFamily,
20
+ setIntoStore,
18
21
  } from "atom.io/internal"
19
22
  import type { Json } from "atom.io/json"
20
23
  import type { SetRTXJson } from "atom.io/transceivers/set-rtx"
@@ -124,7 +127,9 @@ export class Join<
124
127
  const Cardinality extends `1:1` | `1:n` | `n:n`,
125
128
  const Content extends Json.Object | null = null,
126
129
  > {
127
- private transactors: Transactors = TRANSACTORS
130
+ private options: JoinOptions<ASide, BSide, Cardinality, Content>
131
+ private defaultContent: Content | undefined
132
+ private transactors: Transactors
128
133
  public relations: Junction<ASide, BSide, Content>
129
134
  public states: JoinState<ASide, BSide, Cardinality, Content>
130
135
  public core: {
@@ -138,18 +143,42 @@ export class Join<
138
143
  transactors: Transactors,
139
144
  run: (join: Join<ASide, BSide, Cardinality, Content>) => void,
140
145
  ): void {
146
+ const originalTransactors = this.transactors
141
147
  this.transactors = transactors
142
148
  run(this)
143
- this.transactors = TRANSACTORS
149
+ this.transactors = originalTransactors
144
150
  }
151
+
152
+ public alternates: Map<string, Join<ASide, BSide, Cardinality, Content>>
153
+ public in(store: Store): Join<ASide, BSide, Cardinality, Content> {
154
+ const key = store.config.name
155
+ const alternate = this.alternates.get(key)
156
+ if (alternate) {
157
+ return alternate
158
+ }
159
+ const join = new Join(this.options, this.defaultContent, store)
160
+ this.alternates.set(key, join)
161
+ join.alternates = this.alternates
162
+ return join
163
+ }
164
+
145
165
  public constructor(
146
166
  options: JoinOptions<ASide, BSide, Cardinality, Content>,
147
167
  defaultContent: Content | undefined,
148
168
  store: Store = IMPLICIT.STORE,
149
169
  ) {
170
+ this.options = options
171
+ this.defaultContent = defaultContent
172
+ this.alternates = new Map()
173
+ this.alternates.set(store.config.name, this)
174
+ this.transactors = {
175
+ get: (token) => getFromStore(token, store),
176
+ set: (token, value) => setIntoStore(token, value, store),
177
+ find: ((token, key) => findInStore(token, key, store)) as typeof findState,
178
+ }
150
179
  const a: ASide = options.between[0]
151
180
  const b: BSide = options.between[1]
152
- const findRelatedKeysState = createMutableAtomFamily<
181
+ const relatedKeysAtoms = createMutableAtomFamily<
153
182
  SetRTX<string>,
154
183
  SetRTXJson<string>,
155
184
  string
@@ -163,19 +192,19 @@ export class Join<
163
192
  },
164
193
  store,
165
194
  )
166
- this.core = { findRelatedKeysState }
195
+ this.core = { findRelatedKeysState: relatedKeysAtoms }
167
196
  const getRelatedKeys: Read<(key: string) => SetRTX<string>> = (
168
- { get },
197
+ { find, get },
169
198
  key,
170
- ) => get(findRelatedKeysState(key))
199
+ ) => get(find(relatedKeysAtoms, key))
171
200
  const addRelation: Write<(a: string, b: string) => void> = (
172
201
  transactors,
173
202
  a,
174
203
  b,
175
204
  ) => {
176
205
  const { set, find } = transactors
177
- const aKeysState = find(findRelatedKeysState, a)
178
- const bKeysState = find(findRelatedKeysState, b)
206
+ const aKeysState = find(relatedKeysAtoms, a)
207
+ const bKeysState = find(relatedKeysAtoms, b)
179
208
  set(aKeysState, (aKeys) => aKeys.add(b))
180
209
  set(bKeysState, (bKeys) => bKeys.add(a))
181
210
  }
@@ -185,8 +214,8 @@ export class Join<
185
214
  b,
186
215
  ) => {
187
216
  const { find, set } = transactors
188
- const aKeysState = find(findRelatedKeysState, a)
189
- const bKeysState = find(findRelatedKeysState, b)
217
+ const aKeysState = find(relatedKeysAtoms, a)
218
+ const bKeysState = find(relatedKeysAtoms, b)
190
219
  set(aKeysState, (aKeys) => (aKeys.delete(b), aKeys))
191
220
  set(bKeysState, (bKeys) => (bKeys.delete(a), bKeys))
192
221
  }
@@ -194,14 +223,14 @@ export class Join<
194
223
  (a: string, newRelationsOfA: string[]) => void
195
224
  > = (transactors, a, newRelationsOfA) => {
196
225
  const { find, get, set } = transactors
197
- const relationsOfAState = find(findRelatedKeysState, a)
226
+ const relationsOfAState = find(relatedKeysAtoms, a)
198
227
  const currentRelationsOfA = get(relationsOfAState)
199
228
  for (const currentRelationB of currentRelationsOfA) {
200
229
  const remainsRelated = newRelationsOfA.includes(currentRelationB)
201
230
  if (remainsRelated) {
202
231
  continue
203
232
  }
204
- const relationsOfBState = find(findRelatedKeysState, currentRelationB)
233
+ const relationsOfBState = find(relatedKeysAtoms, currentRelationB)
205
234
  set(relationsOfBState, (relationsOfB) => {
206
235
  relationsOfB.delete(a)
207
236
  return relationsOfB
@@ -242,7 +271,7 @@ export class Join<
242
271
  (a: string, newRelationsOfA: string[]) => void
243
272
  > = (transactors, a, newRelationsOfA) => {
244
273
  const { find, set } = transactors
245
- const relationsOfAState = find(findRelatedKeysState, a)
274
+ const relationsOfAState = find(relatedKeysAtoms, a)
246
275
  set(relationsOfAState, (relationsOfA) => {
247
276
  relationsOfA.transaction((nextRelationsOfA) => {
248
277
  for (const newRelationB of newRelationsOfA) {
@@ -253,7 +282,7 @@ export class Join<
253
282
  return relationsOfA
254
283
  })
255
284
  for (const newRelationB of newRelationsOfA) {
256
- const newRelationsBState = find(findRelatedKeysState, newRelationB)
285
+ const newRelationsBState = find(relatedKeysAtoms, newRelationB)
257
286
  set(newRelationsBState, (newRelationsB) => {
258
287
  newRelationsB.add(a)
259
288
  return newRelationsB
@@ -280,24 +309,26 @@ export class Join<
280
309
  has: (a, b) => has(this.transactors, a, b),
281
310
  }
282
311
  let externalStore: ExternalStoreConfiguration<Content>
283
- let findContentState: RegularAtomFamily<Content, string>
312
+ let contentAtoms: RegularAtomFamily<Content, string>
284
313
  if (defaultContent) {
285
- findContentState = createRegularAtomFamily<Content, string>(
314
+ contentAtoms = createRegularAtomFamily<Content, string>(
286
315
  {
287
316
  key: `${options.key}/content`,
288
317
  default: defaultContent,
289
318
  },
290
319
  store,
291
320
  )
292
- const getContent: Read<(key: string) => Content | null> = ({ get }, key) =>
293
- get(findContentState(key))
321
+ const getContent: Read<(key: string) => Content | null> = (
322
+ { find, get },
323
+ key,
324
+ ) => get(find(contentAtoms, key))
294
325
  const setContent: Write<(key: string, content: Content) => void> = (
295
- transactors,
326
+ { find, set },
296
327
  key,
297
328
  content,
298
- ) => transactors.set(findContentState(key), content)
299
- const deleteContent: Write<(key: string) => void> = (_, key) =>
300
- dispose(findContentState(key))
329
+ ) => set(find(contentAtoms, key), content)
330
+ const deleteContent: Write<(key: string) => void> = ({ find }, key) =>
331
+ dispose(find(contentAtoms, key))
301
332
  const externalStoreWithContentConfiguration = {
302
333
  getContent: (contentKey: string) => {
303
334
  const content = getContent(this.transactors, contentKey)
@@ -329,8 +360,9 @@ export class Join<
329
360
  key: `${options.key}/singleRelatedKey`,
330
361
  get:
331
362
  (key) =>
332
- ({ get }) => {
333
- const relatedKeys = get(findRelatedKeysState(key))
363
+ ({ find, get }) => {
364
+ const relatedKeysState = find(relatedKeysAtoms, key)
365
+ const relatedKeys = get(relatedKeysState)
334
366
  for (const relatedKey of relatedKeys) {
335
367
  return relatedKey
336
368
  }
@@ -345,9 +377,10 @@ export class Join<
345
377
  key: `${options.key}/multipleRelatedKeys`,
346
378
  get:
347
379
  (key) =>
348
- ({ get }) => {
349
- const jsonFamily = getJsonFamily(findRelatedKeysState, store)
350
- const json = get(jsonFamily(key))
380
+ ({ find, get }) => {
381
+ const jsonFamily = getJsonFamily(relatedKeysAtoms, store)
382
+ const jsonState = find(jsonFamily, key)
383
+ const json = get(jsonState)
351
384
  return json.members
352
385
  },
353
386
  },
@@ -360,11 +393,14 @@ export class Join<
360
393
  key: `${options.key}/singleRelatedEntry`,
361
394
  get:
362
395
  (key) =>
363
- ({ get }) => {
364
- const relatedKeys = get(findRelatedKeysState(key))
396
+ ({ find, get }) => {
397
+ const relatedKeysState = find(relatedKeysAtoms, key)
398
+ const relatedKeys = get(relatedKeysState)
365
399
  for (const relatedKey of relatedKeys) {
366
400
  const contentKey = relations.makeContentKey(key, relatedKey)
367
- return [relatedKey, get(findContentState(contentKey))]
401
+ const contentState = find(contentAtoms, contentKey)
402
+ const content = get(contentState)
403
+ return [relatedKey, content]
368
404
  }
369
405
  return null
370
406
  },
@@ -377,12 +413,14 @@ export class Join<
377
413
  key: `${options.key}/multipleRelatedEntries`,
378
414
  get:
379
415
  (key) =>
380
- ({ get }) => {
381
- const jsonFamily = getJsonFamily(findRelatedKeysState, store)
416
+ ({ find, get }) => {
417
+ const jsonFamily = getJsonFamily(relatedKeysAtoms, store)
382
418
  const json = get(jsonFamily(key))
383
419
  return json.members.map((relatedKey) => {
384
420
  const contentKey = relations.makeContentKey(key, relatedKey)
385
- return [relatedKey, get(findContentState(contentKey))]
421
+ const contentState = find(contentAtoms, contentKey)
422
+ const content = get(contentState)
423
+ return [relatedKey, content]
386
424
  })
387
425
  },
388
426
  },
@@ -469,6 +507,7 @@ export class Join<
469
507
  }
470
508
  }
471
509
  }
510
+
472
511
  export function join<
473
512
  const ASide extends string,
474
513
  const BSide extends string,
@@ -480,6 +519,7 @@ export function join<
480
519
  ): {
481
520
  readonly relations: Junction<ASide, BSide, null>
482
521
  readonly states: JoinState<ASide, BSide, Cardinality, null>
522
+ readonly in: (store: Store) => Join<ASide, BSide, Cardinality>
483
523
  readonly transact: (
484
524
  transactors: Transactors,
485
525
  run: (join: Join<ASide, BSide, Cardinality, null>) => void,
@@ -504,6 +544,7 @@ export function join<
504
544
  ): {
505
545
  readonly relations: Junction<ASide, BSide, Content>
506
546
  readonly states: JoinState<ASide, BSide, Cardinality, Content>
547
+ readonly in: (store: Store) => Join<ASide, BSide, Cardinality, Content>
507
548
  readonly transact: (
508
549
  transactors: Transactors,
509
550
  run: (join: Join<ASide, BSide, Cardinality, Content>) => void,
@@ -21,7 +21,7 @@ export function structFamily<
21
21
  K & string
22
22
  >}State`]: AtomIO.RegularAtomFamily<Struct[K], string>
23
23
  },
24
- AtomIO.ReadonlySelectorFamily<Struct>,
24
+ AtomIO.ReadonlySelectorFamilyToken<Struct, string>,
25
25
  ] {
26
26
  const atoms: {
27
27
  [K in keyof Struct as `find${Capitalize<Key & string>}${Capitalize<
@@ -38,21 +38,22 @@ export function structFamily<
38
38
  )
39
39
  return acc
40
40
  }, {} as any)
41
- const findStructState = createSelectorFamily(
42
- {
43
- key: options.key,
44
- get:
45
- (id) =>
46
- ({ get }) => {
47
- return Object.keys(options.default).reduce((acc, subKey) => {
48
- acc[subKey] = get(
49
- (atoms as any)[nameFamily(options.key, subKey)](id),
50
- )
51
- return acc
52
- }, {} as any)
53
- },
54
- },
55
- IMPLICIT.STORE,
56
- )
41
+ const findStructState: AtomIO.ReadonlySelectorFamilyToken<Struct, string> =
42
+ createSelectorFamily(
43
+ {
44
+ key: options.key,
45
+ get:
46
+ (id) =>
47
+ ({ get }) => {
48
+ return Object.keys(options.default).reduce((acc, subKey) => {
49
+ acc[subKey] = get(
50
+ (atoms as any)[nameFamily(options.key, subKey)](id),
51
+ )
52
+ return acc
53
+ }, {} as any)
54
+ },
55
+ },
56
+ IMPLICIT.STORE,
57
+ )
57
58
  return [atoms, findStructState]
58
59
  }
@@ -0,0 +1,289 @@
1
+ import { getFromStore, assignTransactionToContinuity, subscribeToTransaction, setIntoStore, setEpochNumberOfContinuity, getEpochNumberOfContinuity, isRootStore, ingestTransactionUpdate, actUponStore } from 'atom.io/internal';
2
+ import { optimisticUpdateQueue, confirmedUpdateQueue } from 'atom.io/realtime-client';
3
+
4
+ // realtime-client/src/sync-continuity.ts
5
+ function syncContinuity(continuity, socket, store) {
6
+ const continuityKey = continuity.key;
7
+ const optimisticUpdates = getFromStore(optimisticUpdateQueue, store);
8
+ const confirmedUpdates = getFromStore(confirmedUpdateQueue, store);
9
+ const initializeContinuity = (epoch, payload) => {
10
+ let i = 0;
11
+ let k = ``;
12
+ let v = null;
13
+ for (const x of payload) {
14
+ if (i % 2 === 0) {
15
+ k = x;
16
+ } else {
17
+ v = x;
18
+ setIntoStore(k, v, store);
19
+ }
20
+ i++;
21
+ }
22
+ setEpochNumberOfContinuity(continuityKey, epoch, store);
23
+ };
24
+ socket.off(`continuity-init:${continuityKey}`);
25
+ socket.on(`continuity-init:${continuityKey}`, initializeContinuity);
26
+ const registerAndAttemptConfirmedUpdate = (confirmedUpdate) => {
27
+ function reconcileEpoch(optimisticUpdate, confirmedUpdate2) {
28
+ store.logger.info(`\u2696\uFE0F`, `continuity`, continuityKey, `reconciling updates`);
29
+ setIntoStore(
30
+ optimisticUpdateQueue,
31
+ (queue) => {
32
+ queue.shift();
33
+ return queue;
34
+ },
35
+ store
36
+ );
37
+ if (optimisticUpdate.id === confirmedUpdate2.id) {
38
+ const clientResult = JSON.stringify(optimisticUpdate.updates);
39
+ const serverResult = JSON.stringify(confirmedUpdate2.updates);
40
+ if (clientResult === serverResult) {
41
+ store.logger.info(
42
+ `\u2705`,
43
+ `continuity`,
44
+ continuityKey,
45
+ `results for ${optimisticUpdate.id} match between client and server`
46
+ );
47
+ socket.emit(`ack:${continuityKey}`, confirmedUpdate2.epoch);
48
+ return;
49
+ }
50
+ } else {
51
+ store.logger.info(
52
+ `\u274C`,
53
+ `continuity`,
54
+ continuityKey,
55
+ `thought update #${confirmedUpdate2.epoch} was ${optimisticUpdate.key}:${optimisticUpdate.id}, but it was actually ${confirmedUpdate2.key}:${confirmedUpdate2.id}`
56
+ );
57
+ }
58
+ const reversedOptimisticUpdates = optimisticUpdates.toReversed();
59
+ for (const subsequentOptimistic of reversedOptimisticUpdates) {
60
+ ingestTransactionUpdate(`oldValue`, subsequentOptimistic, store);
61
+ }
62
+ store.logger.info(
63
+ `\u23EA`,
64
+ `continuity`,
65
+ continuityKey,
66
+ `undid optimistic updates:`,
67
+ reversedOptimisticUpdates
68
+ );
69
+ ingestTransactionUpdate(`oldValue`, optimisticUpdate, store);
70
+ store.logger.info(
71
+ `\u23EA`,
72
+ `continuity`,
73
+ continuityKey,
74
+ `undid zeroth optimistic update`,
75
+ optimisticUpdate
76
+ );
77
+ ingestTransactionUpdate(`newValue`, confirmedUpdate2, store);
78
+ store.logger.info(
79
+ `\u23E9`,
80
+ `continuity`,
81
+ continuityKey,
82
+ `applied confirmed update`,
83
+ confirmedUpdate2
84
+ );
85
+ socket.emit(`ack:${continuityKey}`, confirmedUpdate2.epoch);
86
+ for (const subsequentOptimistic of optimisticUpdates) {
87
+ const token = {
88
+ type: `transaction`,
89
+ key: subsequentOptimistic.key
90
+ };
91
+ const { id, params } = subsequentOptimistic;
92
+ actUponStore(token, id, store)(...params);
93
+ }
94
+ store.logger.info(
95
+ `\u23E9`,
96
+ `continuity`,
97
+ continuityKey,
98
+ `reapplied subsequent optimistic updates:`,
99
+ optimisticUpdates
100
+ );
101
+ }
102
+ store.logger.info(
103
+ `\u2696\uFE0F`,
104
+ `continuity`,
105
+ continuityKey,
106
+ `integrating confirmed update`,
107
+ { confirmedUpdate, confirmedUpdates, optimisticUpdates }
108
+ );
109
+ const zerothOptimisticUpdate = optimisticUpdates[0];
110
+ if (zerothOptimisticUpdate) {
111
+ store.logger.info(
112
+ `\u2696\uFE0F`,
113
+ `continuity`,
114
+ continuityKey,
115
+ `has optimistic updates to reconcile`
116
+ );
117
+ if (confirmedUpdate.epoch === zerothOptimisticUpdate.epoch) {
118
+ store.logger.info(
119
+ `\u2696\uFE0F`,
120
+ `continuity`,
121
+ continuityKey,
122
+ `epoch of confirmed update #${confirmedUpdate.epoch} matches zeroth optimistic update`
123
+ );
124
+ reconcileEpoch(zerothOptimisticUpdate, confirmedUpdate);
125
+ for (const nextConfirmed of confirmedUpdates) {
126
+ const nextOptimistic = optimisticUpdates[0];
127
+ if (nextConfirmed.epoch === (nextOptimistic == null ? void 0 : nextOptimistic.epoch)) {
128
+ reconcileEpoch(nextOptimistic, nextConfirmed);
129
+ } else {
130
+ break;
131
+ }
132
+ }
133
+ } else {
134
+ store.logger.info(
135
+ `\u2696\uFE0F`,
136
+ `continuity`,
137
+ continuityKey,
138
+ `epoch of confirmed update #${confirmedUpdate.epoch} does not match zeroth optimistic update #${zerothOptimisticUpdate.epoch}`
139
+ );
140
+ const confirmedUpdateIsAlreadyEnqueued = confirmedUpdates.some(
141
+ (update) => update.epoch === confirmedUpdate.epoch
142
+ );
143
+ if (!confirmedUpdateIsAlreadyEnqueued) {
144
+ store.logger.info(
145
+ `\u{1F448}`,
146
+ `continuity`,
147
+ continuityKey,
148
+ `pushing confirmed update to queue`,
149
+ confirmedUpdate
150
+ );
151
+ setIntoStore(
152
+ confirmedUpdateQueue,
153
+ (queue) => {
154
+ queue.push(confirmedUpdate);
155
+ queue.sort((a, b) => a.epoch - b.epoch);
156
+ return queue;
157
+ },
158
+ store
159
+ );
160
+ }
161
+ }
162
+ } else {
163
+ store.logger.info(
164
+ `\u2696\uFE0F`,
165
+ `continuity`,
166
+ continuityKey,
167
+ `has no optimistic updates to deal with`
168
+ );
169
+ const continuityEpoch = getEpochNumberOfContinuity(continuityKey, store);
170
+ const isRoot = isRootStore(store);
171
+ if (isRoot && continuityEpoch === confirmedUpdate.epoch - 1) {
172
+ store.logger.info(
173
+ `\u2705`,
174
+ `continuity`,
175
+ continuityKey,
176
+ `integrating update #${confirmedUpdate.epoch} (${confirmedUpdate.key} ${confirmedUpdate.id})`
177
+ );
178
+ ingestTransactionUpdate(`newValue`, confirmedUpdate, store);
179
+ socket.emit(`ack:${continuityKey}`, confirmedUpdate.epoch);
180
+ setEpochNumberOfContinuity(continuityKey, confirmedUpdate.epoch, store);
181
+ } else if (isRoot && continuityEpoch !== void 0) {
182
+ store.logger.info(
183
+ `\u2696\uFE0F`,
184
+ `continuity`,
185
+ continuityKey,
186
+ `received update #${confirmedUpdate.epoch} but still waiting for update #${continuityEpoch + 1}`,
187
+ {
188
+ clientEpoch: continuityEpoch,
189
+ serverEpoch: confirmedUpdate.epoch
190
+ }
191
+ );
192
+ const confirmedUpdateIsAlreadyEnqueued = confirmedUpdates.some(
193
+ (update) => update.epoch === confirmedUpdate.epoch
194
+ );
195
+ if (confirmedUpdateIsAlreadyEnqueued) {
196
+ store.logger.info(
197
+ `\u{1F44D}`,
198
+ `continuity`,
199
+ continuityKey,
200
+ `confirmed update #${confirmedUpdate.epoch} is already enqueued`
201
+ );
202
+ } else {
203
+ store.logger.info(
204
+ `\u{1F448}`,
205
+ `continuity`,
206
+ continuityKey,
207
+ `pushing confirmed update #${confirmedUpdate.epoch} to queue`
208
+ );
209
+ setIntoStore(
210
+ confirmedUpdateQueue,
211
+ (queue) => {
212
+ queue.push(confirmedUpdate);
213
+ queue.sort((a, b) => a.epoch - b.epoch);
214
+ return queue;
215
+ },
216
+ store
217
+ );
218
+ }
219
+ }
220
+ }
221
+ };
222
+ socket.off(`tx-new:${continuityKey}`);
223
+ socket.on(`tx-new:${continuityKey}`, registerAndAttemptConfirmedUpdate);
224
+ const unsubscribeFunctions = continuity.actions.map((transaction) => {
225
+ assignTransactionToContinuity(continuityKey, transaction.key, store);
226
+ const unsubscribeFromTransactionUpdates = subscribeToTransaction(
227
+ transaction,
228
+ (clientUpdate) => {
229
+ store.logger.info(
230
+ `\u{1F91E}`,
231
+ `continuity`,
232
+ continuityKey,
233
+ `enqueuing optimistic update`
234
+ );
235
+ const optimisticUpdateIndex = optimisticUpdates.findIndex(
236
+ (update) => update.id === clientUpdate.id
237
+ );
238
+ if (optimisticUpdateIndex === -1) {
239
+ store.logger.info(
240
+ `\u{1F91E}`,
241
+ `continuity`,
242
+ continuityKey,
243
+ `enqueuing new optimistic update`
244
+ );
245
+ setIntoStore(
246
+ optimisticUpdateQueue,
247
+ (queue) => {
248
+ queue.push(clientUpdate);
249
+ queue.sort((a, b) => a.epoch - b.epoch);
250
+ return queue;
251
+ },
252
+ store
253
+ );
254
+ } else {
255
+ store.logger.info(
256
+ `\u{1F91E}`,
257
+ `continuity`,
258
+ continuityKey,
259
+ `replacing existing optimistic update at index ${optimisticUpdateIndex}`
260
+ );
261
+ setIntoStore(
262
+ optimisticUpdateQueue,
263
+ (queue) => {
264
+ queue[optimisticUpdateIndex] = clientUpdate;
265
+ return queue;
266
+ },
267
+ store
268
+ );
269
+ }
270
+ socket.emit(`tx-run:${continuityKey}`, clientUpdate);
271
+ },
272
+ `tx-run:${continuityKey}`,
273
+ store
274
+ );
275
+ return unsubscribeFromTransactionUpdates;
276
+ });
277
+ socket.emit(`get:${continuityKey}`);
278
+ return () => {
279
+ socket.off(`continuity-init:${continuityKey}`);
280
+ socket.off(`tx-new:${continuityKey}`);
281
+ for (const unsubscribe of unsubscribeFunctions)
282
+ unsubscribe();
283
+ socket.emit(`unsub:${continuityKey}`);
284
+ };
285
+ }
286
+
287
+ export { syncContinuity };
288
+ //# sourceMappingURL=out.js.map
289
+ //# sourceMappingURL=chunk-OEVFAUPE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../realtime-client/src/sync-continuity.ts"],"names":["confirmedUpdate"],"mappings":"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","sourcesContent":["import type * as AtomIO from \"atom.io\"\nimport type { Store } from \"atom.io/internal\"\nimport {\n\tactUponStore,\n\tassignTransactionToContinuity,\n\tgetEpochNumberOfContinuity,\n\tgetFromStore,\n\tingestTransactionUpdate,\n\tisRootStore,\n\tsetEpochNumberOfContinuity,\n\tsetIntoStore,\n\tsubscribeToTransaction,\n} from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport type { ContinuityToken } from \"atom.io/realtime\"\nimport {\n\tconfirmedUpdateQueue,\n\toptimisticUpdateQueue,\n} from \"atom.io/realtime-client\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function syncContinuity<ƒ extends AtomIO.ƒn>(\n\tcontinuity: ContinuityToken,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tconst continuityKey = continuity.key\n\tconst optimisticUpdates = getFromStore(optimisticUpdateQueue, store)\n\tconst confirmedUpdates = getFromStore(confirmedUpdateQueue, store)\n\n\tconst initializeContinuity = (epoch: number, payload: Json.Array) => {\n\t\tlet i = 0\n\t\tlet k: any = ``\n\t\tlet v: any = null\n\t\tfor (const x of payload) {\n\t\t\tif (i % 2 === 0) {\n\t\t\t\tk = x\n\t\t\t} else {\n\t\t\t\tv = x\n\t\t\t\tsetIntoStore(k, v, store)\n\t\t\t}\n\t\t\ti++\n\t\t}\n\t\tsetEpochNumberOfContinuity(continuityKey, epoch, store)\n\t}\n\tsocket.off(`continuity-init:${continuityKey}`)\n\tsocket.on(`continuity-init:${continuityKey}`, initializeContinuity)\n\n\tconst registerAndAttemptConfirmedUpdate = (\n\t\tconfirmedUpdate: AtomIO.TransactionUpdate<ƒ>,\n\t) => {\n\t\tfunction reconcileEpoch(\n\t\t\toptimisticUpdate: AtomIO.TransactionUpdate<any>,\n\t\t\tconfirmedUpdate: AtomIO.TransactionUpdate<any>,\n\t\t): void {\n\t\t\tstore.logger.info(`⚖️`, `continuity`, continuityKey, `reconciling updates`)\n\t\t\tsetIntoStore(\n\t\t\t\toptimisticUpdateQueue,\n\t\t\t\t(queue) => {\n\t\t\t\t\tqueue.shift()\n\t\t\t\t\treturn queue\n\t\t\t\t},\n\t\t\t\tstore,\n\t\t\t)\n\t\t\tif (optimisticUpdate.id === confirmedUpdate.id) {\n\t\t\t\tconst clientResult = JSON.stringify(optimisticUpdate.updates)\n\t\t\t\tconst serverResult = JSON.stringify(confirmedUpdate.updates)\n\t\t\t\tif (clientResult === serverResult) {\n\t\t\t\t\tstore.logger.info(\n\t\t\t\t\t\t`✅`,\n\t\t\t\t\t\t`continuity`,\n\t\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t\t`results for ${optimisticUpdate.id} match between client and server`,\n\t\t\t\t\t)\n\t\t\t\t\tsocket.emit(`ack:${continuityKey}`, confirmedUpdate.epoch)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// id mismatch\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`❌`,\n\t\t\t\t\t`continuity`,\n\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t`thought update #${confirmedUpdate.epoch} was ${optimisticUpdate.key}:${optimisticUpdate.id}, but it was actually ${confirmedUpdate.key}:${confirmedUpdate.id}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst reversedOptimisticUpdates = optimisticUpdates.toReversed()\n\t\t\tfor (const subsequentOptimistic of reversedOptimisticUpdates) {\n\t\t\t\tingestTransactionUpdate(`oldValue`, subsequentOptimistic, store)\n\t\t\t}\n\t\t\tstore.logger.info(\n\t\t\t\t`⏪`,\n\t\t\t\t`continuity`,\n\t\t\t\tcontinuityKey,\n\t\t\t\t`undid optimistic updates:`,\n\t\t\t\treversedOptimisticUpdates,\n\t\t\t)\n\t\t\tingestTransactionUpdate(`oldValue`, optimisticUpdate, store)\n\t\t\tstore.logger.info(\n\t\t\t\t`⏪`,\n\t\t\t\t`continuity`,\n\t\t\t\tcontinuityKey,\n\t\t\t\t`undid zeroth optimistic update`,\n\t\t\t\toptimisticUpdate,\n\t\t\t)\n\t\t\tingestTransactionUpdate(`newValue`, confirmedUpdate, store)\n\t\t\tstore.logger.info(\n\t\t\t\t`⏩`,\n\t\t\t\t`continuity`,\n\t\t\t\tcontinuityKey,\n\t\t\t\t`applied confirmed update`,\n\t\t\t\tconfirmedUpdate,\n\t\t\t)\n\t\t\tsocket.emit(`ack:${continuityKey}`, confirmedUpdate.epoch)\n\n\t\t\tfor (const subsequentOptimistic of optimisticUpdates) {\n\t\t\t\tconst token = {\n\t\t\t\t\ttype: `transaction`,\n\t\t\t\t\tkey: subsequentOptimistic.key,\n\t\t\t\t} as const\n\t\t\t\tconst { id, params } = subsequentOptimistic\n\t\t\t\tactUponStore(token, id, store)(...params)\n\t\t\t}\n\t\t\tstore.logger.info(\n\t\t\t\t`⏩`,\n\t\t\t\t`continuity`,\n\t\t\t\tcontinuityKey,\n\t\t\t\t`reapplied subsequent optimistic updates:`,\n\t\t\t\toptimisticUpdates,\n\t\t\t)\n\t\t}\n\n\t\tstore.logger.info(\n\t\t\t`⚖️`,\n\t\t\t`continuity`,\n\t\t\tcontinuityKey,\n\t\t\t`integrating confirmed update`,\n\t\t\t{ confirmedUpdate, confirmedUpdates, optimisticUpdates },\n\t\t)\n\t\tconst zerothOptimisticUpdate = optimisticUpdates[0]\n\t\tif (zerothOptimisticUpdate) {\n\t\t\tstore.logger.info(\n\t\t\t\t`⚖️`,\n\t\t\t\t`continuity`,\n\t\t\t\tcontinuityKey,\n\t\t\t\t`has optimistic updates to reconcile`,\n\t\t\t)\n\t\t\tif (confirmedUpdate.epoch === zerothOptimisticUpdate.epoch) {\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`⚖️`,\n\t\t\t\t\t`continuity`,\n\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t`epoch of confirmed update #${confirmedUpdate.epoch} matches zeroth optimistic update`,\n\t\t\t\t)\n\t\t\t\treconcileEpoch(zerothOptimisticUpdate, confirmedUpdate)\n\t\t\t\tfor (const nextConfirmed of confirmedUpdates) {\n\t\t\t\t\tconst nextOptimistic = optimisticUpdates[0]\n\t\t\t\t\tif (nextConfirmed.epoch === nextOptimistic?.epoch) {\n\t\t\t\t\t\treconcileEpoch(nextOptimistic, nextConfirmed)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// epoch mismatch\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`⚖️`,\n\t\t\t\t\t`continuity`,\n\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t`epoch of confirmed update #${confirmedUpdate.epoch} does not match zeroth optimistic update #${zerothOptimisticUpdate.epoch}`,\n\t\t\t\t)\n\t\t\t\tconst confirmedUpdateIsAlreadyEnqueued = confirmedUpdates.some(\n\t\t\t\t\t(update) => update.epoch === confirmedUpdate.epoch,\n\t\t\t\t)\n\t\t\t\tif (!confirmedUpdateIsAlreadyEnqueued) {\n\t\t\t\t\tstore.logger.info(\n\t\t\t\t\t\t`👈`,\n\t\t\t\t\t\t`continuity`,\n\t\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t\t`pushing confirmed update to queue`,\n\t\t\t\t\t\tconfirmedUpdate,\n\t\t\t\t\t)\n\t\t\t\t\tsetIntoStore(\n\t\t\t\t\t\tconfirmedUpdateQueue,\n\t\t\t\t\t\t(queue) => {\n\t\t\t\t\t\t\tqueue.push(confirmedUpdate)\n\t\t\t\t\t\t\tqueue.sort((a, b) => a.epoch - b.epoch)\n\t\t\t\t\t\t\treturn queue\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tstore.logger.info(\n\t\t\t\t`⚖️`,\n\t\t\t\t`continuity`,\n\t\t\t\tcontinuityKey,\n\t\t\t\t`has no optimistic updates to deal with`,\n\t\t\t)\n\t\t\tconst continuityEpoch = getEpochNumberOfContinuity(continuityKey, store)\n\t\t\tconst isRoot = isRootStore(store)\n\n\t\t\tif (isRoot && continuityEpoch === confirmedUpdate.epoch - 1) {\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`✅`,\n\t\t\t\t\t`continuity`,\n\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t`integrating update #${confirmedUpdate.epoch} (${confirmedUpdate.key} ${confirmedUpdate.id})`,\n\t\t\t\t)\n\t\t\t\tingestTransactionUpdate(`newValue`, confirmedUpdate, store)\n\t\t\t\tsocket.emit(`ack:${continuityKey}`, confirmedUpdate.epoch)\n\t\t\t\tsetEpochNumberOfContinuity(continuityKey, confirmedUpdate.epoch, store)\n\t\t\t} else if (isRoot && continuityEpoch !== undefined) {\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`⚖️`,\n\t\t\t\t\t`continuity`,\n\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t`received update #${\n\t\t\t\t\t\tconfirmedUpdate.epoch\n\t\t\t\t\t} but still waiting for update #${continuityEpoch + 1}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tclientEpoch: continuityEpoch,\n\t\t\t\t\t\tserverEpoch: confirmedUpdate.epoch,\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tconst confirmedUpdateIsAlreadyEnqueued = confirmedUpdates.some(\n\t\t\t\t\t(update) => update.epoch === confirmedUpdate.epoch,\n\t\t\t\t)\n\t\t\t\tif (confirmedUpdateIsAlreadyEnqueued) {\n\t\t\t\t\tstore.logger.info(\n\t\t\t\t\t\t`👍`,\n\t\t\t\t\t\t`continuity`,\n\t\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t\t`confirmed update #${confirmedUpdate.epoch} is already enqueued`,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tstore.logger.info(\n\t\t\t\t\t\t`👈`,\n\t\t\t\t\t\t`continuity`,\n\t\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t\t`pushing confirmed update #${confirmedUpdate.epoch} to queue`,\n\t\t\t\t\t)\n\t\t\t\t\tsetIntoStore(\n\t\t\t\t\t\tconfirmedUpdateQueue,\n\t\t\t\t\t\t(queue) => {\n\t\t\t\t\t\t\tqueue.push(confirmedUpdate)\n\t\t\t\t\t\t\tqueue.sort((a, b) => a.epoch - b.epoch)\n\t\t\t\t\t\t\treturn queue\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tsocket.off(`tx-new:${continuityKey}`)\n\tsocket.on(`tx-new:${continuityKey}`, registerAndAttemptConfirmedUpdate)\n\n\tconst unsubscribeFunctions = continuity.actions.map((transaction) => {\n\t\tassignTransactionToContinuity(continuityKey, transaction.key, store)\n\t\tconst unsubscribeFromTransactionUpdates = subscribeToTransaction(\n\t\t\ttransaction,\n\t\t\t(clientUpdate) => {\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`🤞`,\n\t\t\t\t\t`continuity`,\n\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t`enqueuing optimistic update`,\n\t\t\t\t)\n\t\t\t\tconst optimisticUpdateIndex = optimisticUpdates.findIndex(\n\t\t\t\t\t(update) => update.id === clientUpdate.id,\n\t\t\t\t)\n\t\t\t\tif (optimisticUpdateIndex === -1) {\n\t\t\t\t\tstore.logger.info(\n\t\t\t\t\t\t`🤞`,\n\t\t\t\t\t\t`continuity`,\n\t\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t\t`enqueuing new optimistic update`,\n\t\t\t\t\t)\n\t\t\t\t\tsetIntoStore(\n\t\t\t\t\t\toptimisticUpdateQueue,\n\t\t\t\t\t\t(queue) => {\n\t\t\t\t\t\t\tqueue.push(clientUpdate)\n\t\t\t\t\t\t\tqueue.sort((a, b) => a.epoch - b.epoch)\n\t\t\t\t\t\t\treturn queue\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstore,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tstore.logger.info(\n\t\t\t\t\t\t`🤞`,\n\t\t\t\t\t\t`continuity`,\n\t\t\t\t\t\tcontinuityKey,\n\t\t\t\t\t\t`replacing existing optimistic update at index ${optimisticUpdateIndex}`,\n\t\t\t\t\t)\n\t\t\t\t\tsetIntoStore(\n\t\t\t\t\t\toptimisticUpdateQueue,\n\t\t\t\t\t\t(queue) => {\n\t\t\t\t\t\t\tqueue[optimisticUpdateIndex] = clientUpdate\n\t\t\t\t\t\t\treturn queue\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tsocket.emit(`tx-run:${continuityKey}`, clientUpdate)\n\t\t\t},\n\t\t\t`tx-run:${continuityKey}`,\n\t\t\tstore,\n\t\t)\n\t\treturn unsubscribeFromTransactionUpdates\n\t})\n\n\tsocket.emit(`get:${continuityKey}`)\n\treturn () => {\n\t\tsocket.off(`continuity-init:${continuityKey}`)\n\t\tsocket.off(`tx-new:${continuityKey}`)\n\t\tfor (const unsubscribe of unsubscribeFunctions) unsubscribe()\n\t\tsocket.emit(`unsub:${continuityKey}`)\n\t}\n}\n"]}