atom.io 0.29.5 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/dist/index.d.ts +84 -51
- package/data/dist/index.js +35 -31
- package/data/src/join.ts +240 -134
- package/dist/chunk-ADMEAXYU.js +167 -0
- package/dist/{chunk-TCINPEYE.js → chunk-SMKF3ZNG.js} +221 -137
- package/dist/index.d.ts +75 -10
- package/dist/index.js +3 -17
- package/internal/dist/index.d.ts +76 -41
- package/internal/dist/index.js +2 -1
- package/internal/src/atom/dispose-atom.ts +4 -8
- package/internal/src/index.ts +1 -1
- package/internal/src/ingest-updates/ingest-creation-disposal.ts +71 -27
- package/internal/src/junction.ts +152 -84
- package/internal/src/molecule/create-molecule-family.ts +2 -2
- package/internal/src/molecule/dispose-molecule.ts +4 -2
- package/internal/src/molecule/make-molecule-in-store.ts +11 -9
- package/internal/src/molecule/molecule-internal.ts +12 -8
- package/internal/src/mutable/create-mutable-atom-family.ts +2 -2
- package/internal/src/mutable/get-json-family.ts +2 -2
- package/internal/src/store/store.ts +10 -2
- package/internal/src/timeline/create-timeline.ts +99 -71
- package/internal/src/transaction/index.ts +1 -1
- package/internal/src/utility-types.ts +9 -2
- package/json/dist/index.d.ts +3 -3
- package/json/dist/index.js +2 -1
- package/json/src/entries.ts +3 -3
- package/package.json +15 -15
- package/react-devtools/dist/index.js +9 -5
- package/react-devtools/src/TimelineIndex.tsx +4 -1
- package/react-devtools/src/Updates.tsx +18 -3
- package/realtime/dist/index.d.ts +1 -1
- package/realtime/dist/index.js +3 -1
- package/realtime/src/shared-room-store.ts +2 -0
- package/realtime-server/dist/index.d.ts +13 -4
- package/realtime-server/dist/index.js +4 -2
- package/realtime-server/src/realtime-continuity-synchronizer.ts +2 -2
- package/realtime-server/src/realtime-server-stores/server-user-store.ts +17 -1
- package/realtime-testing/dist/index.d.ts +3 -0
- package/realtime-testing/dist/index.js +11 -4
- package/realtime-testing/src/setup-realtime-test.tsx +12 -4
- package/src/allocate.ts +277 -0
- package/src/index.ts +1 -0
- package/src/molecule.ts +9 -5
- package/src/transaction.ts +22 -4
|
@@ -25,6 +25,7 @@ import { newest } from "../lineage"
|
|
|
25
25
|
import { getUpdateToken } from "../mutable"
|
|
26
26
|
import { type Store, withdraw } from "../store"
|
|
27
27
|
import { Subject } from "../subject"
|
|
28
|
+
import { isChildStore } from "../transaction"
|
|
28
29
|
import type { Flat, Func } from "../utility-types"
|
|
29
30
|
|
|
30
31
|
export type TimelineAtomUpdate<ManagedAtom extends TimelineManageable> = Flat<
|
|
@@ -371,74 +372,81 @@ function addMoleculeFamilyToTimeline(
|
|
|
371
372
|
`got a molecule creation or disposal`,
|
|
372
373
|
creationOrDisposal,
|
|
373
374
|
)
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
375
|
+
if (creationOrDisposal.subType === `classic`) {
|
|
376
|
+
switch (creationOrDisposal.type) {
|
|
377
|
+
case `molecule_creation`:
|
|
378
|
+
{
|
|
379
|
+
store.timelineTopics.set(
|
|
380
|
+
{
|
|
381
|
+
topicKey: creationOrDisposal.token.key,
|
|
382
|
+
timelineKey: tl.key,
|
|
383
|
+
},
|
|
384
|
+
{ topicType: `molecule` },
|
|
385
|
+
)
|
|
386
|
+
const txUpdateInProgress =
|
|
387
|
+
newest(store).on.transactionApplying.state?.update
|
|
388
|
+
if (txUpdateInProgress) {
|
|
389
|
+
joinTransaction(tl, txUpdateInProgress, store)
|
|
390
|
+
} else if (tl.timeTraveling === null) {
|
|
391
|
+
const event = Object.assign(creationOrDisposal, {
|
|
392
|
+
timestamp: Date.now(),
|
|
393
|
+
})
|
|
394
|
+
tl.history.push(event)
|
|
395
|
+
tl.at = tl.history.length
|
|
396
|
+
tl.subject.next(event)
|
|
397
|
+
}
|
|
398
|
+
const molecule = withdraw(creationOrDisposal.token, store)
|
|
397
399
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
400
|
+
for (const token of molecule.tokens.values()) {
|
|
401
|
+
switch (token.type) {
|
|
402
|
+
case `atom`:
|
|
403
|
+
case `mutable_atom`:
|
|
404
|
+
addAtomToTimeline(token, tl, store)
|
|
405
|
+
break
|
|
406
|
+
}
|
|
404
407
|
}
|
|
408
|
+
tl.subscriptions.set(
|
|
409
|
+
molecule.key,
|
|
410
|
+
molecule.subject.subscribe(
|
|
411
|
+
`timeline:${tl.key}`,
|
|
412
|
+
(stateCreationOrDisposal) => {
|
|
413
|
+
handleStateLifecycleEvent(
|
|
414
|
+
stateCreationOrDisposal,
|
|
415
|
+
tl,
|
|
416
|
+
store,
|
|
417
|
+
)
|
|
418
|
+
},
|
|
419
|
+
),
|
|
420
|
+
)
|
|
405
421
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
(
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const moleculeKey = creationOrDisposal.token.key
|
|
432
|
-
tl.subscriptions.get(moleculeKey)?.()
|
|
433
|
-
tl.subscriptions.delete(moleculeKey)
|
|
434
|
-
for (const [familyKey] of creationOrDisposal.values) {
|
|
435
|
-
const stateKey = `${familyKey}(${stringifyJson(moleculeKey)})`
|
|
436
|
-
tl.subscriptions.get(stateKey)?.()
|
|
437
|
-
tl.subscriptions.delete(stateKey)
|
|
438
|
-
store.timelineTopics.delete(stateKey)
|
|
422
|
+
break
|
|
423
|
+
case `molecule_disposal`:
|
|
424
|
+
{
|
|
425
|
+
const txUpdateInProgress =
|
|
426
|
+
newest(store).on.transactionApplying.state?.update
|
|
427
|
+
if (txUpdateInProgress) {
|
|
428
|
+
joinTransaction(tl, txUpdateInProgress, store)
|
|
429
|
+
} else if (tl.timeTraveling === null) {
|
|
430
|
+
const event = Object.assign(creationOrDisposal, {
|
|
431
|
+
timestamp: Date.now(),
|
|
432
|
+
})
|
|
433
|
+
tl.history.push(event)
|
|
434
|
+
tl.at = tl.history.length
|
|
435
|
+
tl.subject.next(event)
|
|
436
|
+
}
|
|
437
|
+
const moleculeKey = stringifyJson(creationOrDisposal.token.key)
|
|
438
|
+
|
|
439
|
+
tl.subscriptions.get(moleculeKey)?.()
|
|
440
|
+
tl.subscriptions.delete(moleculeKey)
|
|
441
|
+
for (const [familyKey] of creationOrDisposal.values) {
|
|
442
|
+
const stateKey = `${familyKey}(${stringifyJson(moleculeKey)})`
|
|
443
|
+
tl.subscriptions.get(stateKey)?.()
|
|
444
|
+
tl.subscriptions.delete(stateKey)
|
|
445
|
+
store.timelineTopics.delete(stateKey)
|
|
446
|
+
}
|
|
439
447
|
}
|
|
440
|
-
|
|
441
|
-
|
|
448
|
+
break
|
|
449
|
+
}
|
|
442
450
|
}
|
|
443
451
|
}),
|
|
444
452
|
)
|
|
@@ -506,17 +514,32 @@ function filterTransactionUpdates(
|
|
|
506
514
|
}
|
|
507
515
|
|
|
508
516
|
let key: string
|
|
517
|
+
let familyKey: string | undefined
|
|
509
518
|
switch (updateFromTx.type) {
|
|
510
519
|
case `state_creation`:
|
|
511
520
|
case `state_disposal`:
|
|
521
|
+
key = updateFromTx.token.key
|
|
522
|
+
familyKey = updateFromTx.token.family?.key
|
|
523
|
+
break
|
|
512
524
|
case `molecule_creation`:
|
|
513
525
|
case `molecule_disposal`:
|
|
514
|
-
|
|
526
|
+
switch (updateFromTx.subType) {
|
|
527
|
+
case `classic`:
|
|
528
|
+
key = updateFromTx.token.key
|
|
529
|
+
break
|
|
530
|
+
case `modern`:
|
|
531
|
+
return true // always include
|
|
532
|
+
}
|
|
515
533
|
break
|
|
516
534
|
default:
|
|
517
535
|
key = updateFromTx.key
|
|
536
|
+
familyKey = updateFromTx.family?.key
|
|
518
537
|
break
|
|
519
538
|
}
|
|
539
|
+
timelineTopics.has(key)
|
|
540
|
+
if (familyKey && timelineTopics.has(familyKey)) {
|
|
541
|
+
return true
|
|
542
|
+
}
|
|
520
543
|
return timelineTopics.has(key)
|
|
521
544
|
})
|
|
522
545
|
.map((updateFromTx) => {
|
|
@@ -543,13 +566,18 @@ function handleStateLifecycleEvent(
|
|
|
543
566
|
timestamp,
|
|
544
567
|
}) as TimelineUpdate<any>
|
|
545
568
|
if (!tl.timeTraveling) {
|
|
546
|
-
const
|
|
547
|
-
if (
|
|
548
|
-
|
|
569
|
+
const target = newest(store)
|
|
570
|
+
if (isChildStore(target)) {
|
|
571
|
+
// we don't want to update the true timeline while we are in a transaction
|
|
549
572
|
} else {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
573
|
+
const txUpdateInProgress = target.on.transactionApplying.state
|
|
574
|
+
if (txUpdateInProgress) {
|
|
575
|
+
joinTransaction(tl, txUpdateInProgress.update, store)
|
|
576
|
+
} else {
|
|
577
|
+
tl.history.push(timelineEvent)
|
|
578
|
+
tl.at = tl.history.length
|
|
579
|
+
tl.subject.next(timelineEvent)
|
|
580
|
+
}
|
|
553
581
|
}
|
|
554
582
|
}
|
|
555
583
|
switch (event.type) {
|
|
@@ -4,6 +4,13 @@ export type Flat<R extends { [K in PropertyKey]: any }> = {
|
|
|
4
4
|
[K in keyof R]: R[K]
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export type
|
|
7
|
+
export type Count<N extends number, A extends any[] = []> = [
|
|
8
|
+
...A,
|
|
9
|
+
any,
|
|
10
|
+
][`length`] extends N
|
|
8
11
|
? A[`length`]
|
|
9
|
-
: A[`length`] |
|
|
12
|
+
: A[`length`] | Count<N, [...A, any]>
|
|
13
|
+
|
|
14
|
+
export type Each<E extends any[]> = {
|
|
15
|
+
[P in Count<E[`length`]>]: E[P]
|
|
16
|
+
}
|
package/json/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Count, Flat, Store, Transceiver } from 'atom.io/internal';
|
|
2
2
|
import * as AtomIO from 'atom.io';
|
|
3
3
|
|
|
4
4
|
type Entries<K extends PropertyKey = keyof any, V = any> = [K, V][];
|
|
5
5
|
type KeyOfEntries<E extends Entries> = E extends [infer K, any][] ? K : never;
|
|
6
6
|
type ValueOfEntry<E extends Entries, K extends KeyOfEntries<E>> = {
|
|
7
|
-
[P in
|
|
8
|
-
}[
|
|
7
|
+
[P in Count<E[`length`]>]: E[P] extends [K, infer V] ? V : never;
|
|
8
|
+
}[Count<E[`length`]>];
|
|
9
9
|
type FromEntries<E extends Entries> = Flat<{
|
|
10
10
|
[K in KeyOfEntries<E>]: ValueOfEntry<E, K>;
|
|
11
11
|
}>;
|
package/json/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createWritableSelectorFamily } from '../../dist/chunk-
|
|
1
|
+
import { createWritableSelectorFamily } from '../../dist/chunk-SMKF3ZNG.js';
|
|
2
|
+
import '../../dist/chunk-ADMEAXYU.js';
|
|
2
3
|
import '../../dist/chunk-XWL6SNVU.js';
|
|
3
4
|
import { createStandaloneSelector, IMPLICIT, growMoleculeInStore, initFamilyMemberInStore, withdraw, seekInStore } from 'atom.io/internal';
|
|
4
5
|
|
package/json/src/entries.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Count, Flat } from "atom.io/internal"
|
|
2
2
|
|
|
3
3
|
export type Entries<K extends PropertyKey = keyof any, V = any> = [K, V][]
|
|
4
4
|
|
|
@@ -7,8 +7,8 @@ export type KeyOfEntries<E extends Entries> = E extends [infer K, any][]
|
|
|
7
7
|
: never
|
|
8
8
|
|
|
9
9
|
export type ValueOfEntry<E extends Entries, K extends KeyOfEntries<E>> = {
|
|
10
|
-
[P in
|
|
11
|
-
}[
|
|
10
|
+
[P in Count<E[`length`]>]: E[P] extends [K, infer V] ? V : never
|
|
11
|
+
}[Count<E[`length`]>]
|
|
12
12
|
|
|
13
13
|
export type FromEntries<E extends Entries> = Flat<{
|
|
14
14
|
[K in KeyOfEntries<E>]: ValueOfEntry<E, K>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atom.io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.1",
|
|
4
4
|
"description": "Composable and testable reactive data library.",
|
|
5
5
|
"homepage": "https://atom.io.fyi",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -57,34 +57,34 @@
|
|
|
57
57
|
"@types/estree": "1.0.6",
|
|
58
58
|
"@types/http-proxy": "1.17.15",
|
|
59
59
|
"@types/npmlog": "7.0.0",
|
|
60
|
-
"@types/react": "18.3.
|
|
60
|
+
"@types/react": "18.3.12",
|
|
61
61
|
"@types/tmp": "0.2.6",
|
|
62
|
-
"@typescript-eslint/parser": "8.
|
|
63
|
-
"@typescript-eslint/rule-tester": "8.
|
|
64
|
-
"@vitest/coverage-v8": "2.1.
|
|
65
|
-
"@vitest/ui": "2.1.
|
|
62
|
+
"@typescript-eslint/parser": "8.10.0",
|
|
63
|
+
"@typescript-eslint/rule-tester": "8.10.0",
|
|
64
|
+
"@vitest/coverage-v8": "2.1.3",
|
|
65
|
+
"@vitest/ui": "2.1.3",
|
|
66
66
|
"concurrently": "9.0.1",
|
|
67
|
-
"drizzle-kit": "0.
|
|
68
|
-
"drizzle-orm": "0.
|
|
69
|
-
"eslint": "9.
|
|
70
|
-
"framer-motion": "11.
|
|
67
|
+
"drizzle-kit": "0.26.2",
|
|
68
|
+
"drizzle-orm": "0.35.3",
|
|
69
|
+
"eslint": "9.13.0",
|
|
70
|
+
"framer-motion": "11.11.9",
|
|
71
71
|
"happy-dom": "15.7.4",
|
|
72
72
|
"http-proxy": "1.18.1",
|
|
73
73
|
"npmlog": "7.0.1",
|
|
74
74
|
"postgres": "3.4.4",
|
|
75
|
-
"preact": "10.24.
|
|
75
|
+
"preact": "10.24.3",
|
|
76
76
|
"react": "18.3.1",
|
|
77
77
|
"react-dom": "18.3.1",
|
|
78
|
-
"react-router-dom": "6.
|
|
78
|
+
"react-router-dom": "6.27.0",
|
|
79
79
|
"socket.io": "4.8.0",
|
|
80
80
|
"socket.io-client": "4.8.0",
|
|
81
81
|
"tmp": "0.2.3",
|
|
82
82
|
"tsup": "8.3.0",
|
|
83
83
|
"tsx": "4.19.1",
|
|
84
|
-
"typescript": "5.6.
|
|
85
|
-
"vite": "5.4.
|
|
84
|
+
"typescript": "5.6.3",
|
|
85
|
+
"vite": "5.4.10",
|
|
86
86
|
"vite-tsconfig-paths": "5.0.1",
|
|
87
|
-
"vitest": "2.1.
|
|
87
|
+
"vitest": "2.1.3",
|
|
88
88
|
"zod": "3.23.8"
|
|
89
89
|
},
|
|
90
90
|
"main": "dist/index.js",
|
|
@@ -6,7 +6,7 @@ import { undo, redo, getState } from 'atom.io';
|
|
|
6
6
|
import { IMPLICIT, createStandaloneAtom, createAtomFamily, findInStore, become } from 'atom.io/internal';
|
|
7
7
|
import { jsonRefinery, attachIntrospectionStates, primitiveRefinery, prettyJson, discoverType } from 'atom.io/introspection';
|
|
8
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
-
import { isJson, fromEntries, toEntries, JSON_DEFAULTS } from 'atom.io/json';
|
|
9
|
+
import { isJson, fromEntries, toEntries, JSON_DEFAULTS, stringifyJson } from 'atom.io/json';
|
|
10
10
|
import { persistSync } from 'atom.io/web';
|
|
11
11
|
|
|
12
12
|
var OpenClose = ({ isOpen, setIsOpen, disabled, testid }) => {
|
|
@@ -1131,7 +1131,9 @@ var TransactionUpdateFC = ({ serialNumber, transactionUpdate }) => {
|
|
|
1131
1131
|
] }),
|
|
1132
1132
|
/* @__PURE__ */ jsxs("section", { className: "transaction_impact", children: [
|
|
1133
1133
|
/* @__PURE__ */ jsx("span", { className: "detail", children: "impact: " }),
|
|
1134
|
-
transactionUpdate.updates.filter(
|
|
1134
|
+
transactionUpdate.updates.filter(
|
|
1135
|
+
(token) => token.type !== `molecule_creation` && token.type !== `molecule_disposal` && token.type !== `state_creation` && token.type !== `state_disposal` && !token.key.startsWith(`\u{1F441}\u200D\u{1F5E8}`)
|
|
1136
|
+
).map((update, index) => {
|
|
1135
1137
|
switch (update.type) {
|
|
1136
1138
|
case `atom_update`:
|
|
1137
1139
|
case `selector_update`:
|
|
@@ -1165,7 +1167,7 @@ var TimelineUpdateFC = ({ timelineUpdate, serialNumber }) => {
|
|
|
1165
1167
|
"article",
|
|
1166
1168
|
{
|
|
1167
1169
|
className: "node timeline_update",
|
|
1168
|
-
"data-testid": `timeline-update-${timelineUpdate.key}-${serialNumber}`,
|
|
1170
|
+
"data-testid": `timeline-update-${typeof timelineUpdate.key === `string` ? timelineUpdate.key : stringifyJson(timelineUpdate.key)}-${serialNumber}`,
|
|
1169
1171
|
children: [
|
|
1170
1172
|
/* @__PURE__ */ jsx("header", { children: /* @__PURE__ */ jsxs("h4", { children: [
|
|
1171
1173
|
timelineUpdate.timestamp,
|
|
@@ -1175,7 +1177,9 @@ var TimelineUpdateFC = ({ timelineUpdate, serialNumber }) => {
|
|
|
1175
1177
|
timelineUpdate.key,
|
|
1176
1178
|
")"
|
|
1177
1179
|
] }) }),
|
|
1178
|
-
/* @__PURE__ */ jsx("main", { children: timelineUpdate.type === `transaction_update` ? timelineUpdate.updates.filter(
|
|
1180
|
+
/* @__PURE__ */ jsx("main", { children: timelineUpdate.type === `transaction_update` ? timelineUpdate.updates.filter(
|
|
1181
|
+
(token) => token.type !== `molecule_creation` && token.type !== `molecule_disposal` && token.type !== `state_creation` && token.type !== `state_disposal` && !token.key.startsWith(`\u{1F441}\u200D\u{1F5E8}`)
|
|
1182
|
+
).map((update, index) => {
|
|
1179
1183
|
switch (update.type) {
|
|
1180
1184
|
case `atom_update`:
|
|
1181
1185
|
case `selector_update`:
|
|
@@ -1276,7 +1280,7 @@ var TimelineLog = ({ token, isOpenState, timelineState }) => {
|
|
|
1276
1280
|
] })
|
|
1277
1281
|
] }),
|
|
1278
1282
|
isOpen ? /* @__PURE__ */ jsx("main", { children: timeline.history.map(
|
|
1279
|
-
(update, index) => `
|
|
1283
|
+
(update, index) => update.type !== `molecule_creation` && update.type !== `molecule_disposal` && update.type !== `state_creation` && update.type !== `state_disposal` ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1280
1284
|
index === timeline.at ? /* @__PURE__ */ jsx(YouAreHere, {}) : null,
|
|
1281
1285
|
/* @__PURE__ */ jsx(
|
|
1282
1286
|
article.TimelineUpdate,
|
|
@@ -64,7 +64,10 @@ export const TimelineLog: FC<{
|
|
|
64
64
|
{isOpen ? (
|
|
65
65
|
<main>
|
|
66
66
|
{timeline.history.map((update, index) =>
|
|
67
|
-
`
|
|
67
|
+
update.type !== `molecule_creation` &&
|
|
68
|
+
update.type !== `molecule_disposal` &&
|
|
69
|
+
update.type !== `state_creation` &&
|
|
70
|
+
update.type !== `state_disposal` ? (
|
|
68
71
|
<Fragment key={update.key + index + timeline.at}>
|
|
69
72
|
{index === timeline.at ? <YouAreHere /> : null}
|
|
70
73
|
<article.TimelineUpdate
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
} from "atom.io"
|
|
6
6
|
import type { Func } from "atom.io/internal"
|
|
7
7
|
import { discoverType, prettyJson } from "atom.io/introspection"
|
|
8
|
+
import { stringifyJson } from "atom.io/json"
|
|
8
9
|
import * as React from "react"
|
|
9
10
|
|
|
10
11
|
/* eslint-disable no-console */
|
|
@@ -90,7 +91,14 @@ const TransactionUpdateFC: React.FC<{
|
|
|
90
91
|
<section className="transaction_impact">
|
|
91
92
|
<span className="detail">impact: </span>
|
|
92
93
|
{transactionUpdate.updates
|
|
93
|
-
.filter(
|
|
94
|
+
.filter(
|
|
95
|
+
(token) =>
|
|
96
|
+
token.type !== `molecule_creation` &&
|
|
97
|
+
token.type !== `molecule_disposal` &&
|
|
98
|
+
token.type !== `state_creation` &&
|
|
99
|
+
token.type !== `state_disposal` &&
|
|
100
|
+
!token.key.startsWith(`👁🗨`),
|
|
101
|
+
)
|
|
94
102
|
.map((update, index) => {
|
|
95
103
|
switch (update.type) {
|
|
96
104
|
case `atom_update`:
|
|
@@ -125,7 +133,7 @@ export const TimelineUpdateFC: React.FC<{
|
|
|
125
133
|
return `key` in timelineUpdate ? (
|
|
126
134
|
<article
|
|
127
135
|
className="node timeline_update"
|
|
128
|
-
data-testid={`timeline-update-${timelineUpdate.key}-${serialNumber}`}
|
|
136
|
+
data-testid={`timeline-update-${typeof timelineUpdate.key === `string` ? timelineUpdate.key : stringifyJson(timelineUpdate.key)}-${serialNumber}`}
|
|
129
137
|
>
|
|
130
138
|
<header>
|
|
131
139
|
<h4>
|
|
@@ -136,7 +144,14 @@ export const TimelineUpdateFC: React.FC<{
|
|
|
136
144
|
<main>
|
|
137
145
|
{timelineUpdate.type === `transaction_update` ? (
|
|
138
146
|
timelineUpdate.updates
|
|
139
|
-
.filter(
|
|
147
|
+
.filter(
|
|
148
|
+
(token) =>
|
|
149
|
+
token.type !== `molecule_creation` &&
|
|
150
|
+
token.type !== `molecule_disposal` &&
|
|
151
|
+
token.type !== `state_creation` &&
|
|
152
|
+
token.type !== `state_disposal` &&
|
|
153
|
+
!token.key.startsWith(`👁🗨`),
|
|
154
|
+
)
|
|
140
155
|
.map((update, index) => {
|
|
141
156
|
switch (update.type) {
|
|
142
157
|
case `atom_update`:
|
package/realtime/dist/index.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ type UserInRoomMeta = {
|
|
|
45
45
|
enteredAtEpoch: number;
|
|
46
46
|
};
|
|
47
47
|
declare const DEFAULT_USER_IN_ROOM_META: UserInRoomMeta;
|
|
48
|
-
declare const usersInRooms: atom_io_data.JoinToken<"room", "user", "1:n", UserInRoomMeta>;
|
|
48
|
+
declare const usersInRooms: atom_io_data.JoinToken<"room", string, "user", string, "1:n", UserInRoomMeta>;
|
|
49
49
|
declare const usersInMyRoomView: atom_io.ReadonlySelectorFamilyToken<MutableAtomToken<SetRTX<string>, SetRTXJson<string>>[], string>;
|
|
50
50
|
|
|
51
51
|
export { type ContinuityOptions, type ContinuityToken, DEFAULT_USER_IN_ROOM_META, InvariantMap, type PerspectiveToken, SyncGroup, type UserInRoomMeta, continuity, roomIndex, usersInMyRoomView, usersInRooms, usersInThisRoomIndex };
|
package/realtime/dist/index.js
CHANGED
|
@@ -90,7 +90,9 @@ var usersInRooms = join(
|
|
|
90
90
|
{
|
|
91
91
|
key: `usersInRooms`,
|
|
92
92
|
between: [`room`, `user`],
|
|
93
|
-
cardinality: `1:n
|
|
93
|
+
cardinality: `1:n`,
|
|
94
|
+
isAType: (input) => typeof input === `string`,
|
|
95
|
+
isBType: (input) => typeof input === `string`
|
|
94
96
|
},
|
|
95
97
|
DEFAULT_USER_IN_ROOM_META
|
|
96
98
|
);
|
|
@@ -31,6 +31,8 @@ export const usersInRooms = join(
|
|
|
31
31
|
key: `usersInRooms`,
|
|
32
32
|
between: [`room`, `user`],
|
|
33
33
|
cardinality: `1:n`,
|
|
34
|
+
isAType: (input): input is string => typeof input === `string`,
|
|
35
|
+
isBType: (input): input is string => typeof input === `string`,
|
|
34
36
|
},
|
|
35
37
|
DEFAULT_USER_IN_ROOM_META,
|
|
36
38
|
)
|
|
@@ -2,7 +2,7 @@ import { Subject, Transceiver, Store } from 'atom.io/internal';
|
|
|
2
2
|
import { Json, stringified, JsonIO, Canonical } from 'atom.io/json';
|
|
3
3
|
import { ChildProcessWithoutNullStreams } from 'node:child_process';
|
|
4
4
|
import * as AtomIO from 'atom.io';
|
|
5
|
-
import { TransactionUpdateContent, TransactionUpdate, WritableToken } from 'atom.io';
|
|
5
|
+
import { TransactionUpdateContent, TransactionUpdate, Hierarchy, WritableToken } from 'atom.io';
|
|
6
6
|
import { ContinuityToken, UserInRoomMeta } from 'atom.io/realtime';
|
|
7
7
|
import * as atom_io_data from 'atom.io/data';
|
|
8
8
|
import { Loadable } from 'atom.io/data';
|
|
@@ -112,10 +112,19 @@ declare const actionOcclusionAtoms: AtomIO.RegularAtomFamilyToken<{
|
|
|
112
112
|
}, string>;
|
|
113
113
|
declare const userUnacknowledgedQueues: AtomIO.RegularAtomFamilyToken<Pick<TransactionUpdate<any>, "key" | "epoch" | "id" | "updates" | "output">[], string>;
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
type SocketKey = `socket::${string}`;
|
|
116
|
+
type UserKey = `user::${string}`;
|
|
117
|
+
type RoomKey = `room::${string}`;
|
|
118
|
+
type SocketSystemHierarchy = Hierarchy<[
|
|
119
|
+
{
|
|
120
|
+
above: `root`;
|
|
121
|
+
below: [UserKey, SocketKey, RoomKey];
|
|
122
|
+
}
|
|
123
|
+
]>;
|
|
124
|
+
declare const socketAtoms: AtomIO.RegularAtomFamilyToken<Socket | null, `socket::${string}`>;
|
|
116
125
|
declare const socketIndex: AtomIO.MutableAtomToken<SetRTX<string>, SetRTXJson<string>>;
|
|
117
126
|
declare const userIndex: AtomIO.MutableAtomToken<SetRTX<string>, SetRTXJson<string>>;
|
|
118
|
-
declare const usersOfSockets: atom_io_data.JoinToken<"user", "socket", "1:1", null>;
|
|
127
|
+
declare const usersOfSockets: atom_io_data.JoinToken<"user", `user::${string}`, "socket", `socket::${string}`, "1:1", null>;
|
|
119
128
|
|
|
120
129
|
type StateProvider = ReturnType<typeof realtimeStateProvider>;
|
|
121
130
|
declare function realtimeStateProvider({ socket, store, }: ServerConfig): <J extends Json.Serializable>(token: AtomIO.WritableToken<J>) => () => void;
|
|
@@ -136,4 +145,4 @@ type ServerConfig = {
|
|
|
136
145
|
store?: Store;
|
|
137
146
|
};
|
|
138
147
|
|
|
139
|
-
export { type ActionReceiver, ChildSocket, type CreateRoomIO, CustomSocket, type EventBuffer, type Events, type FamilyProvider, type JoinRoomIO, type LeaveRoomIO, type MutableFamilyProvider, type MutableProvider, ParentSocket, type RealtimeContinuitySynchronizer, type RoomArguments, type ServerConfig, type Socket, type StateProvider, type StateReceiver, type StringifiedEvent, SubjectSocket, actionOcclusionAtoms, createRoomTX, destroyRoomTX, joinRoomTX, leaveRoomTX, realtimeActionReceiver, realtimeAtomFamilyProvider, realtimeContinuitySynchronizer, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, redactTransactionUpdateContent, roomArgumentsAtoms, roomSelectors, socketAtoms, socketIndex, userIndex, userUnacknowledgedQueues, usersOfSockets };
|
|
148
|
+
export { type ActionReceiver, ChildSocket, type CreateRoomIO, CustomSocket, type EventBuffer, type Events, type FamilyProvider, type JoinRoomIO, type LeaveRoomIO, type MutableFamilyProvider, type MutableProvider, ParentSocket, type RealtimeContinuitySynchronizer, type RoomArguments, type RoomKey, type ServerConfig, type Socket, type SocketKey, type SocketSystemHierarchy, type StateProvider, type StateReceiver, type StringifiedEvent, SubjectSocket, type UserKey, actionOcclusionAtoms, createRoomTX, destroyRoomTX, joinRoomTX, leaveRoomTX, realtimeActionReceiver, realtimeAtomFamilyProvider, realtimeContinuitySynchronizer, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, redactTransactionUpdateContent, roomArgumentsAtoms, roomSelectors, socketAtoms, socketIndex, userIndex, userUnacknowledgedQueues, usersOfSockets };
|
|
@@ -468,7 +468,9 @@ var userIndex = atom({
|
|
|
468
468
|
var usersOfSockets = join({
|
|
469
469
|
key: `usersOfSockets`,
|
|
470
470
|
between: [`user`, `socket`],
|
|
471
|
-
cardinality: `1:1
|
|
471
|
+
cardinality: `1:1`,
|
|
472
|
+
isAType: (s) => s.startsWith(`user::`),
|
|
473
|
+
isBType: (s) => s.startsWith(`socket::`)
|
|
472
474
|
});
|
|
473
475
|
|
|
474
476
|
// realtime-server/src/realtime-continuity-synchronizer.ts
|
|
@@ -481,7 +483,7 @@ function realtimeContinuitySynchronizer({
|
|
|
481
483
|
const continuityKey = continuity.key;
|
|
482
484
|
const userKeyState = findRelationsInStore(
|
|
483
485
|
usersOfSockets,
|
|
484
|
-
socket.id
|
|
486
|
+
`socket::${socket.id}`,
|
|
485
487
|
store
|
|
486
488
|
).userKeyOfSocket;
|
|
487
489
|
const userKey = getFromStore(store, userKeyState);
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import type { Json, JsonIO } from "atom.io/json"
|
|
16
16
|
import type { ContinuityToken } from "atom.io/realtime"
|
|
17
17
|
|
|
18
|
-
import type { ServerConfig, Socket } from "."
|
|
18
|
+
import type { ServerConfig, Socket, SocketKey } from "."
|
|
19
19
|
import { socketAtoms, usersOfSockets } from "."
|
|
20
20
|
import {
|
|
21
21
|
redactTransactionUpdateContent,
|
|
@@ -35,7 +35,7 @@ export function realtimeContinuitySynchronizer({
|
|
|
35
35
|
const continuityKey = continuity.key
|
|
36
36
|
const userKeyState = findRelationsInStore(
|
|
37
37
|
usersOfSockets,
|
|
38
|
-
socket.id
|
|
38
|
+
`socket::${socket.id}`,
|
|
39
39
|
store,
|
|
40
40
|
).userKeyOfSocket
|
|
41
41
|
const userKey = getFromStore(store, userKeyState)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Hierarchy } from "atom.io"
|
|
1
2
|
import { atom, atomFamily } from "atom.io"
|
|
2
3
|
import { join } from "atom.io/data"
|
|
3
4
|
import type { SetRTXJson } from "atom.io/transceivers/set-rtx"
|
|
@@ -5,7 +6,20 @@ import { SetRTX } from "atom.io/transceivers/set-rtx"
|
|
|
5
6
|
|
|
6
7
|
import type { Socket } from ".."
|
|
7
8
|
|
|
8
|
-
export
|
|
9
|
+
export type SocketKey = `socket::${string}`
|
|
10
|
+
export type UserKey = `user::${string}`
|
|
11
|
+
export type RoomKey = `room::${string}`
|
|
12
|
+
|
|
13
|
+
export type SocketSystemHierarchy = Hierarchy<
|
|
14
|
+
[
|
|
15
|
+
{
|
|
16
|
+
above: `root`
|
|
17
|
+
below: [UserKey, SocketKey, RoomKey]
|
|
18
|
+
},
|
|
19
|
+
]
|
|
20
|
+
>
|
|
21
|
+
|
|
22
|
+
export const socketAtoms = atomFamily<Socket | null, SocketKey>({
|
|
9
23
|
key: `sockets`,
|
|
10
24
|
default: null,
|
|
11
25
|
})
|
|
@@ -28,4 +42,6 @@ export const usersOfSockets = join({
|
|
|
28
42
|
key: `usersOfSockets`,
|
|
29
43
|
between: [`user`, `socket`],
|
|
30
44
|
cardinality: `1:1`,
|
|
45
|
+
isAType: (s): s is UserKey => s.startsWith(`user::`),
|
|
46
|
+
isBType: (s): s is SocketKey => s.startsWith(`socket::`),
|
|
31
47
|
})
|
|
@@ -2,6 +2,7 @@ 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';
|
|
5
6
|
import { editRelationsInStore, findRelationsInStore } from 'atom.io/data';
|
|
6
7
|
import { IMPLICIT, findInStore, setIntoStore, getFromStore, clearStore } from 'atom.io/internal';
|
|
7
8
|
import { toEntries } from 'atom.io/json';
|
|
@@ -32,9 +33,13 @@ function prefixLogger(store, prefix) {
|
|
|
32
33
|
var setupRealtimeTestServer = (options) => {
|
|
33
34
|
++testNumber;
|
|
34
35
|
const silo = new AtomIO.Silo(
|
|
35
|
-
{
|
|
36
|
+
{
|
|
37
|
+
name: `SERVER-${testNumber}`,
|
|
38
|
+
lifespan: options.immortal?.server ? `immortal` : `ephemeral`
|
|
39
|
+
},
|
|
36
40
|
IMPLICIT.STORE
|
|
37
41
|
);
|
|
42
|
+
const socketRealm = realm(silo.store);
|
|
38
43
|
const httpServer = http.createServer((_, res) => res.end(`Hello World!`));
|
|
39
44
|
const address = httpServer.listen(options.port).address();
|
|
40
45
|
const port = typeof address === `string` ? null : address === null ? null : address.port;
|
|
@@ -42,12 +47,14 @@ var setupRealtimeTestServer = (options) => {
|
|
|
42
47
|
const server = new SocketIO.Server(httpServer).use((socket, next) => {
|
|
43
48
|
const { token, username } = socket.handshake.auth;
|
|
44
49
|
if (token === `test` && socket.id) {
|
|
45
|
-
const
|
|
50
|
+
const userClaim = socketRealm.allocate(`root`, `user::${username}`);
|
|
51
|
+
const socketClaim = socketRealm.allocate(`root`, `socket::${socket.id}`);
|
|
52
|
+
const socketState = findInStore(silo.store, RTS.socketAtoms, socketClaim);
|
|
46
53
|
setIntoStore(silo.store, socketState, socket);
|
|
47
54
|
editRelationsInStore(
|
|
48
55
|
RTS.usersOfSockets,
|
|
49
56
|
(relations) => {
|
|
50
|
-
relations.set(
|
|
57
|
+
relations.set(userClaim, socketClaim);
|
|
51
58
|
},
|
|
52
59
|
silo.store
|
|
53
60
|
);
|
|
@@ -64,7 +71,7 @@ var setupRealtimeTestServer = (options) => {
|
|
|
64
71
|
function enableLogging() {
|
|
65
72
|
const userKeyState = findRelationsInStore(
|
|
66
73
|
RTS.usersOfSockets,
|
|
67
|
-
socket.id
|
|
74
|
+
`socket::${socket.id}`,
|
|
68
75
|
silo.store
|
|
69
76
|
).userKeyOfSocket;
|
|
70
77
|
userKey = getFromStore(silo.store, userKeyState);
|