atom.io 0.24.4 → 0.24.6
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/dist/index.d.ts +1 -1
- package/internal/dist/index.cjs +1449 -1403
- package/internal/dist/index.d.ts +10 -9
- package/internal/dist/index.js +1450 -1403
- package/internal/src/atom/dispose-atom.ts +5 -1
- package/internal/src/families/init-family-member.ts +11 -8
- package/internal/src/ingest-updates/ingest-creation-disposal.ts +22 -9
- package/internal/src/molecule/dispose-molecule.ts +27 -16
- package/internal/src/molecule/grow-molecule-in-store.ts +2 -6
- package/internal/src/molecule/make-molecule-in-store.ts +21 -15
- package/internal/src/mutable/tracker.ts +1 -1
- package/internal/src/selector/create-writable-selector.ts +9 -8
- package/internal/src/selector/dispose-selector.ts +4 -0
- package/internal/src/selector/register-selector.ts +3 -2
- package/internal/src/set-state/evict-downstream.ts +10 -8
- package/internal/src/store/store.ts +29 -25
- package/internal/src/timeline/create-timeline.ts +404 -104
- package/internal/src/timeline/index.ts +0 -1
- package/internal/src/transaction/apply-transaction.ts +0 -8
- package/internal/src/transaction/build-transaction.ts +2 -1
- package/package.json +1 -1
- package/realtime-server/dist/index.cjs +0 -61
- package/realtime-server/dist/index.js +3 -64
- package/realtime-server/src/realtime-server-stores/index.ts +0 -1
- package/src/transaction.ts +1 -1
- package/internal/src/timeline/add-atom-to-timeline.ts +0 -265
- package/realtime-server/src/realtime-server-stores/realtime-continuity-store.ts +0 -109
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AtomFamilyToken,
|
|
3
|
+
AtomToken,
|
|
3
4
|
FamilyMetadata,
|
|
4
5
|
Flat,
|
|
5
6
|
Func,
|
|
6
7
|
MoleculeConstructor,
|
|
7
8
|
MoleculeCreation,
|
|
8
9
|
MoleculeDisposal,
|
|
10
|
+
MoleculeFamilyToken,
|
|
9
11
|
ReadableToken,
|
|
10
12
|
StateCreation,
|
|
11
13
|
StateDisposal,
|
|
@@ -15,15 +17,16 @@ import type {
|
|
|
15
17
|
TimelineToken,
|
|
16
18
|
TimelineUpdate,
|
|
17
19
|
TokenType,
|
|
20
|
+
TransactionToken,
|
|
18
21
|
TransactionUpdate,
|
|
22
|
+
TransactionUpdateContent,
|
|
19
23
|
} from "atom.io"
|
|
20
24
|
import { stringifyJson } from "atom.io/json"
|
|
21
25
|
|
|
22
26
|
import { newest } from "../lineage"
|
|
23
|
-
import { getUpdateToken
|
|
27
|
+
import { getUpdateToken } from "../mutable"
|
|
24
28
|
import { type Store, withdraw } from "../store"
|
|
25
29
|
import { Subject } from "../subject"
|
|
26
|
-
import { addAtomToTimeline } from "./add-atom-to-timeline"
|
|
27
30
|
|
|
28
31
|
export type TimelineAtomUpdate<ManagedAtom extends TimelineManageable> = Flat<
|
|
29
32
|
StateUpdate<TokenType<ManagedAtom>> & {
|
|
@@ -100,56 +103,27 @@ export function createTimeline<ManagedAtom extends TimelineManageable>(
|
|
|
100
103
|
}
|
|
101
104
|
const timelineKey = options.key
|
|
102
105
|
const target = newest(store)
|
|
103
|
-
for (const
|
|
104
|
-
|
|
105
|
-
switch (tokenOrFamily.type) {
|
|
106
|
-
case `atom_family`:
|
|
107
|
-
case `mutable_atom_family`:
|
|
108
|
-
{
|
|
109
|
-
const familyToken: AtomFamilyToken<any> = tokenOrFamily
|
|
110
|
-
const family = withdraw(familyToken, store)
|
|
111
|
-
const familyKey = family.key
|
|
112
|
-
target.timelineAtoms.set({ atomKey: familyKey, timelineKey })
|
|
113
|
-
tl.subscriptions.set(
|
|
114
|
-
family.key,
|
|
115
|
-
family.subject.subscribe(
|
|
116
|
-
`timeline:${options.key}`,
|
|
117
|
-
(creationOrDisposal) => {
|
|
118
|
-
handleStateLifecycleEvent(creationOrDisposal, tl, store)
|
|
119
|
-
},
|
|
120
|
-
),
|
|
121
|
-
)
|
|
122
|
-
for (const atom of target.atoms.values()) {
|
|
123
|
-
if (atom.family?.key === familyKey) {
|
|
124
|
-
addAtomToTimeline(atom, tl, store)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
break
|
|
106
|
+
for (const initialTopic of options.scope) {
|
|
107
|
+
switch (initialTopic.type) {
|
|
129
108
|
case `atom`:
|
|
130
109
|
case `mutable_atom`:
|
|
131
110
|
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const familyTimelineKey = target.timelineAtoms.getRelatedKey(
|
|
140
|
-
atom.family.key,
|
|
141
|
-
)
|
|
142
|
-
if (familyTimelineKey) {
|
|
111
|
+
const atomToken: AtomToken<ManagedAtom> = initialTopic
|
|
112
|
+
const atomKey = atomToken.key
|
|
113
|
+
let existingTimelineKey = target.timelineTopics.getRelatedKey(atomKey)
|
|
114
|
+
if (`family` in atomToken) {
|
|
115
|
+
const familyKey = atomToken.family.key
|
|
116
|
+
existingTimelineKey = target.timelineTopics.getRelatedKey(familyKey)
|
|
117
|
+
if (existingTimelineKey) {
|
|
143
118
|
store.logger.error(
|
|
144
119
|
`❌`,
|
|
145
120
|
`timeline`,
|
|
146
121
|
options.key,
|
|
147
|
-
`Failed to add atom "${
|
|
122
|
+
`Failed to add atom "${atomKey}" because its family "${familyKey}" already belongs to timeline "${existingTimelineKey}"`,
|
|
148
123
|
)
|
|
149
124
|
continue
|
|
150
125
|
}
|
|
151
126
|
}
|
|
152
|
-
const existingTimelineKey = target.timelineAtoms.getRelatedKey(atomKey)
|
|
153
127
|
if (existingTimelineKey) {
|
|
154
128
|
store.logger.error(
|
|
155
129
|
`❌`,
|
|
@@ -159,72 +133,46 @@ export function createTimeline<ManagedAtom extends TimelineManageable>(
|
|
|
159
133
|
)
|
|
160
134
|
continue
|
|
161
135
|
}
|
|
162
|
-
addAtomToTimeline(
|
|
136
|
+
addAtomToTimeline(atomToken, tl, store)
|
|
163
137
|
}
|
|
164
138
|
break
|
|
165
|
-
|
|
139
|
+
|
|
140
|
+
case `atom_family`:
|
|
141
|
+
case `mutable_atom_family`:
|
|
166
142
|
{
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
})
|
|
184
|
-
tl.history.push(event)
|
|
185
|
-
tl.at = tl.history.length
|
|
186
|
-
tl.subject.next(event)
|
|
143
|
+
const familyToken: AtomFamilyToken<any, any> = initialTopic
|
|
144
|
+
const familyKey = familyToken.key
|
|
145
|
+
const existingTimelineKey =
|
|
146
|
+
target.timelineTopics.getRelatedKey(familyKey)
|
|
147
|
+
if (existingTimelineKey) {
|
|
148
|
+
store.logger.error(
|
|
149
|
+
`❌`,
|
|
150
|
+
`timeline`,
|
|
151
|
+
options.key,
|
|
152
|
+
`Failed to add atom family "${familyKey}" because it already belongs to timeline "${existingTimelineKey}"`,
|
|
153
|
+
)
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
addAtomFamilyToTimeline(familyToken, tl, store)
|
|
157
|
+
}
|
|
158
|
+
break
|
|
187
159
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
(stateCreationOrDisposal) => {
|
|
201
|
-
handleStateLifecycleEvent(
|
|
202
|
-
stateCreationOrDisposal,
|
|
203
|
-
tl,
|
|
204
|
-
store,
|
|
205
|
-
)
|
|
206
|
-
},
|
|
207
|
-
),
|
|
208
|
-
)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
break
|
|
212
|
-
case `molecule_disposal`:
|
|
213
|
-
tl.subscriptions.get(creationOrDisposal.token.key)?.()
|
|
214
|
-
tl.subscriptions.delete(creationOrDisposal.token.key)
|
|
215
|
-
for (const familyKey of creationOrDisposal.familyKeys) {
|
|
216
|
-
const stateKey = `${familyKey}(${stringifyJson(
|
|
217
|
-
creationOrDisposal.token.key,
|
|
218
|
-
)})`
|
|
219
|
-
tl.subscriptions.get(stateKey)?.()
|
|
220
|
-
tl.subscriptions.delete(stateKey)
|
|
221
|
-
}
|
|
222
|
-
break
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
),
|
|
160
|
+
case `molecule_family`:
|
|
161
|
+
{
|
|
162
|
+
const familyToken: MoleculeFamilyToken<any> = initialTopic
|
|
163
|
+
const familyKey = familyToken.key
|
|
164
|
+
const existingTimelineKey =
|
|
165
|
+
target.timelineTopics.getRelatedKey(familyKey)
|
|
166
|
+
if (existingTimelineKey) {
|
|
167
|
+
store.logger.error(
|
|
168
|
+
`❌`,
|
|
169
|
+
`timeline`,
|
|
170
|
+
options.key,
|
|
171
|
+
`Failed to add molecule family "${familyKey}" because it already belongs to timeline "${existingTimelineKey}"`,
|
|
226
172
|
)
|
|
173
|
+
continue
|
|
227
174
|
}
|
|
175
|
+
addMoleculeFamilyToTimeline(familyToken, tl, store)
|
|
228
176
|
}
|
|
229
177
|
break
|
|
230
178
|
}
|
|
@@ -239,6 +187,353 @@ export function createTimeline<ManagedAtom extends TimelineManageable>(
|
|
|
239
187
|
return token
|
|
240
188
|
}
|
|
241
189
|
|
|
190
|
+
function addAtomToTimeline(
|
|
191
|
+
atomToken: AtomToken<any>,
|
|
192
|
+
tl: Timeline<any>,
|
|
193
|
+
store: Store,
|
|
194
|
+
): void {
|
|
195
|
+
let maybeAtom = withdraw(atomToken, store)
|
|
196
|
+
if (maybeAtom.type === `mutable_atom`) {
|
|
197
|
+
const updateToken = getUpdateToken(maybeAtom)
|
|
198
|
+
maybeAtom = withdraw(updateToken, store)
|
|
199
|
+
}
|
|
200
|
+
const atom = maybeAtom
|
|
201
|
+
store.timelineTopics.set(
|
|
202
|
+
{ topicKey: atom.key, timelineKey: tl.key },
|
|
203
|
+
{ topicType: `atom` },
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
tl.subscriptions.set(
|
|
207
|
+
atom.key,
|
|
208
|
+
atom.subject.subscribe(`timeline`, (update) => {
|
|
209
|
+
const target = newest(store)
|
|
210
|
+
const currentSelectorKey =
|
|
211
|
+
store.operation.open && store.operation.token.type === `selector`
|
|
212
|
+
? store.operation.token.key
|
|
213
|
+
: null
|
|
214
|
+
const currentSelectorTime =
|
|
215
|
+
store.operation.open && store.operation.token.type === `selector`
|
|
216
|
+
? store.operation.time
|
|
217
|
+
: null
|
|
218
|
+
|
|
219
|
+
const txUpdateInProgress = target.on.transactionApplying.state?.update
|
|
220
|
+
|
|
221
|
+
store.logger.info(
|
|
222
|
+
`⏳`,
|
|
223
|
+
`timeline`,
|
|
224
|
+
tl.key,
|
|
225
|
+
`atom`,
|
|
226
|
+
atomToken.key,
|
|
227
|
+
`went`,
|
|
228
|
+
update.oldValue,
|
|
229
|
+
`->`,
|
|
230
|
+
update.newValue,
|
|
231
|
+
txUpdateInProgress
|
|
232
|
+
? `in transaction "${txUpdateInProgress.key}"`
|
|
233
|
+
: currentSelectorKey
|
|
234
|
+
? `in selector "${currentSelectorKey}"`
|
|
235
|
+
: ``,
|
|
236
|
+
)
|
|
237
|
+
if (tl.timeTraveling === null) {
|
|
238
|
+
if (txUpdateInProgress) {
|
|
239
|
+
joinTransaction(tl, txUpdateInProgress, store)
|
|
240
|
+
} else if (currentSelectorKey && currentSelectorTime) {
|
|
241
|
+
let latestUpdate: TimelineUpdate<any> | undefined = tl.history.at(-1)
|
|
242
|
+
|
|
243
|
+
if (currentSelectorTime !== tl.selectorTime) {
|
|
244
|
+
latestUpdate = {
|
|
245
|
+
type: `selector_update`,
|
|
246
|
+
timestamp: currentSelectorTime,
|
|
247
|
+
key: currentSelectorKey,
|
|
248
|
+
atomUpdates: [],
|
|
249
|
+
}
|
|
250
|
+
latestUpdate.atomUpdates.push({
|
|
251
|
+
key: atom.key,
|
|
252
|
+
type: `atom_update`,
|
|
253
|
+
...update,
|
|
254
|
+
})
|
|
255
|
+
if (tl.at !== tl.history.length) {
|
|
256
|
+
tl.history.splice(tl.at)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
tl.history.push(latestUpdate)
|
|
260
|
+
|
|
261
|
+
store.logger.info(
|
|
262
|
+
`⌛`,
|
|
263
|
+
`timeline`,
|
|
264
|
+
tl.key,
|
|
265
|
+
`got a selector_update "${currentSelectorKey}" with`,
|
|
266
|
+
latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
tl.at = tl.history.length
|
|
270
|
+
tl.selectorTime = currentSelectorTime
|
|
271
|
+
} else {
|
|
272
|
+
if (latestUpdate?.type === `selector_update`) {
|
|
273
|
+
latestUpdate.atomUpdates.push({
|
|
274
|
+
key: atom.key,
|
|
275
|
+
type: `atom_update`,
|
|
276
|
+
...update,
|
|
277
|
+
})
|
|
278
|
+
store.logger.info(
|
|
279
|
+
`⌛`,
|
|
280
|
+
`timeline`,
|
|
281
|
+
tl.key,
|
|
282
|
+
`set selector_update "${currentSelectorKey}" to`,
|
|
283
|
+
latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.key),
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (latestUpdate) {
|
|
288
|
+
const willCaptureSelectorUpdate =
|
|
289
|
+
tl.shouldCapture?.(latestUpdate, tl) ?? true
|
|
290
|
+
if (willCaptureSelectorUpdate) {
|
|
291
|
+
tl.subject.next(latestUpdate)
|
|
292
|
+
} else {
|
|
293
|
+
tl.history.pop()
|
|
294
|
+
tl.at = tl.history.length
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
const timestamp = Date.now()
|
|
299
|
+
tl.selectorTime = null
|
|
300
|
+
if (tl.at !== tl.history.length) {
|
|
301
|
+
tl.history.splice(tl.at)
|
|
302
|
+
}
|
|
303
|
+
const atomUpdate: TimelineAtomUpdate<any> = {
|
|
304
|
+
type: `atom_update`,
|
|
305
|
+
timestamp,
|
|
306
|
+
key: atom.key,
|
|
307
|
+
oldValue: update.oldValue,
|
|
308
|
+
newValue: update.newValue,
|
|
309
|
+
}
|
|
310
|
+
if (atom.family) {
|
|
311
|
+
atomUpdate.family = atom.family
|
|
312
|
+
}
|
|
313
|
+
const willCapture = tl.shouldCapture?.(atomUpdate, tl) ?? true
|
|
314
|
+
store.logger.info(
|
|
315
|
+
`⌛`,
|
|
316
|
+
`timeline`,
|
|
317
|
+
tl.key,
|
|
318
|
+
`got an atom_update to "${atom.key}"`,
|
|
319
|
+
)
|
|
320
|
+
if (willCapture) {
|
|
321
|
+
tl.history.push(atomUpdate)
|
|
322
|
+
tl.at = tl.history.length
|
|
323
|
+
tl.subject.next(atomUpdate)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}),
|
|
328
|
+
)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function addAtomFamilyToTimeline(
|
|
332
|
+
atomFamilyToken: AtomFamilyToken<any, any>,
|
|
333
|
+
tl: Timeline<any>,
|
|
334
|
+
store: Store,
|
|
335
|
+
): void {
|
|
336
|
+
const family = withdraw(atomFamilyToken, store)
|
|
337
|
+
store.timelineTopics.set(
|
|
338
|
+
{ topicKey: family.key, timelineKey: tl.key },
|
|
339
|
+
{ topicType: `atom_family` },
|
|
340
|
+
)
|
|
341
|
+
tl.subscriptions.set(
|
|
342
|
+
family.key,
|
|
343
|
+
family.subject.subscribe(`timeline`, (creationOrDisposal) => {
|
|
344
|
+
handleStateLifecycleEvent(creationOrDisposal, tl, store)
|
|
345
|
+
}),
|
|
346
|
+
)
|
|
347
|
+
for (const atom of store.atoms.values()) {
|
|
348
|
+
if (atom.family?.key === family.key) {
|
|
349
|
+
addAtomToTimeline(atom, tl, store)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function addMoleculeFamilyToTimeline(
|
|
355
|
+
familyToken: MoleculeFamilyToken<any>,
|
|
356
|
+
tl: Timeline<any>,
|
|
357
|
+
store: Store,
|
|
358
|
+
): void {
|
|
359
|
+
store.timelineTopics.set(
|
|
360
|
+
{ topicKey: familyToken.key, timelineKey: tl.key },
|
|
361
|
+
{ topicType: `molecule_family` },
|
|
362
|
+
)
|
|
363
|
+
const family = store.moleculeFamilies.get(familyToken.key)
|
|
364
|
+
if (family) {
|
|
365
|
+
tl.subscriptions.set(
|
|
366
|
+
familyToken.key,
|
|
367
|
+
family.subject.subscribe(`timeline:${tl.key}`, (creationOrDisposal) => {
|
|
368
|
+
store.logger.info(
|
|
369
|
+
`🐞`,
|
|
370
|
+
`timeline`,
|
|
371
|
+
tl.key,
|
|
372
|
+
`got a molecule creation or disposal`,
|
|
373
|
+
creationOrDisposal,
|
|
374
|
+
)
|
|
375
|
+
switch (creationOrDisposal.type) {
|
|
376
|
+
case `molecule_creation`:
|
|
377
|
+
{
|
|
378
|
+
store.timelineTopics.set(
|
|
379
|
+
{
|
|
380
|
+
topicKey: creationOrDisposal.token.key,
|
|
381
|
+
timelineKey: tl.key,
|
|
382
|
+
},
|
|
383
|
+
{ topicType: `molecule` },
|
|
384
|
+
)
|
|
385
|
+
const txUpdateInProgress =
|
|
386
|
+
newest(store).on.transactionApplying.state?.update
|
|
387
|
+
if (txUpdateInProgress) {
|
|
388
|
+
joinTransaction(tl, txUpdateInProgress, store)
|
|
389
|
+
} else if (tl.timeTraveling === null) {
|
|
390
|
+
const event = Object.assign(creationOrDisposal, {
|
|
391
|
+
timestamp: Date.now(),
|
|
392
|
+
})
|
|
393
|
+
tl.history.push(event)
|
|
394
|
+
tl.at = tl.history.length
|
|
395
|
+
tl.subject.next(event)
|
|
396
|
+
}
|
|
397
|
+
const molecule = withdraw(creationOrDisposal.token, store)
|
|
398
|
+
|
|
399
|
+
for (const token of molecule.tokens.values()) {
|
|
400
|
+
switch (token.type) {
|
|
401
|
+
case `atom`:
|
|
402
|
+
case `mutable_atom`:
|
|
403
|
+
addAtomToTimeline(token, tl, store)
|
|
404
|
+
break
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
tl.subscriptions.set(
|
|
408
|
+
molecule.key,
|
|
409
|
+
molecule.subject.subscribe(
|
|
410
|
+
`timeline:${tl.key}`,
|
|
411
|
+
(stateCreationOrDisposal) => {
|
|
412
|
+
handleStateLifecycleEvent(stateCreationOrDisposal, tl, store)
|
|
413
|
+
},
|
|
414
|
+
),
|
|
415
|
+
)
|
|
416
|
+
}
|
|
417
|
+
break
|
|
418
|
+
case `molecule_disposal`:
|
|
419
|
+
{
|
|
420
|
+
const txUpdateInProgress =
|
|
421
|
+
newest(store).on.transactionApplying.state?.update
|
|
422
|
+
if (txUpdateInProgress) {
|
|
423
|
+
joinTransaction(tl, txUpdateInProgress, store)
|
|
424
|
+
} else if (tl.timeTraveling === null) {
|
|
425
|
+
const event = Object.assign(creationOrDisposal, {
|
|
426
|
+
timestamp: Date.now(),
|
|
427
|
+
})
|
|
428
|
+
tl.history.push(event)
|
|
429
|
+
tl.at = tl.history.length
|
|
430
|
+
tl.subject.next(event)
|
|
431
|
+
}
|
|
432
|
+
const moleculeKey = creationOrDisposal.token.key
|
|
433
|
+
tl.subscriptions.get(moleculeKey)?.()
|
|
434
|
+
tl.subscriptions.delete(moleculeKey)
|
|
435
|
+
for (const [familyKey] of creationOrDisposal.values) {
|
|
436
|
+
const stateKey = `${familyKey}(${stringifyJson(moleculeKey)})`
|
|
437
|
+
tl.subscriptions.get(stateKey)?.()
|
|
438
|
+
tl.subscriptions.delete(stateKey)
|
|
439
|
+
store.timelineTopics.delete(stateKey)
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
break
|
|
443
|
+
}
|
|
444
|
+
}),
|
|
445
|
+
)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function joinTransaction(
|
|
450
|
+
tl: Timeline<any>,
|
|
451
|
+
txUpdateInProgress: TransactionUpdate<Func>,
|
|
452
|
+
store: Store,
|
|
453
|
+
) {
|
|
454
|
+
const currentTxKey = txUpdateInProgress.key
|
|
455
|
+
const currentTxInstanceId = txUpdateInProgress.id
|
|
456
|
+
const currentTxToken: TransactionToken<any> = {
|
|
457
|
+
key: currentTxKey,
|
|
458
|
+
type: `transaction`,
|
|
459
|
+
}
|
|
460
|
+
const currentTransaction = withdraw(currentTxToken, store)
|
|
461
|
+
if (currentTxKey && tl.transactionKey === null) {
|
|
462
|
+
tl.transactionKey = currentTxKey
|
|
463
|
+
const unsubscribe = currentTransaction.subject.subscribe(
|
|
464
|
+
`timeline:${tl.key}`,
|
|
465
|
+
(transactionUpdate) => {
|
|
466
|
+
unsubscribe()
|
|
467
|
+
tl.transactionKey = null
|
|
468
|
+
if (tl.timeTraveling === null && currentTxInstanceId) {
|
|
469
|
+
if (tl.at !== tl.history.length) {
|
|
470
|
+
tl.history.splice(tl.at)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// biome-ignore lint/style/noNonNullAssertion: we are in the context of this timeline
|
|
474
|
+
const timelineTopics = store.timelineTopics.getRelatedKeys(tl.key)!
|
|
475
|
+
|
|
476
|
+
const updates = filterTransactionUpdates(
|
|
477
|
+
transactionUpdate.updates,
|
|
478
|
+
timelineTopics,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
const timelineTransactionUpdate: TimelineTransactionUpdate = {
|
|
482
|
+
timestamp: Date.now(),
|
|
483
|
+
...transactionUpdate,
|
|
484
|
+
updates,
|
|
485
|
+
}
|
|
486
|
+
const willCapture =
|
|
487
|
+
tl.shouldCapture?.(timelineTransactionUpdate, tl) ?? true
|
|
488
|
+
if (willCapture) {
|
|
489
|
+
tl.history.push(timelineTransactionUpdate)
|
|
490
|
+
tl.at = tl.history.length
|
|
491
|
+
tl.subject.next(timelineTransactionUpdate)
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
)
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function filterTransactionUpdates(
|
|
500
|
+
updates: TransactionUpdateContent[],
|
|
501
|
+
timelineTopics: Set<string>,
|
|
502
|
+
): TransactionUpdateContent[] {
|
|
503
|
+
return updates
|
|
504
|
+
.filter((updateFromTx) => {
|
|
505
|
+
if (updateFromTx.type === `transaction_update`) {
|
|
506
|
+
return true
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
let key: string
|
|
510
|
+
switch (updateFromTx.type) {
|
|
511
|
+
case `state_creation`:
|
|
512
|
+
case `state_disposal`:
|
|
513
|
+
case `molecule_creation`:
|
|
514
|
+
case `molecule_disposal`:
|
|
515
|
+
key = updateFromTx.token.key
|
|
516
|
+
break
|
|
517
|
+
default:
|
|
518
|
+
key = updateFromTx.key
|
|
519
|
+
break
|
|
520
|
+
}
|
|
521
|
+
return timelineTopics.has(key)
|
|
522
|
+
})
|
|
523
|
+
.map((updateFromTx) => {
|
|
524
|
+
if (`updates` in updateFromTx) {
|
|
525
|
+
return {
|
|
526
|
+
...updateFromTx,
|
|
527
|
+
updates: filterTransactionUpdates(
|
|
528
|
+
updateFromTx.updates,
|
|
529
|
+
timelineTopics,
|
|
530
|
+
),
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return updateFromTx
|
|
534
|
+
})
|
|
535
|
+
}
|
|
536
|
+
|
|
242
537
|
function handleStateLifecycleEvent(
|
|
243
538
|
event: StateCreation<any> | StateDisposal<any>,
|
|
244
539
|
tl: Timeline<any>,
|
|
@@ -249,9 +544,14 @@ function handleStateLifecycleEvent(
|
|
|
249
544
|
timestamp,
|
|
250
545
|
}) as TimelineUpdate<any>
|
|
251
546
|
if (!tl.timeTraveling) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
547
|
+
const txUpdateInProgress = newest(store).on.transactionApplying.state?.update
|
|
548
|
+
if (txUpdateInProgress) {
|
|
549
|
+
joinTransaction(tl, txUpdateInProgress, store)
|
|
550
|
+
} else {
|
|
551
|
+
tl.history.push(timelineEvent)
|
|
552
|
+
tl.at = tl.history.length
|
|
553
|
+
tl.subject.next(timelineEvent)
|
|
554
|
+
}
|
|
255
555
|
}
|
|
256
556
|
switch (event.type) {
|
|
257
557
|
case `state_creation`:
|
|
@@ -38,14 +38,6 @@ export const applyTransaction = <F extends Func>(
|
|
|
38
38
|
`Applying transaction with ${updates.length} updates:`,
|
|
39
39
|
updates,
|
|
40
40
|
)
|
|
41
|
-
for (const tracker of child.trackers.values()) {
|
|
42
|
-
const mutableKey = tracker.mutableState.key
|
|
43
|
-
if (!parent.atoms.has(mutableKey)) {
|
|
44
|
-
const atom = child.atoms.get(mutableKey)
|
|
45
|
-
atom?.install(parent)
|
|
46
|
-
}
|
|
47
|
-
tracker.dispose()
|
|
48
|
-
}
|
|
49
41
|
|
|
50
42
|
ingestTransactionUpdate(`newValue`, child.transactionMeta.update, parent)
|
|
51
43
|
|
|
@@ -38,7 +38,7 @@ export const buildTransaction = (
|
|
|
38
38
|
operation: { open: false },
|
|
39
39
|
readonlySelectors: new LazyMap(parent.readonlySelectors),
|
|
40
40
|
timelines: new LazyMap(parent.timelines),
|
|
41
|
-
|
|
41
|
+
timelineTopics: new Junction(parent.timelineTopics.toJSON()),
|
|
42
42
|
trackers: new Map(),
|
|
43
43
|
transactions: new LazyMap(parent.transactions),
|
|
44
44
|
selectorAtoms: new Junction(parent.selectorAtoms.toJSON()),
|
|
@@ -49,6 +49,7 @@ export const buildTransaction = (
|
|
|
49
49
|
valueMap: new LazyMap(parent.valueMap),
|
|
50
50
|
molecules: new LazyMap(parent.molecules),
|
|
51
51
|
moleculeFamilies: new LazyMap(parent.moleculeFamilies),
|
|
52
|
+
moleculeInProgress: parent.moleculeInProgress,
|
|
52
53
|
miscResources: new LazyMap(parent.miscResources),
|
|
53
54
|
}
|
|
54
55
|
const epoch = getEpochNumberOfAction(key, store)
|
package/package.json
CHANGED
|
@@ -369,67 +369,6 @@ function realtimeActionReceiver({
|
|
|
369
369
|
};
|
|
370
370
|
};
|
|
371
371
|
}
|
|
372
|
-
AtomIO.selectorFamily({
|
|
373
|
-
key: `perspectiveRedactor`,
|
|
374
|
-
get: ({ userId, syncGroupKey }) => ({ get, find }) => {
|
|
375
|
-
const syncGroup = realtime.SyncGroup.existing.get(syncGroupKey);
|
|
376
|
-
if (!syncGroup) {
|
|
377
|
-
throw new Error(
|
|
378
|
-
`Tried to create a synchronizer for a sync group that does not exist.`
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
const userPerspectiveTokens = syncGroup.perspectives.flatMap(
|
|
382
|
-
({ viewAtoms }) => {
|
|
383
|
-
const userPerspectiveToken = find(viewAtoms, userId);
|
|
384
|
-
const userPerspective = get(userPerspectiveToken);
|
|
385
|
-
const visibleTokens = [...userPerspective].map((token) => {
|
|
386
|
-
return token.type === `mutable_atom` ? internal.getUpdateToken(token).key : token.key;
|
|
387
|
-
});
|
|
388
|
-
internal.IMPLICIT.STORE.logger.info(
|
|
389
|
-
`\u{1F52D}`,
|
|
390
|
-
`continuity`,
|
|
391
|
-
syncGroupKey,
|
|
392
|
-
`${userId} can see ${visibleTokens.length} tokens in ${viewAtoms.key}`,
|
|
393
|
-
visibleTokens
|
|
394
|
-
);
|
|
395
|
-
return visibleTokens;
|
|
396
|
-
}
|
|
397
|
-
);
|
|
398
|
-
const filterTransactionUpdate = (visible, transactionUpdate) => {
|
|
399
|
-
internal.IMPLICIT.STORE.logger.info(
|
|
400
|
-
`\u{1F58C}`,
|
|
401
|
-
`continuity`,
|
|
402
|
-
syncGroupKey,
|
|
403
|
-
`redacting updates from ${transactionUpdate.epoch}:${transactionUpdate.key}:${transactionUpdate.id}`,
|
|
404
|
-
visible,
|
|
405
|
-
transactionUpdate.updates
|
|
406
|
-
);
|
|
407
|
-
const updates = transactionUpdate.updates.filter((update) => {
|
|
408
|
-
if (`newValue` in update) {
|
|
409
|
-
return visible.includes(update.key);
|
|
410
|
-
}
|
|
411
|
-
return true;
|
|
412
|
-
}).map((update) => {
|
|
413
|
-
if (`updates` in update) {
|
|
414
|
-
return filterTransactionUpdate(visible, update);
|
|
415
|
-
}
|
|
416
|
-
return update;
|
|
417
|
-
});
|
|
418
|
-
const filtered = __spreadProps(__spreadValues({}, transactionUpdate), {
|
|
419
|
-
updates
|
|
420
|
-
});
|
|
421
|
-
return filtered;
|
|
422
|
-
};
|
|
423
|
-
const filter = (update) => {
|
|
424
|
-
const visibleKeys = syncGroup.globals.map(
|
|
425
|
-
(atomToken) => atomToken.type === `mutable_atom` ? internal.getUpdateToken(atomToken).key : atomToken.key
|
|
426
|
-
);
|
|
427
|
-
visibleKeys.push(...userPerspectiveTokens);
|
|
428
|
-
return filterTransactionUpdate(visibleKeys, update);
|
|
429
|
-
};
|
|
430
|
-
return filter;
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
372
|
var roomArgumentsAtoms = AtomIO.atomFamily({
|
|
434
373
|
key: `roomArguments`,
|
|
435
374
|
default: [`echo`, [`Hello World!`]]
|