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.
- package/data/dist/index.cjs +62 -40
- package/data/dist/index.cjs.map +1 -1
- package/data/dist/index.d.ts +8 -2
- package/data/dist/index.js +64 -42
- package/data/dist/index.js.map +1 -1
- package/data/src/dict.ts +8 -4
- package/data/src/join.ts +74 -33
- package/data/src/struct-family.ts +18 -17
- package/dist/chunk-OEVFAUPE.js +289 -0
- package/dist/chunk-OEVFAUPE.js.map +1 -0
- package/dist/index.cjs +36 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +64 -53
- package/dist/index.js +15 -36
- package/dist/index.js.map +1 -1
- package/internal/dist/index.cjs +211 -81
- package/internal/dist/index.cjs.map +1 -1
- package/internal/dist/index.d.ts +100 -72
- package/internal/dist/index.js +200 -75
- package/internal/dist/index.js.map +1 -1
- package/internal/src/arbitrary.ts +3 -0
- package/internal/src/atom/create-regular-atom.ts +2 -3
- package/internal/src/caching.ts +8 -6
- package/internal/src/families/find-in-store.ts +16 -0
- package/internal/src/get-environment-data.ts +4 -7
- package/internal/src/get-state/get-from-store.ts +14 -0
- package/internal/src/get-state/index.ts +2 -0
- package/internal/src/{read-or-compute-value.ts → get-state/read-or-compute-value.ts} +3 -3
- package/internal/src/index.ts +7 -6
- package/internal/src/ingest-updates/ingest-atom-update.ts +2 -2
- package/internal/src/ingest-updates/ingest-transaction-update.ts +0 -1
- package/internal/src/mutable/create-mutable-atom.ts +3 -4
- package/internal/src/mutable/tracker.ts +18 -13
- package/internal/src/selector/create-standalone-selector.ts +0 -2
- package/internal/src/selector/register-selector.ts +1 -1
- package/internal/src/set-state/index.ts +1 -0
- package/internal/src/set-state/set-atom.ts +15 -19
- package/internal/src/set-state/set-into-store.ts +24 -0
- package/internal/src/store/store.ts +14 -2
- package/internal/src/store/withdraw.ts +72 -2
- package/internal/src/subscribe/subscribe-to-root-atoms.ts +1 -1
- package/internal/src/subscribe/subscribe-to-timeline.ts +2 -2
- package/internal/src/subscribe/subscribe-to-transaction.ts +2 -2
- package/internal/src/timeline/create-timeline.ts +12 -1
- package/internal/src/transaction/act-upon-store.ts +19 -0
- package/internal/src/transaction/apply-transaction.ts +7 -1
- package/internal/src/transaction/assign-transaction-to-continuity.ts +18 -0
- package/internal/src/transaction/build-transaction.ts +11 -8
- package/internal/src/transaction/create-transaction.ts +1 -1
- package/internal/src/transaction/get-epoch-number.ts +40 -0
- package/internal/src/transaction/index.ts +10 -1
- package/internal/src/transaction/set-epoch-number.ts +31 -0
- package/introspection/dist/index.cjs.map +1 -1
- package/introspection/dist/index.d.ts +3 -3
- package/introspection/dist/index.js.map +1 -1
- package/introspection/src/attach-introspection-states.ts +6 -2
- package/introspection/src/attach-timeline-family.ts +5 -2
- package/introspection/src/attach-transaction-logs.ts +2 -2
- package/json/dist/index.d.ts +3 -1
- package/json/src/index.ts +6 -2
- package/package.json +24 -13
- package/react/dist/index.cjs +3 -3
- package/react/dist/index.cjs.map +1 -1
- package/react/dist/index.d.ts +1 -1
- package/react/dist/index.js +5 -5
- package/react/dist/index.js.map +1 -1
- package/react/src/use-i.ts +2 -3
- package/react/src/use-json.ts +1 -1
- package/react/src/use-o.ts +3 -4
- package/react-devtools/dist/index.cjs +131 -134
- package/react-devtools/dist/index.cjs.map +1 -1
- package/react-devtools/dist/index.css +2 -2
- package/react-devtools/dist/index.css.map +1 -1
- package/react-devtools/dist/index.d.ts +3 -3
- package/react-devtools/dist/index.js +103 -106
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/src/StateEditor.tsx +6 -6
- package/react-devtools/src/StateIndex.tsx +2 -5
- package/react-devtools/src/TimelineIndex.tsx +3 -3
- package/react-devtools/src/TransactionIndex.tsx +9 -8
- package/react-devtools/src/Updates.tsx +1 -1
- package/react-devtools/src/index.ts +4 -4
- package/realtime/dist/index.cjs +72 -0
- package/realtime/dist/index.cjs.map +1 -0
- package/realtime/dist/index.d.ts +39 -0
- package/realtime/dist/index.js +68 -0
- package/realtime/dist/index.js.map +1 -0
- package/realtime/package.json +16 -0
- package/realtime/src/index.ts +1 -0
- package/realtime/src/realtime-continuity.ts +152 -0
- package/realtime-client/dist/index.cjs +403 -59
- package/realtime-client/dist/index.cjs.map +1 -1
- package/realtime-client/dist/index.d.ts +16 -9
- package/realtime-client/dist/index.js +114 -48
- package/realtime-client/dist/index.js.map +1 -1
- package/realtime-client/src/index.ts +8 -5
- package/realtime-client/src/{pull-family-member.ts → pull-atom-family-member.ts} +5 -5
- package/realtime-client/src/{pull-state.ts → pull-atom.ts} +5 -5
- package/realtime-client/src/{pull-mutable-family-member.ts → pull-mutable-atom-family-member.ts} +5 -5
- package/realtime-client/src/{pull-mutable.ts → pull-mutable-atom.ts} +5 -5
- package/realtime-client/src/pull-selector-family-member.ts +42 -0
- package/realtime-client/src/pull-selector.ts +38 -0
- package/realtime-client/src/realtime-client-stores/client-main-store.ts +2 -2
- package/realtime-client/src/realtime-client-stores/client-sync-store.ts +7 -7
- package/realtime-client/src/sync-continuity.ts +321 -0
- package/realtime-client/src/sync-server-action.ts +22 -21
- package/realtime-client/src/sync-state.ts +3 -3
- package/realtime-react/dist/index.cjs +330 -15
- package/realtime-react/dist/index.cjs.map +1 -1
- package/realtime-react/dist/index.d.ts +26 -6
- package/realtime-react/dist/index.js +43 -12
- package/realtime-react/dist/index.js.map +1 -1
- package/realtime-react/src/index.ts +6 -3
- package/realtime-react/src/use-pull-atom-family-member.ts +21 -0
- package/realtime-react/src/{use-pull.ts → use-pull-atom.ts} +6 -5
- package/realtime-react/src/{use-pull-mutable.ts → use-pull-mutable-atom.ts} +4 -3
- package/realtime-react/src/use-pull-mutable-family-member.ts +9 -4
- package/realtime-react/src/use-pull-selector-family-member.ts +21 -0
- package/realtime-react/src/{use-pull-family-member.ts → use-pull-selector.ts} +7 -5
- package/realtime-react/src/use-push.ts +3 -2
- package/realtime-react/src/use-server-action.ts +3 -2
- package/realtime-react/src/use-sync-continuity.ts +12 -0
- package/realtime-react/src/use-sync-server-action.ts +3 -2
- package/realtime-server/dist/index.cjs +582 -256
- package/realtime-server/dist/index.cjs.map +1 -1
- package/realtime-server/dist/index.d.ts +124 -49
- package/realtime-server/dist/index.js +566 -249
- package/realtime-server/dist/index.js.map +1 -1
- package/realtime-server/src/index.ts +18 -2
- package/realtime-server/src/ipc-socket.ts +230 -0
- package/realtime-server/src/realtime-action-receiver.ts +8 -5
- package/realtime-server/src/realtime-action-synchronizer.ts +53 -35
- package/realtime-server/src/realtime-continuity-synchronizer.ts +247 -0
- package/realtime-server/src/realtime-family-provider.ts +37 -73
- package/realtime-server/src/realtime-mutable-family-provider.ts +26 -87
- package/realtime-server/src/realtime-mutable-provider.ts +3 -2
- package/realtime-server/src/realtime-server-stores/index.ts +3 -1
- package/realtime-server/src/realtime-server-stores/realtime-continuity-store.ts +90 -0
- package/realtime-server/src/realtime-server-stores/server-room-store.ts +97 -0
- package/realtime-server/src/realtime-server-stores/server-sync-store.ts +2 -72
- package/realtime-server/src/realtime-server-stores/server-user-store.ts +14 -29
- package/realtime-server/src/realtime-state-provider.ts +3 -3
- package/realtime-server/src/realtime-state-receiver.ts +2 -3
- package/realtime-server/src/realtime-state-synchronizer.ts +3 -3
- package/realtime-testing/dist/index.cjs +28 -28
- package/realtime-testing/dist/index.cjs.map +1 -1
- package/realtime-testing/dist/index.js +28 -27
- package/realtime-testing/dist/index.js.map +1 -1
- package/realtime-testing/src/setup-realtime-test.tsx +38 -28
- package/src/atom.ts +49 -31
- package/src/get-state.ts +2 -11
- package/src/logger.ts +10 -5
- package/src/selector.ts +44 -25
- package/src/set-state.ts +1 -13
- package/src/silo.ts +7 -3
- package/src/subscribe.ts +2 -1
- package/src/timeline.ts +4 -4
- package/src/transaction.ts +13 -17
- package/src/validators.ts +15 -9
- package/dist/chunk-H4Q5FTPZ.js +0 -11
- package/dist/chunk-H4Q5FTPZ.js.map +0 -1
- 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
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
178
|
-
const bKeysState = find(
|
|
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(
|
|
189
|
-
const bKeysState = find(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
312
|
+
let contentAtoms: RegularAtomFamily<Content, string>
|
|
284
313
|
if (defaultContent) {
|
|
285
|
-
|
|
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> = (
|
|
293
|
-
get
|
|
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
|
-
|
|
326
|
+
{ find, set },
|
|
296
327
|
key,
|
|
297
328
|
content,
|
|
298
|
-
) =>
|
|
299
|
-
const deleteContent: Write<(key: string) => void> = (
|
|
300
|
-
dispose(
|
|
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
|
|
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(
|
|
350
|
-
const
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
acc
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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"]}
|