atom.io 0.30.6 → 0.31.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.d.ts +20 -18
- package/data/dist/index.js +104 -172
- package/data/src/join.ts +138 -210
- package/dist/{chunk-LSCRHXLI.js → chunk-42UH5F5Q.js} +385 -773
- package/dist/chunk-ICGFFQ3H.js +272 -0
- package/dist/index.d.ts +52 -103
- package/dist/index.js +3 -11
- package/eslint-plugin/dist/index.d.ts +22 -3
- package/eslint-plugin/dist/index.js +7 -7
- package/immortal/dist/index.d.ts +1 -2
- package/immortal/dist/index.js +0 -3
- package/immortal/src/seek-state.ts +2 -14
- package/internal/dist/index.d.ts +59 -95
- package/internal/dist/index.js +2 -2
- package/internal/src/atom/dispose-atom.ts +31 -15
- package/internal/src/families/dispose-from-store.ts +15 -44
- package/internal/src/families/find-in-store.ts +15 -8
- package/internal/src/families/init-family-member.ts +1 -1
- package/internal/src/families/seek-in-store.ts +2 -14
- package/internal/src/get-state/get-from-store.ts +13 -79
- package/internal/src/get-trace.ts +7 -0
- package/internal/src/index.ts +2 -1
- package/internal/src/ingest-updates/ingest-creation-disposal.ts +63 -70
- package/internal/src/ingest-updates/ingest-transaction-update.ts +4 -0
- package/internal/src/junction.ts +52 -12
- package/internal/src/lineage.ts +0 -7
- package/internal/src/molecule.ts +7 -0
- package/internal/src/mutable/transceiver.ts +5 -5
- package/internal/src/pretty-print.ts +0 -4
- package/internal/src/selector/dispose-selector.ts +3 -5
- package/internal/src/selector/register-selector.ts +2 -26
- package/internal/src/set-state/set-into-store.ts +3 -2
- package/internal/src/store/counterfeit.ts +11 -25
- package/internal/src/store/deposit.ts +5 -39
- package/internal/src/store/index.ts +1 -0
- package/internal/src/store/store.ts +51 -12
- package/internal/src/store/withdraw.ts +3 -26
- package/internal/src/timeline/create-timeline.ts +133 -237
- package/internal/src/timeline/time-travel.ts +1 -8
- package/internal/src/transaction/build-transaction.ts +10 -5
- package/internal/src/transaction/index.ts +1 -1
- package/internal/src/utility-types.ts +2 -0
- package/introspection/dist/index.d.ts +2 -3
- package/introspection/dist/index.js +9 -9
- package/introspection/src/refinery.ts +1 -3
- package/json/dist/index.js +9 -40
- package/json/src/index.ts +2 -0
- package/json/src/select-json-family.ts +7 -44
- package/package.json +34 -29
- package/react/dist/index.js +2 -10
- package/react/src/parse-state-overloads.ts +3 -11
- package/react-devtools/dist/index.js +13 -13
- package/react-devtools/src/Updates.tsx +2 -0
- package/realtime-client/dist/index.d.ts +20 -12
- package/realtime-client/dist/index.js +241 -244
- package/realtime-client/src/continuity/index.ts +3 -0
- package/realtime-client/src/continuity/register-and-attempt-confirmed-update.ts +231 -0
- package/realtime-client/src/continuity/use-conceal-state.ts +11 -0
- package/realtime-client/src/continuity/use-reveal-state.ts +19 -0
- package/realtime-client/src/index.ts +1 -0
- package/realtime-client/src/sync-continuity.ts +18 -262
- package/realtime-react/dist/index.js +2 -2
- package/realtime-server/dist/index.d.ts +1 -1
- package/realtime-server/dist/index.js +2 -2
- package/realtime-server/src/index.ts +1 -1
- package/realtime-testing/dist/index.js +2 -3
- package/realtime-testing/src/setup-realtime-test.tsx +1 -2
- package/src/allocate.ts +311 -145
- package/src/dispose-state.ts +5 -21
- package/src/get-state.ts +3 -21
- package/src/molecule.ts +11 -133
- package/src/silo.ts +1 -12
- package/src/timeline.ts +2 -3
- package/src/transaction.ts +25 -38
- package/dist/chunk-ADMEAXYU.js +0 -167
- package/internal/src/molecule/create-molecule-family.ts +0 -30
- package/internal/src/molecule/dispose-molecule.ts +0 -79
- package/internal/src/molecule/grow-molecule-in-store.ts +0 -95
- package/internal/src/molecule/index.ts +0 -5
- package/internal/src/molecule/make-molecule-in-store.ts +0 -191
- package/internal/src/molecule/molecule-internal.ts +0 -52
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import type * as AtomIO from "atom.io"
|
|
2
|
+
import type { Func, Store } from "atom.io/internal"
|
|
3
|
+
import {
|
|
4
|
+
actUponStore,
|
|
5
|
+
getEpochNumberOfContinuity,
|
|
6
|
+
ingestTransactionUpdate,
|
|
7
|
+
isRootStore,
|
|
8
|
+
setEpochNumberOfContinuity,
|
|
9
|
+
setIntoStore,
|
|
10
|
+
} from "atom.io/internal"
|
|
11
|
+
import {
|
|
12
|
+
confirmedUpdateQueue,
|
|
13
|
+
optimisticUpdateQueue,
|
|
14
|
+
} from "atom.io/realtime-client"
|
|
15
|
+
import type { Socket } from "atom.io/realtime-server"
|
|
16
|
+
|
|
17
|
+
export const useRegisterAndAttemptConfirmedUpdate =
|
|
18
|
+
(
|
|
19
|
+
store: Store,
|
|
20
|
+
continuityKey: string,
|
|
21
|
+
socket: Socket,
|
|
22
|
+
optimisticUpdates: AtomIO.TransactionUpdate<any>[],
|
|
23
|
+
confirmedUpdates: AtomIO.TransactionUpdate<any>[],
|
|
24
|
+
) =>
|
|
25
|
+
(confirmed: AtomIO.TransactionUpdate<Func>): void => {
|
|
26
|
+
function reconcileEpoch(
|
|
27
|
+
optimisticUpdate: AtomIO.TransactionUpdate<any>,
|
|
28
|
+
confirmedUpdate: AtomIO.TransactionUpdate<any>,
|
|
29
|
+
): void {
|
|
30
|
+
store.logger.info(
|
|
31
|
+
`🧑⚖️`,
|
|
32
|
+
`continuity`,
|
|
33
|
+
continuityKey,
|
|
34
|
+
`reconciling updates`,
|
|
35
|
+
)
|
|
36
|
+
setIntoStore(store, optimisticUpdateQueue, (queue) => {
|
|
37
|
+
queue.shift()
|
|
38
|
+
return queue
|
|
39
|
+
})
|
|
40
|
+
if (optimisticUpdate.id === confirmedUpdate.id) {
|
|
41
|
+
const clientResult = JSON.stringify(optimisticUpdate.updates)
|
|
42
|
+
const serverResult = JSON.stringify(confirmedUpdate.updates)
|
|
43
|
+
if (clientResult === serverResult) {
|
|
44
|
+
store.logger.info(
|
|
45
|
+
`✅`,
|
|
46
|
+
`continuity`,
|
|
47
|
+
continuityKey,
|
|
48
|
+
`results for ${optimisticUpdate.id} match between client and server`,
|
|
49
|
+
)
|
|
50
|
+
socket.emit(`ack:${continuityKey}`, confirmedUpdate.epoch)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
// id mismatch
|
|
55
|
+
store.logger.info(
|
|
56
|
+
`❌`,
|
|
57
|
+
`continuity`,
|
|
58
|
+
continuityKey,
|
|
59
|
+
`thought update #${confirmedUpdate.epoch} was ${optimisticUpdate.key}:${optimisticUpdate.id}, but it was actually ${confirmedUpdate.key}:${confirmedUpdate.id}`,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
store.logger.info(
|
|
63
|
+
`🧑⚖️`,
|
|
64
|
+
`continuity`,
|
|
65
|
+
continuityKey,
|
|
66
|
+
`updates do not match`,
|
|
67
|
+
optimisticUpdate,
|
|
68
|
+
confirmedUpdate,
|
|
69
|
+
)
|
|
70
|
+
const reversedOptimisticUpdates = optimisticUpdates.toReversed()
|
|
71
|
+
for (const subsequentOptimistic of reversedOptimisticUpdates) {
|
|
72
|
+
ingestTransactionUpdate(`oldValue`, subsequentOptimistic, store)
|
|
73
|
+
}
|
|
74
|
+
store.logger.info(
|
|
75
|
+
`⏪`,
|
|
76
|
+
`continuity`,
|
|
77
|
+
continuityKey,
|
|
78
|
+
`undid optimistic updates:`,
|
|
79
|
+
reversedOptimisticUpdates,
|
|
80
|
+
)
|
|
81
|
+
ingestTransactionUpdate(`oldValue`, optimisticUpdate, store)
|
|
82
|
+
store.logger.info(
|
|
83
|
+
`⏪`,
|
|
84
|
+
`continuity`,
|
|
85
|
+
continuityKey,
|
|
86
|
+
`undid zeroth optimistic update`,
|
|
87
|
+
optimisticUpdate,
|
|
88
|
+
)
|
|
89
|
+
ingestTransactionUpdate(`newValue`, confirmedUpdate, store)
|
|
90
|
+
store.logger.info(
|
|
91
|
+
`⏩`,
|
|
92
|
+
`continuity`,
|
|
93
|
+
continuityKey,
|
|
94
|
+
`applied confirmed update`,
|
|
95
|
+
confirmedUpdate,
|
|
96
|
+
)
|
|
97
|
+
socket.emit(`ack:${continuityKey}`, confirmedUpdate.epoch)
|
|
98
|
+
|
|
99
|
+
for (const subsequentOptimistic of optimisticUpdates) {
|
|
100
|
+
const token = {
|
|
101
|
+
type: `transaction`,
|
|
102
|
+
key: subsequentOptimistic.key,
|
|
103
|
+
} as const
|
|
104
|
+
const { id, params } = subsequentOptimistic
|
|
105
|
+
actUponStore(token, id, store)(...params)
|
|
106
|
+
}
|
|
107
|
+
store.logger.info(
|
|
108
|
+
`⏩`,
|
|
109
|
+
`continuity`,
|
|
110
|
+
continuityKey,
|
|
111
|
+
`reapplied subsequent optimistic updates:`,
|
|
112
|
+
optimisticUpdates,
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
store.logger.info(
|
|
117
|
+
`🧑⚖️`,
|
|
118
|
+
`continuity`,
|
|
119
|
+
continuityKey,
|
|
120
|
+
`integrating confirmed update`,
|
|
121
|
+
{ confirmedUpdate: confirmed, confirmedUpdates, optimisticUpdates },
|
|
122
|
+
)
|
|
123
|
+
const zerothOptimisticUpdate = optimisticUpdates[0]
|
|
124
|
+
if (zerothOptimisticUpdate) {
|
|
125
|
+
store.logger.info(
|
|
126
|
+
`🧑⚖️`,
|
|
127
|
+
`continuity`,
|
|
128
|
+
continuityKey,
|
|
129
|
+
`has optimistic updates to reconcile`,
|
|
130
|
+
)
|
|
131
|
+
if (confirmed.epoch === zerothOptimisticUpdate.epoch) {
|
|
132
|
+
store.logger.info(
|
|
133
|
+
`🧑⚖️`,
|
|
134
|
+
`continuity`,
|
|
135
|
+
continuityKey,
|
|
136
|
+
`epoch of confirmed update #${confirmed.epoch} matches zeroth optimistic update`,
|
|
137
|
+
)
|
|
138
|
+
reconcileEpoch(zerothOptimisticUpdate, confirmed)
|
|
139
|
+
for (const nextConfirmed of confirmedUpdates) {
|
|
140
|
+
const nextOptimistic = optimisticUpdates[0]
|
|
141
|
+
if (nextConfirmed.epoch === nextOptimistic?.epoch) {
|
|
142
|
+
reconcileEpoch(nextOptimistic, nextConfirmed)
|
|
143
|
+
} else {
|
|
144
|
+
break
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
// epoch mismatch
|
|
149
|
+
store.logger.info(
|
|
150
|
+
`🧑⚖️`,
|
|
151
|
+
`continuity`,
|
|
152
|
+
continuityKey,
|
|
153
|
+
`epoch of confirmed update #${confirmed.epoch} does not match zeroth optimistic update #${zerothOptimisticUpdate.epoch}`,
|
|
154
|
+
)
|
|
155
|
+
const confirmedUpdateIsAlreadyEnqueued = confirmedUpdates.some(
|
|
156
|
+
(update) => update.epoch === confirmed.epoch,
|
|
157
|
+
)
|
|
158
|
+
if (!confirmedUpdateIsAlreadyEnqueued) {
|
|
159
|
+
store.logger.info(
|
|
160
|
+
`👈`,
|
|
161
|
+
`continuity`,
|
|
162
|
+
continuityKey,
|
|
163
|
+
`pushing confirmed update to queue`,
|
|
164
|
+
confirmed,
|
|
165
|
+
)
|
|
166
|
+
setIntoStore(store, confirmedUpdateQueue, (queue) => {
|
|
167
|
+
queue.push(confirmed)
|
|
168
|
+
queue.sort((a, b) => a.epoch - b.epoch)
|
|
169
|
+
return queue
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
store.logger.info(
|
|
175
|
+
`🧑⚖️`,
|
|
176
|
+
`continuity`,
|
|
177
|
+
continuityKey,
|
|
178
|
+
`has no optimistic updates to deal with`,
|
|
179
|
+
)
|
|
180
|
+
const continuityEpoch = getEpochNumberOfContinuity(continuityKey, store)
|
|
181
|
+
const isRoot = isRootStore(store)
|
|
182
|
+
|
|
183
|
+
if (isRoot && continuityEpoch === confirmed.epoch - 1) {
|
|
184
|
+
store.logger.info(
|
|
185
|
+
`✅`,
|
|
186
|
+
`continuity`,
|
|
187
|
+
continuityKey,
|
|
188
|
+
`integrating update #${confirmed.epoch} (${confirmed.key} ${confirmed.id})`,
|
|
189
|
+
)
|
|
190
|
+
ingestTransactionUpdate(`newValue`, confirmed, store)
|
|
191
|
+
socket.emit(`ack:${continuityKey}`, confirmed.epoch)
|
|
192
|
+
setEpochNumberOfContinuity(continuityKey, confirmed.epoch, store)
|
|
193
|
+
} else if (isRoot && continuityEpoch !== undefined) {
|
|
194
|
+
store.logger.info(
|
|
195
|
+
`🧑⚖️`,
|
|
196
|
+
`continuity`,
|
|
197
|
+
continuityKey,
|
|
198
|
+
`received update #${confirmed.epoch} but still waiting for update #${
|
|
199
|
+
continuityEpoch + 1
|
|
200
|
+
}`,
|
|
201
|
+
{
|
|
202
|
+
clientEpoch: continuityEpoch,
|
|
203
|
+
serverEpoch: confirmed.epoch,
|
|
204
|
+
},
|
|
205
|
+
)
|
|
206
|
+
const confirmedUpdateIsAlreadyEnqueued = confirmedUpdates.some(
|
|
207
|
+
(update) => update.epoch === confirmed.epoch,
|
|
208
|
+
)
|
|
209
|
+
if (confirmedUpdateIsAlreadyEnqueued) {
|
|
210
|
+
store.logger.info(
|
|
211
|
+
`👍`,
|
|
212
|
+
`continuity`,
|
|
213
|
+
continuityKey,
|
|
214
|
+
`confirmed update #${confirmed.epoch} is already enqueued`,
|
|
215
|
+
)
|
|
216
|
+
} else {
|
|
217
|
+
store.logger.info(
|
|
218
|
+
`👈`,
|
|
219
|
+
`continuity`,
|
|
220
|
+
continuityKey,
|
|
221
|
+
`pushing confirmed update #${confirmed.epoch} to queue`,
|
|
222
|
+
)
|
|
223
|
+
setIntoStore(store, confirmedUpdateQueue, (queue) => {
|
|
224
|
+
queue.push(confirmed)
|
|
225
|
+
queue.sort((a, b) => a.epoch - b.epoch)
|
|
226
|
+
return queue
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AtomToken } from "atom.io"
|
|
2
|
+
import type { Store } from "atom.io/internal"
|
|
3
|
+
import { disposeAtom } from "atom.io/internal"
|
|
4
|
+
|
|
5
|
+
export function useConcealState(store: Store) {
|
|
6
|
+
return (concealed: AtomToken<unknown>[]): void => {
|
|
7
|
+
for (const token of concealed) {
|
|
8
|
+
disposeAtom(token, store)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { setIntoStore, type Store } from "atom.io/internal"
|
|
2
|
+
import type { Json } from "atom.io/json"
|
|
3
|
+
|
|
4
|
+
export function useRevealState(store: Store) {
|
|
5
|
+
return (revealed: Json.Array): void => {
|
|
6
|
+
let i = 0
|
|
7
|
+
let k: any
|
|
8
|
+
let v: any
|
|
9
|
+
for (const x of revealed) {
|
|
10
|
+
if (i % 2 === 0) {
|
|
11
|
+
k = x
|
|
12
|
+
} else {
|
|
13
|
+
v = x
|
|
14
|
+
setIntoStore(store, k, v)
|
|
15
|
+
}
|
|
16
|
+
i++
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { Func, Store } from "atom.io/internal"
|
|
1
|
+
import type { Store } from "atom.io/internal"
|
|
3
2
|
import {
|
|
4
|
-
actUponStore,
|
|
5
3
|
assignTransactionToContinuity,
|
|
6
|
-
disposeAtom,
|
|
7
|
-
getEpochNumberOfContinuity,
|
|
8
4
|
getFromStore,
|
|
9
5
|
getJsonToken,
|
|
10
|
-
growMoleculeInStore,
|
|
11
|
-
ingestTransactionUpdate,
|
|
12
|
-
initFamilyMemberInStore,
|
|
13
|
-
isRootStore,
|
|
14
6
|
setEpochNumberOfContinuity,
|
|
15
7
|
setIntoStore,
|
|
16
8
|
subscribeToTransaction,
|
|
17
9
|
} from "atom.io/internal"
|
|
18
|
-
import {
|
|
10
|
+
import type { Json } from "atom.io/json"
|
|
19
11
|
import type { ContinuityToken } from "atom.io/realtime"
|
|
20
12
|
import {
|
|
21
13
|
confirmedUpdateQueue,
|
|
@@ -23,7 +15,11 @@ import {
|
|
|
23
15
|
} from "atom.io/realtime-client"
|
|
24
16
|
import type { Socket } from "socket.io-client"
|
|
25
17
|
|
|
26
|
-
|
|
18
|
+
import { useRegisterAndAttemptConfirmedUpdate } from "./continuity/register-and-attempt-confirmed-update"
|
|
19
|
+
import { useConcealState } from "./continuity/use-conceal-state"
|
|
20
|
+
import { useRevealState } from "./continuity/use-reveal-state"
|
|
21
|
+
|
|
22
|
+
export function syncContinuity(
|
|
27
23
|
continuity: ContinuityToken,
|
|
28
24
|
socket: Socket,
|
|
29
25
|
store: Store,
|
|
@@ -54,215 +50,13 @@ export function syncContinuity<F extends Func>(
|
|
|
54
50
|
socket.off(`continuity-init:${continuityKey}`)
|
|
55
51
|
socket.on(`continuity-init:${continuityKey}`, initializeContinuity)
|
|
56
52
|
|
|
57
|
-
const registerAndAttemptConfirmedUpdate = (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
store.logger.info(
|
|
65
|
-
`🧑⚖️`,
|
|
66
|
-
`continuity`,
|
|
67
|
-
continuityKey,
|
|
68
|
-
`reconciling updates`,
|
|
69
|
-
)
|
|
70
|
-
setIntoStore(store, optimisticUpdateQueue, (queue) => {
|
|
71
|
-
queue.shift()
|
|
72
|
-
return queue
|
|
73
|
-
})
|
|
74
|
-
if (optimisticUpdate.id === confirmedUpdate.id) {
|
|
75
|
-
const clientResult = JSON.stringify(optimisticUpdate.updates)
|
|
76
|
-
const serverResult = JSON.stringify(confirmedUpdate.updates)
|
|
77
|
-
if (clientResult === serverResult) {
|
|
78
|
-
store.logger.info(
|
|
79
|
-
`✅`,
|
|
80
|
-
`continuity`,
|
|
81
|
-
continuityKey,
|
|
82
|
-
`results for ${optimisticUpdate.id} match between client and server`,
|
|
83
|
-
)
|
|
84
|
-
socket.emit(`ack:${continuityKey}`, confirmedUpdate.epoch)
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
} else {
|
|
88
|
-
// id mismatch
|
|
89
|
-
store.logger.info(
|
|
90
|
-
`❌`,
|
|
91
|
-
`continuity`,
|
|
92
|
-
continuityKey,
|
|
93
|
-
`thought update #${confirmedUpdate.epoch} was ${optimisticUpdate.key}:${optimisticUpdate.id}, but it was actually ${confirmedUpdate.key}:${confirmedUpdate.id}`,
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
store.logger.info(
|
|
97
|
-
`🧑⚖️`,
|
|
98
|
-
`continuity`,
|
|
99
|
-
continuityKey,
|
|
100
|
-
`updates do not match`,
|
|
101
|
-
optimisticUpdate,
|
|
102
|
-
confirmedUpdate,
|
|
103
|
-
)
|
|
104
|
-
const reversedOptimisticUpdates = optimisticUpdates.toReversed()
|
|
105
|
-
for (const subsequentOptimistic of reversedOptimisticUpdates) {
|
|
106
|
-
ingestTransactionUpdate(`oldValue`, subsequentOptimistic, store)
|
|
107
|
-
}
|
|
108
|
-
store.logger.info(
|
|
109
|
-
`⏪`,
|
|
110
|
-
`continuity`,
|
|
111
|
-
continuityKey,
|
|
112
|
-
`undid optimistic updates:`,
|
|
113
|
-
reversedOptimisticUpdates,
|
|
114
|
-
)
|
|
115
|
-
ingestTransactionUpdate(`oldValue`, optimisticUpdate, store)
|
|
116
|
-
store.logger.info(
|
|
117
|
-
`⏪`,
|
|
118
|
-
`continuity`,
|
|
119
|
-
continuityKey,
|
|
120
|
-
`undid zeroth optimistic update`,
|
|
121
|
-
optimisticUpdate,
|
|
122
|
-
)
|
|
123
|
-
ingestTransactionUpdate(`newValue`, confirmedUpdate, store)
|
|
124
|
-
store.logger.info(
|
|
125
|
-
`⏩`,
|
|
126
|
-
`continuity`,
|
|
127
|
-
continuityKey,
|
|
128
|
-
`applied confirmed update`,
|
|
129
|
-
confirmedUpdate,
|
|
130
|
-
)
|
|
131
|
-
socket.emit(`ack:${continuityKey}`, confirmedUpdate.epoch)
|
|
132
|
-
|
|
133
|
-
for (const subsequentOptimistic of optimisticUpdates) {
|
|
134
|
-
const token = {
|
|
135
|
-
type: `transaction`,
|
|
136
|
-
key: subsequentOptimistic.key,
|
|
137
|
-
} as const
|
|
138
|
-
const { id, params } = subsequentOptimistic
|
|
139
|
-
actUponStore(token, id, store)(...params)
|
|
140
|
-
}
|
|
141
|
-
store.logger.info(
|
|
142
|
-
`⏩`,
|
|
143
|
-
`continuity`,
|
|
144
|
-
continuityKey,
|
|
145
|
-
`reapplied subsequent optimistic updates:`,
|
|
146
|
-
optimisticUpdates,
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
store.logger.info(
|
|
151
|
-
`🧑⚖️`,
|
|
152
|
-
`continuity`,
|
|
153
|
-
continuityKey,
|
|
154
|
-
`integrating confirmed update`,
|
|
155
|
-
{ confirmedUpdate: confirmed, confirmedUpdates, optimisticUpdates },
|
|
156
|
-
)
|
|
157
|
-
const zerothOptimisticUpdate = optimisticUpdates[0]
|
|
158
|
-
if (zerothOptimisticUpdate) {
|
|
159
|
-
store.logger.info(
|
|
160
|
-
`🧑⚖️`,
|
|
161
|
-
`continuity`,
|
|
162
|
-
continuityKey,
|
|
163
|
-
`has optimistic updates to reconcile`,
|
|
164
|
-
)
|
|
165
|
-
if (confirmed.epoch === zerothOptimisticUpdate.epoch) {
|
|
166
|
-
store.logger.info(
|
|
167
|
-
`🧑⚖️`,
|
|
168
|
-
`continuity`,
|
|
169
|
-
continuityKey,
|
|
170
|
-
`epoch of confirmed update #${confirmed.epoch} matches zeroth optimistic update`,
|
|
171
|
-
)
|
|
172
|
-
reconcileEpoch(zerothOptimisticUpdate, confirmed)
|
|
173
|
-
for (const nextConfirmed of confirmedUpdates) {
|
|
174
|
-
const nextOptimistic = optimisticUpdates[0]
|
|
175
|
-
if (nextConfirmed.epoch === nextOptimistic?.epoch) {
|
|
176
|
-
reconcileEpoch(nextOptimistic, nextConfirmed)
|
|
177
|
-
} else {
|
|
178
|
-
break
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
} else {
|
|
182
|
-
// epoch mismatch
|
|
183
|
-
store.logger.info(
|
|
184
|
-
`🧑⚖️`,
|
|
185
|
-
`continuity`,
|
|
186
|
-
continuityKey,
|
|
187
|
-
`epoch of confirmed update #${confirmed.epoch} does not match zeroth optimistic update #${zerothOptimisticUpdate.epoch}`,
|
|
188
|
-
)
|
|
189
|
-
const confirmedUpdateIsAlreadyEnqueued = confirmedUpdates.some(
|
|
190
|
-
(update) => update.epoch === confirmed.epoch,
|
|
191
|
-
)
|
|
192
|
-
if (!confirmedUpdateIsAlreadyEnqueued) {
|
|
193
|
-
store.logger.info(
|
|
194
|
-
`👈`,
|
|
195
|
-
`continuity`,
|
|
196
|
-
continuityKey,
|
|
197
|
-
`pushing confirmed update to queue`,
|
|
198
|
-
confirmed,
|
|
199
|
-
)
|
|
200
|
-
setIntoStore(store, confirmedUpdateQueue, (queue) => {
|
|
201
|
-
queue.push(confirmed)
|
|
202
|
-
queue.sort((a, b) => a.epoch - b.epoch)
|
|
203
|
-
return queue
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
} else {
|
|
208
|
-
store.logger.info(
|
|
209
|
-
`🧑⚖️`,
|
|
210
|
-
`continuity`,
|
|
211
|
-
continuityKey,
|
|
212
|
-
`has no optimistic updates to deal with`,
|
|
213
|
-
)
|
|
214
|
-
const continuityEpoch = getEpochNumberOfContinuity(continuityKey, store)
|
|
215
|
-
const isRoot = isRootStore(store)
|
|
216
|
-
|
|
217
|
-
if (isRoot && continuityEpoch === confirmed.epoch - 1) {
|
|
218
|
-
store.logger.info(
|
|
219
|
-
`✅`,
|
|
220
|
-
`continuity`,
|
|
221
|
-
continuityKey,
|
|
222
|
-
`integrating update #${confirmed.epoch} (${confirmed.key} ${confirmed.id})`,
|
|
223
|
-
)
|
|
224
|
-
ingestTransactionUpdate(`newValue`, confirmed, store)
|
|
225
|
-
socket.emit(`ack:${continuityKey}`, confirmed.epoch)
|
|
226
|
-
setEpochNumberOfContinuity(continuityKey, confirmed.epoch, store)
|
|
227
|
-
} else if (isRoot && continuityEpoch !== undefined) {
|
|
228
|
-
store.logger.info(
|
|
229
|
-
`🧑⚖️`,
|
|
230
|
-
`continuity`,
|
|
231
|
-
continuityKey,
|
|
232
|
-
`received update #${confirmed.epoch} but still waiting for update #${
|
|
233
|
-
continuityEpoch + 1
|
|
234
|
-
}`,
|
|
235
|
-
{
|
|
236
|
-
clientEpoch: continuityEpoch,
|
|
237
|
-
serverEpoch: confirmed.epoch,
|
|
238
|
-
},
|
|
239
|
-
)
|
|
240
|
-
const confirmedUpdateIsAlreadyEnqueued = confirmedUpdates.some(
|
|
241
|
-
(update) => update.epoch === confirmed.epoch,
|
|
242
|
-
)
|
|
243
|
-
if (confirmedUpdateIsAlreadyEnqueued) {
|
|
244
|
-
store.logger.info(
|
|
245
|
-
`👍`,
|
|
246
|
-
`continuity`,
|
|
247
|
-
continuityKey,
|
|
248
|
-
`confirmed update #${confirmed.epoch} is already enqueued`,
|
|
249
|
-
)
|
|
250
|
-
} else {
|
|
251
|
-
store.logger.info(
|
|
252
|
-
`👈`,
|
|
253
|
-
`continuity`,
|
|
254
|
-
continuityKey,
|
|
255
|
-
`pushing confirmed update #${confirmed.epoch} to queue`,
|
|
256
|
-
)
|
|
257
|
-
setIntoStore(store, confirmedUpdateQueue, (queue) => {
|
|
258
|
-
queue.push(confirmed)
|
|
259
|
-
queue.sort((a, b) => a.epoch - b.epoch)
|
|
260
|
-
return queue
|
|
261
|
-
})
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
53
|
+
const registerAndAttemptConfirmedUpdate = useRegisterAndAttemptConfirmedUpdate(
|
|
54
|
+
store,
|
|
55
|
+
continuityKey,
|
|
56
|
+
socket,
|
|
57
|
+
optimisticUpdates,
|
|
58
|
+
confirmedUpdates,
|
|
59
|
+
)
|
|
266
60
|
socket.off(`tx-new:${continuityKey}`)
|
|
267
61
|
socket.on(`tx-new:${continuityKey}`, registerAndAttemptConfirmedUpdate)
|
|
268
62
|
|
|
@@ -316,28 +110,10 @@ export function syncContinuity<F extends Func>(
|
|
|
316
110
|
return unsubscribeFromTransactionUpdates
|
|
317
111
|
})
|
|
318
112
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
for (const x of revealed) {
|
|
324
|
-
if (i % 2 === 0) {
|
|
325
|
-
k = x
|
|
326
|
-
} else {
|
|
327
|
-
v = x
|
|
328
|
-
upsertState(k, v, store)
|
|
329
|
-
}
|
|
330
|
-
i++
|
|
331
|
-
}
|
|
332
|
-
})
|
|
333
|
-
socket.on(
|
|
334
|
-
`conceal:${continuityKey}`,
|
|
335
|
-
(concealed: AtomIO.AtomToken<unknown>[]) => {
|
|
336
|
-
for (const token of concealed) {
|
|
337
|
-
disposeAtom(token, store)
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
|
-
)
|
|
113
|
+
const revealState = useRevealState(store)
|
|
114
|
+
const concealState = useConcealState(store)
|
|
115
|
+
socket.on(`reveal:${continuityKey}`, revealState)
|
|
116
|
+
socket.on(`conceal:${continuityKey}`, concealState)
|
|
341
117
|
|
|
342
118
|
socket.emit(`get:${continuityKey}`)
|
|
343
119
|
return () => {
|
|
@@ -347,23 +123,3 @@ export function syncContinuity<F extends Func>(
|
|
|
347
123
|
// socket.emit(`unsub:${continuityKey}`)
|
|
348
124
|
}
|
|
349
125
|
}
|
|
350
|
-
|
|
351
|
-
function upsertState<T>(
|
|
352
|
-
store: Store,
|
|
353
|
-
token: AtomIO.WritableToken<T>,
|
|
354
|
-
value: T,
|
|
355
|
-
): void {
|
|
356
|
-
if (token.family) {
|
|
357
|
-
const family = store.families.get(token.family.key)
|
|
358
|
-
if (family) {
|
|
359
|
-
const molecule = store.molecules.get(token.family.subKey)
|
|
360
|
-
if (molecule) {
|
|
361
|
-
growMoleculeInStore(molecule, family, store)
|
|
362
|
-
} else if (store.config.lifespan === `immortal`) {
|
|
363
|
-
throw new Error(`No molecule found for key "${token.family.subKey}"`)
|
|
364
|
-
}
|
|
365
|
-
initFamilyMemberInStore(store, family, parseJson(token.family.subKey))
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
setIntoStore(store, token, value)
|
|
369
|
-
}
|
|
@@ -19,7 +19,7 @@ var RealtimeProvider = ({ children, socket }) => {
|
|
|
19
19
|
setMyId(socket.id);
|
|
20
20
|
});
|
|
21
21
|
socket?.on(`disconnect`, () => {
|
|
22
|
-
setMyId(
|
|
22
|
+
setMyId(undefined);
|
|
23
23
|
});
|
|
24
24
|
}, [socket, setMyId]);
|
|
25
25
|
return /* @__PURE__ */ jsx(RealtimeContext.Provider, { value: { socket, services }, children });
|
|
@@ -61,7 +61,7 @@ function useRealtimeService(key, create) {
|
|
|
61
61
|
if (service) {
|
|
62
62
|
service[0]++;
|
|
63
63
|
} else {
|
|
64
|
-
const dispose = socket ? create(socket) :
|
|
64
|
+
const dispose = socket ? create(socket) : undefined;
|
|
65
65
|
service = [1, dispose];
|
|
66
66
|
services?.set(key, service);
|
|
67
67
|
}
|
|
@@ -126,7 +126,7 @@ type StateReceiver = ReturnType<typeof realtimeStateReceiver>;
|
|
|
126
126
|
declare function realtimeStateReceiver({ socket, store, }: ServerConfig): <J extends Json.Serializable>(token: WritableToken<J>) => () => void;
|
|
127
127
|
|
|
128
128
|
type Socket = {
|
|
129
|
-
id: string;
|
|
129
|
+
id: string | undefined;
|
|
130
130
|
on: (event: string, listener: (...args: Json.Serializable[]) => void) => void;
|
|
131
131
|
onAny: (listener: (event: string, ...args: Json.Serializable[]) => void) => void;
|
|
132
132
|
off: (event: string, listener: (...args: Json.Serializable[]) => void) => void;
|
|
@@ -960,7 +960,7 @@ function realtimeStateProvider({
|
|
|
960
960
|
socket.off(`unsub:${token.key}`, fillUnsubRequest);
|
|
961
961
|
if (unsubscribeFromStateUpdates) {
|
|
962
962
|
unsubscribeFromStateUpdates();
|
|
963
|
-
unsubscribeFromStateUpdates =
|
|
963
|
+
unsubscribeFromStateUpdates = undefined;
|
|
964
964
|
}
|
|
965
965
|
};
|
|
966
966
|
socket.on(`unsub:${token.key}`, fillUnsubRequest);
|
|
@@ -970,7 +970,7 @@ function realtimeStateProvider({
|
|
|
970
970
|
socket.off(`sub:${token.key}`, fillSubRequest);
|
|
971
971
|
if (unsubscribeFromStateUpdates) {
|
|
972
972
|
unsubscribeFromStateUpdates();
|
|
973
|
-
unsubscribeFromStateUpdates =
|
|
973
|
+
unsubscribeFromStateUpdates = undefined;
|
|
974
974
|
}
|
|
975
975
|
};
|
|
976
976
|
};
|
|
@@ -12,7 +12,7 @@ export * from "./realtime-state-provider"
|
|
|
12
12
|
export * from "./realtime-state-receiver"
|
|
13
13
|
|
|
14
14
|
export type Socket = {
|
|
15
|
-
id: string
|
|
15
|
+
id: string | undefined
|
|
16
16
|
on: (event: string, listener: (...args: Json.Serializable[]) => void) => void
|
|
17
17
|
onAny: (
|
|
18
18
|
listener: (event: string, ...args: Json.Serializable[]) => void,
|
|
@@ -2,7 +2,6 @@ import '../../dist/chunk-XWL6SNVU.js';
|
|
|
2
2
|
import * as http from 'node:http';
|
|
3
3
|
import { render, prettyDOM } from '@testing-library/react';
|
|
4
4
|
import * as AtomIO from 'atom.io';
|
|
5
|
-
import { realm } from 'atom.io';
|
|
6
5
|
import { editRelationsInStore, findRelationsInStore } from 'atom.io/data';
|
|
7
6
|
import { IMPLICIT, findInStore, setIntoStore, getFromStore, clearStore } from 'atom.io/internal';
|
|
8
7
|
import { toEntries } from 'atom.io/json';
|
|
@@ -18,7 +17,7 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
18
17
|
|
|
19
18
|
var testNumber = 0;
|
|
20
19
|
function prefixLogger(store, prefix) {
|
|
21
|
-
store.loggers[0] = new AtomIO.AtomIOLogger(`info`,
|
|
20
|
+
store.loggers[0] = new AtomIO.AtomIOLogger(`info`, undefined, {
|
|
22
21
|
info: (...args) => {
|
|
23
22
|
console.info(prefix, ...args);
|
|
24
23
|
},
|
|
@@ -39,7 +38,7 @@ var setupRealtimeTestServer = (options) => {
|
|
|
39
38
|
},
|
|
40
39
|
IMPLICIT.STORE
|
|
41
40
|
);
|
|
42
|
-
const socketRealm =
|
|
41
|
+
const socketRealm = new AtomIO.Realm(silo.store);
|
|
43
42
|
const httpServer = http.createServer((_, res) => res.end(`Hello World!`));
|
|
44
43
|
const address = httpServer.listen(options.port).address();
|
|
45
44
|
const port = typeof address === `string` ? null : address === null ? null : address.port;
|
|
@@ -3,7 +3,6 @@ import * as http from "node:http"
|
|
|
3
3
|
import type { RenderResult } from "@testing-library/react"
|
|
4
4
|
import { prettyDOM, render } from "@testing-library/react"
|
|
5
5
|
import * as AtomIO from "atom.io"
|
|
6
|
-
import { realm } from "atom.io"
|
|
7
6
|
import { editRelationsInStore, findRelationsInStore } from "atom.io/data"
|
|
8
7
|
import type { Store } from "atom.io/internal"
|
|
9
8
|
import {
|
|
@@ -105,7 +104,7 @@ export const setupRealtimeTestServer = (
|
|
|
105
104
|
},
|
|
106
105
|
IMPLICIT.STORE,
|
|
107
106
|
)
|
|
108
|
-
const socketRealm =
|
|
107
|
+
const socketRealm = new AtomIO.Realm<RTS.SocketSystemHierarchy>(silo.store)
|
|
109
108
|
|
|
110
109
|
const httpServer = http.createServer((_, res) => res.end(`Hello World!`))
|
|
111
110
|
const address = httpServer.listen(options.port).address()
|