atom.io 0.27.5 → 0.28.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 +31 -29
- package/data/dist/index.js +16 -17
- package/data/src/join.ts +17 -19
- package/dist/{chunk-6ABWLAGY.js → chunk-6WL4RQMQ.js} +314 -249
- package/dist/chunk-D52JNVER.js +721 -0
- package/dist/chunk-YQ46F5O2.js +95 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/internal/dist/index.d.ts +72 -36
- package/internal/dist/index.js +1 -1
- package/internal/src/atom/dispose-atom.ts +2 -9
- package/internal/src/families/dispose-from-store.ts +29 -18
- package/internal/src/families/find-in-store.ts +17 -7
- package/internal/src/get-state/get-from-store.ts +41 -32
- package/internal/src/ingest-updates/ingest-creation-disposal.ts +10 -1
- package/internal/src/molecule/dispose-molecule.ts +6 -17
- package/internal/src/pretty-print.ts +1 -16
- package/internal/src/selector/dispose-selector.ts +2 -9
- package/internal/src/set-state/set-into-store.ts +17 -19
- package/internal/src/store/circular-buffer.ts +34 -0
- package/internal/src/store/counterfeit.ts +109 -0
- package/internal/src/store/deposit.ts +14 -0
- package/internal/src/store/index.ts +1 -0
- package/internal/src/store/store.ts +3 -0
- package/internal/src/store/withdraw.ts +15 -10
- package/internal/src/transaction/build-transaction.ts +1 -0
- package/introspection/dist/index.d.ts +84 -4
- package/introspection/dist/index.js +1 -392
- package/introspection/src/attach-introspection-states.ts +7 -4
- package/introspection/src/attach-type-selectors.ts +26 -0
- package/introspection/src/differ.ts +167 -0
- package/introspection/src/index.ts +2 -0
- package/introspection/src/refinery.ts +100 -0
- package/json/dist/index.d.ts +31 -30
- package/json/dist/index.js +2 -77
- package/json/src/entries.ts +6 -0
- package/json/src/index.ts +53 -6
- package/package.json +18 -9
- package/react-devtools/dist/index.d.ts +1 -91
- package/react-devtools/dist/index.js +285 -414
- package/react-devtools/src/AtomIODevtools.tsx +2 -2
- package/react-devtools/src/StateEditor.tsx +20 -12
- package/react-devtools/src/StateIndex.tsx +10 -27
- package/react-devtools/src/TimelineIndex.tsx +3 -3
- package/react-devtools/src/TransactionIndex.tsx +6 -6
- package/react-devtools/src/Updates.tsx +2 -3
- package/react-devtools/src/index.ts +0 -71
- package/react-devtools/src/store.ts +51 -0
- package/realtime/dist/index.d.ts +7 -7
- package/realtime/dist/index.js +18 -22
- package/realtime/src/realtime-continuity.ts +30 -37
- package/realtime-client/dist/index.js +24 -10
- package/realtime-client/src/pull-atom-family-member.ts +2 -0
- package/realtime-client/src/pull-mutable-atom-family-member.ts +2 -0
- package/realtime-client/src/pull-selector-family-member.ts +2 -0
- package/realtime-client/src/realtime-client-stores/client-main-store.ts +6 -6
- package/realtime-client/src/sync-continuity.ts +28 -6
- package/realtime-server/dist/index.js +41 -5
- package/realtime-server/src/ipc-sockets/child-socket.ts +2 -0
- package/realtime-server/src/realtime-continuity-synchronizer.ts +42 -78
- package/realtime-testing/dist/index.d.ts +2 -0
- package/realtime-testing/dist/index.js +50 -8
- package/realtime-testing/src/setup-realtime-test.tsx +61 -9
- package/src/logger.ts +1 -0
- package/src/silo.ts +7 -3
- package/transceivers/set-rtx/src/set-rtx.ts +1 -0
- package/web/dist/index.d.ts +9 -0
- package/{dist/chunk-H6EDLPKH.js → web/dist/index.js} +5 -4
- package/web/package.json +13 -0
- package/web/src/index.ts +1 -0
- package/web/src/persist-sync.ts +25 -0
- package/dist/chunk-AK23DRMD.js +0 -21
- package/dist/chunk-IW6WYRS7.js +0 -140
|
@@ -7,13 +7,15 @@ import {
|
|
|
7
7
|
getEpochNumberOfContinuity,
|
|
8
8
|
getFromStore,
|
|
9
9
|
getJsonToken,
|
|
10
|
+
growMoleculeInStore,
|
|
10
11
|
ingestTransactionUpdate,
|
|
12
|
+
initFamilyMemberInStore,
|
|
11
13
|
isRootStore,
|
|
12
14
|
setEpochNumberOfContinuity,
|
|
13
15
|
setIntoStore,
|
|
14
16
|
subscribeToTransaction,
|
|
15
17
|
} from "atom.io/internal"
|
|
16
|
-
import type
|
|
18
|
+
import { type Json, parseJson } from "atom.io/json"
|
|
17
19
|
import type { ContinuityToken } from "atom.io/realtime"
|
|
18
20
|
import {
|
|
19
21
|
confirmedUpdateQueue,
|
|
@@ -33,8 +35,8 @@ export function syncContinuity<F extends Func>(
|
|
|
33
35
|
const initializeContinuity = (epoch: number, payload: Json.Array) => {
|
|
34
36
|
socket.off(`continuity-init:${continuityKey}`, initializeContinuity)
|
|
35
37
|
let i = 0
|
|
36
|
-
let k: any
|
|
37
|
-
let v: any
|
|
38
|
+
let k: any
|
|
39
|
+
let v: any
|
|
38
40
|
for (const x of payload) {
|
|
39
41
|
if (i % 2 === 0) {
|
|
40
42
|
k = x
|
|
@@ -316,14 +318,14 @@ export function syncContinuity<F extends Func>(
|
|
|
316
318
|
|
|
317
319
|
socket.on(`reveal:${continuityKey}`, (revealed: Json.Array) => {
|
|
318
320
|
let i = 0
|
|
319
|
-
let k: any
|
|
320
|
-
let v: any
|
|
321
|
+
let k: any
|
|
322
|
+
let v: any
|
|
321
323
|
for (const x of revealed) {
|
|
322
324
|
if (i % 2 === 0) {
|
|
323
325
|
k = x
|
|
324
326
|
} else {
|
|
325
327
|
v = x
|
|
326
|
-
|
|
328
|
+
upsertState(k, v, store)
|
|
327
329
|
}
|
|
328
330
|
i++
|
|
329
331
|
}
|
|
@@ -345,3 +347,23 @@ export function syncContinuity<F extends Func>(
|
|
|
345
347
|
// socket.emit(`unsub:${continuityKey}`)
|
|
346
348
|
}
|
|
347
349
|
}
|
|
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
|
+
}
|
|
@@ -510,17 +510,23 @@ function realtimeContinuitySynchronizer({
|
|
|
510
510
|
`seeing ${userKey} on new socket ${newSocketKey}`
|
|
511
511
|
);
|
|
512
512
|
if (newSocketKey === null) {
|
|
513
|
-
store.logger.
|
|
513
|
+
store.logger.warn(
|
|
514
514
|
`\u274C`,
|
|
515
515
|
`continuity`,
|
|
516
516
|
continuityKey,
|
|
517
|
-
`
|
|
517
|
+
`User (${userKey}) is not connected to a socket, waiting for them to reappear.`
|
|
518
518
|
);
|
|
519
519
|
return;
|
|
520
520
|
}
|
|
521
521
|
const newSocketState = findInStore(store, socketAtoms, newSocketKey);
|
|
522
522
|
const newSocket = getFromStore(store, newSocketState);
|
|
523
523
|
socket = newSocket;
|
|
524
|
+
for (const unacknowledgedUpdate of userUnacknowledgedUpdates) {
|
|
525
|
+
socket?.emit(
|
|
526
|
+
`tx-new:${continuityKey}`,
|
|
527
|
+
unacknowledgedUpdate
|
|
528
|
+
);
|
|
529
|
+
}
|
|
524
530
|
},
|
|
525
531
|
`sync-continuity:${continuityKey}:${userKey}`,
|
|
526
532
|
store
|
|
@@ -530,7 +536,7 @@ function realtimeContinuitySynchronizer({
|
|
|
530
536
|
userUnacknowledgedQueues,
|
|
531
537
|
userKey
|
|
532
538
|
);
|
|
533
|
-
getFromStore(
|
|
539
|
+
const userUnacknowledgedUpdates = getFromStore(
|
|
534
540
|
store,
|
|
535
541
|
userUnacknowledgedQueue
|
|
536
542
|
);
|
|
@@ -581,7 +587,8 @@ function realtimeContinuitySynchronizer({
|
|
|
581
587
|
const initialPayload = [];
|
|
582
588
|
for (const atom2 of continuity.globals) {
|
|
583
589
|
const resourceToken = atom2.type === `mutable_atom` ? getJsonToken(store, atom2) : atom2;
|
|
584
|
-
|
|
590
|
+
const resource = getFromStore(store, resourceToken);
|
|
591
|
+
initialPayload.push(resourceToken, resource);
|
|
585
592
|
}
|
|
586
593
|
for (const perspective of continuity.perspectives) {
|
|
587
594
|
const { viewAtoms, resourceAtoms } = perspective;
|
|
@@ -605,7 +612,12 @@ function realtimeContinuitySynchronizer({
|
|
|
605
612
|
transaction2,
|
|
606
613
|
(update) => {
|
|
607
614
|
try {
|
|
608
|
-
const visibleKeys = continuity.globals.map((atom2) =>
|
|
615
|
+
const visibleKeys = continuity.globals.map((atom2) => {
|
|
616
|
+
if (atom2.type === `atom`) {
|
|
617
|
+
return atom2.key;
|
|
618
|
+
}
|
|
619
|
+
return getUpdateToken(atom2).key;
|
|
620
|
+
}).concat(
|
|
609
621
|
continuity.perspectives.flatMap((perspective) => {
|
|
610
622
|
const { viewAtoms } = perspective;
|
|
611
623
|
const userPerspectiveTokenState = findInStore(
|
|
@@ -631,6 +643,13 @@ function realtimeContinuitySynchronizer({
|
|
|
631
643
|
...update,
|
|
632
644
|
updates: redactedUpdates
|
|
633
645
|
};
|
|
646
|
+
setIntoStore(store, userUnacknowledgedQueue, (updates) => {
|
|
647
|
+
if (redactedUpdate) {
|
|
648
|
+
updates.push(redactedUpdate);
|
|
649
|
+
updates.sort((a, b) => a.epoch - b.epoch);
|
|
650
|
+
}
|
|
651
|
+
return updates;
|
|
652
|
+
});
|
|
634
653
|
socket?.emit(
|
|
635
654
|
`tx-new:${continuityKey}`,
|
|
636
655
|
redactedUpdate
|
|
@@ -710,8 +729,25 @@ function realtimeContinuitySynchronizer({
|
|
|
710
729
|
};
|
|
711
730
|
socket.off(`tx-run:${continuityKey}`, fillTransactionRequest);
|
|
712
731
|
socket.on(`tx-run:${continuityKey}`, fillTransactionRequest);
|
|
732
|
+
const trackClientAcknowledgement = (epoch) => {
|
|
733
|
+
store.logger.info(
|
|
734
|
+
`\u{1F44D}`,
|
|
735
|
+
`continuity`,
|
|
736
|
+
continuityKey,
|
|
737
|
+
`${userKey} acknowledged epoch ${epoch}`
|
|
738
|
+
);
|
|
739
|
+
const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch;
|
|
740
|
+
if (isUnacknowledged) {
|
|
741
|
+
setIntoStore(store, userUnacknowledgedQueue, (updates) => {
|
|
742
|
+
updates.shift();
|
|
743
|
+
return updates;
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
socket?.on(`ack:${continuityKey}`, trackClientAcknowledgement);
|
|
713
748
|
return () => {
|
|
714
749
|
for (const unsubscribe of unsubscribeFunctions) unsubscribe();
|
|
750
|
+
socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement);
|
|
715
751
|
unsubscribeFromPerspectives();
|
|
716
752
|
socket?.off(`get:${continuityKey}`, sendInitialPayload);
|
|
717
753
|
socket?.off(`tx-run:${continuityKey}`, fillTransactionRequest);
|
|
@@ -5,8 +5,10 @@ import {
|
|
|
5
5
|
findInStore,
|
|
6
6
|
getFromStore,
|
|
7
7
|
getJsonToken,
|
|
8
|
+
getUpdateToken,
|
|
8
9
|
IMPLICIT,
|
|
9
10
|
isRootStore,
|
|
11
|
+
setIntoStore,
|
|
10
12
|
subscribeToState,
|
|
11
13
|
subscribeToTransaction,
|
|
12
14
|
} from "atom.io/internal"
|
|
@@ -62,17 +64,23 @@ export function realtimeContinuitySynchronizer({
|
|
|
62
64
|
`seeing ${userKey} on new socket ${newSocketKey}`,
|
|
63
65
|
)
|
|
64
66
|
if (newSocketKey === null) {
|
|
65
|
-
store.logger.
|
|
67
|
+
store.logger.warn(
|
|
66
68
|
`❌`,
|
|
67
69
|
`continuity`,
|
|
68
70
|
continuityKey,
|
|
69
|
-
`
|
|
71
|
+
`User (${userKey}) is not connected to a socket, waiting for them to reappear.`,
|
|
70
72
|
)
|
|
71
73
|
return
|
|
72
74
|
}
|
|
73
75
|
const newSocketState = findInStore(store, socketAtoms, newSocketKey)
|
|
74
76
|
const newSocket = getFromStore(store, newSocketState)
|
|
75
77
|
socket = newSocket
|
|
78
|
+
for (const unacknowledgedUpdate of userUnacknowledgedUpdates) {
|
|
79
|
+
socket?.emit(
|
|
80
|
+
`tx-new:${continuityKey}`,
|
|
81
|
+
unacknowledgedUpdate as Json.Serializable,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
76
84
|
},
|
|
77
85
|
`sync-continuity:${continuityKey}:${userKey}`,
|
|
78
86
|
store,
|
|
@@ -142,7 +150,8 @@ export function realtimeContinuitySynchronizer({
|
|
|
142
150
|
for (const atom of continuity.globals) {
|
|
143
151
|
const resourceToken =
|
|
144
152
|
atom.type === `mutable_atom` ? getJsonToken(store, atom) : atom
|
|
145
|
-
|
|
153
|
+
const resource = getFromStore(store, resourceToken)
|
|
154
|
+
initialPayload.push(resourceToken, resource)
|
|
146
155
|
}
|
|
147
156
|
for (const perspective of continuity.perspectives) {
|
|
148
157
|
const { viewAtoms, resourceAtoms } = perspective
|
|
@@ -176,7 +185,12 @@ export function realtimeContinuitySynchronizer({
|
|
|
176
185
|
(update) => {
|
|
177
186
|
try {
|
|
178
187
|
const visibleKeys = continuity.globals
|
|
179
|
-
.map((atom) =>
|
|
188
|
+
.map((atom) => {
|
|
189
|
+
if (atom.type === `atom`) {
|
|
190
|
+
return atom.key
|
|
191
|
+
}
|
|
192
|
+
return getUpdateToken(atom).key
|
|
193
|
+
})
|
|
180
194
|
.concat(
|
|
181
195
|
continuity.perspectives.flatMap((perspective) => {
|
|
182
196
|
const { viewAtoms } = perspective
|
|
@@ -206,17 +220,13 @@ export function realtimeContinuitySynchronizer({
|
|
|
206
220
|
...update,
|
|
207
221
|
updates: redactedUpdates,
|
|
208
222
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// return updates
|
|
217
|
-
// },
|
|
218
|
-
// store,
|
|
219
|
-
// )
|
|
223
|
+
setIntoStore(store, userUnacknowledgedQueue, (updates) => {
|
|
224
|
+
if (redactedUpdate) {
|
|
225
|
+
updates.push(redactedUpdate)
|
|
226
|
+
updates.sort((a, b) => a.epoch - b.epoch)
|
|
227
|
+
}
|
|
228
|
+
return updates
|
|
229
|
+
})
|
|
220
230
|
|
|
221
231
|
socket?.emit(
|
|
222
232
|
`tx-new:${continuityKey}`,
|
|
@@ -304,73 +314,27 @@ export function realtimeContinuitySynchronizer({
|
|
|
304
314
|
socket.off(`tx-run:${continuityKey}`, fillTransactionRequest)
|
|
305
315
|
socket.on(`tx-run:${continuityKey}`, fillTransactionRequest)
|
|
306
316
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
// return updates
|
|
324
|
-
// },
|
|
325
|
-
// store,
|
|
326
|
-
// )
|
|
327
|
-
// }
|
|
328
|
-
// }
|
|
329
|
-
// subscribeToState(
|
|
330
|
-
// userUnacknowledgedQueue,
|
|
331
|
-
// ({ newValue }) => {
|
|
332
|
-
// if (newValue.length === 0) {
|
|
333
|
-
// clearInterval(retryTimeout)
|
|
334
|
-
// socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement)
|
|
335
|
-
// retryTimeout = undefined
|
|
336
|
-
// }
|
|
337
|
-
// if (newValue.length > 0) {
|
|
338
|
-
// if (retryTimeout) {
|
|
339
|
-
// return
|
|
340
|
-
// }
|
|
341
|
-
|
|
342
|
-
// socket?.on(`ack:${continuityKey}`, trackClientAcknowledgement)
|
|
343
|
-
|
|
344
|
-
// retryTimeout = setInterval(() => {
|
|
345
|
-
// i++
|
|
346
|
-
// if (i === n) {
|
|
347
|
-
// n += i
|
|
348
|
-
// const toEmit = newValue[0]
|
|
349
|
-
// if (!toEmit) return
|
|
350
|
-
// store.logger.info(
|
|
351
|
-
// `🔄`,
|
|
352
|
-
// `continuity`,
|
|
353
|
-
// continuityKey,
|
|
354
|
-
// `${store.config.name} retrying ${userKey}`,
|
|
355
|
-
// socket?.id,
|
|
356
|
-
// newValue,
|
|
357
|
-
// )
|
|
358
|
-
// socket?.emit(
|
|
359
|
-
// `tx-new:${continuityKey}`,
|
|
360
|
-
// toEmit as Json.Serializable,
|
|
361
|
-
// )
|
|
362
|
-
// }
|
|
363
|
-
// }, 250)
|
|
364
|
-
// }
|
|
365
|
-
// },
|
|
366
|
-
// `sync-continuity:${continuityKey}:${userKey}`,
|
|
367
|
-
// store,
|
|
368
|
-
// )
|
|
317
|
+
const trackClientAcknowledgement = (epoch: number) => {
|
|
318
|
+
store.logger.info(
|
|
319
|
+
`👍`,
|
|
320
|
+
`continuity`,
|
|
321
|
+
continuityKey,
|
|
322
|
+
`${userKey} acknowledged epoch ${epoch}`,
|
|
323
|
+
)
|
|
324
|
+
const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch
|
|
325
|
+
if (isUnacknowledged) {
|
|
326
|
+
setIntoStore(store, userUnacknowledgedQueue, (updates) => {
|
|
327
|
+
updates.shift()
|
|
328
|
+
return updates
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
socket?.on(`ack:${continuityKey}`, trackClientAcknowledgement)
|
|
369
333
|
|
|
370
334
|
return () => {
|
|
371
335
|
// clearInterval(retryTimeout)
|
|
372
336
|
for (const unsubscribe of unsubscribeFunctions) unsubscribe()
|
|
373
|
-
|
|
337
|
+
socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement)
|
|
374
338
|
unsubscribeFromPerspectives()
|
|
375
339
|
socket?.off(`get:${continuityKey}`, sendInitialPayload)
|
|
376
340
|
socket?.off(`tx-run:${continuityKey}`, fillTransactionRequest)
|
|
@@ -9,6 +9,7 @@ type TestSetupOptions = {
|
|
|
9
9
|
server: (tools: {
|
|
10
10
|
socket: SocketIO.Socket;
|
|
11
11
|
silo: AtomIO.Silo;
|
|
12
|
+
enableLogging: () => void;
|
|
12
13
|
}) => void;
|
|
13
14
|
};
|
|
14
15
|
type TestSetupOptions__SingleClient = TestSetupOptions & {
|
|
@@ -26,6 +27,7 @@ type RealtimeTestTools = {
|
|
|
26
27
|
type RealtimeTestClient = RealtimeTestTools & {
|
|
27
28
|
renderResult: RenderResult;
|
|
28
29
|
prettyPrint: () => void;
|
|
30
|
+
enableLogging: () => void;
|
|
29
31
|
socket: Socket;
|
|
30
32
|
};
|
|
31
33
|
type RealtimeTestClientBuilder = {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { recordToEntries } from '../../dist/chunk-IW6WYRS7.js';
|
|
2
1
|
import '../../dist/chunk-XWL6SNVU.js';
|
|
3
2
|
import * as http from 'http';
|
|
4
3
|
import { render, prettyDOM } from '@testing-library/react';
|
|
5
4
|
import * as AtomIO from 'atom.io';
|
|
6
|
-
import { editRelationsInStore } from 'atom.io/data';
|
|
5
|
+
import { editRelationsInStore, findRelationsInStore } from 'atom.io/data';
|
|
7
6
|
import { IMPLICIT, findInStore, setIntoStore, getFromStore, clearStore } from 'atom.io/internal';
|
|
7
|
+
import { toEntries } from 'atom.io/json';
|
|
8
8
|
import * as AR from 'atom.io/react';
|
|
9
9
|
import * as RT from 'atom.io/realtime';
|
|
10
|
-
import
|
|
10
|
+
import * as RTC from 'atom.io/realtime-client';
|
|
11
11
|
import * as RTR from 'atom.io/realtime-react';
|
|
12
12
|
import * as RTS from 'atom.io/realtime-server';
|
|
13
13
|
import * as Happy from 'happy-dom';
|
|
@@ -16,6 +16,19 @@ import { io } from 'socket.io-client';
|
|
|
16
16
|
import { jsx } from 'react/jsx-runtime';
|
|
17
17
|
|
|
18
18
|
var testNumber = 0;
|
|
19
|
+
function prefixLogger(store, prefix) {
|
|
20
|
+
store.loggers[0] = new AtomIO.AtomIOLogger(`info`, void 0, {
|
|
21
|
+
info: (...args) => {
|
|
22
|
+
console.info(prefix, ...args);
|
|
23
|
+
},
|
|
24
|
+
warn: (...args) => {
|
|
25
|
+
console.warn(prefix, ...args);
|
|
26
|
+
},
|
|
27
|
+
error: (...args) => {
|
|
28
|
+
console.error(prefix, ...args);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
19
32
|
var setupRealtimeTestServer = (options) => {
|
|
20
33
|
++testNumber;
|
|
21
34
|
const silo = new AtomIO.Silo(
|
|
@@ -47,7 +60,26 @@ var setupRealtimeTestServer = (options) => {
|
|
|
47
60
|
}
|
|
48
61
|
});
|
|
49
62
|
server.on(`connection`, (socket) => {
|
|
50
|
-
|
|
63
|
+
let userKey = null;
|
|
64
|
+
function enableLogging() {
|
|
65
|
+
const userKeyState = findRelationsInStore(
|
|
66
|
+
RTS.usersOfSockets,
|
|
67
|
+
socket.id,
|
|
68
|
+
silo.store
|
|
69
|
+
).userKeyOfSocket;
|
|
70
|
+
userKey = getFromStore(silo.store, userKeyState);
|
|
71
|
+
prefixLogger(silo.store, `server`);
|
|
72
|
+
socket.onAny((event, ...args) => {
|
|
73
|
+
console.log(`\u{1F6F0} `, userKey, event, ...args);
|
|
74
|
+
});
|
|
75
|
+
socket.onAnyOutgoing((event, ...args) => {
|
|
76
|
+
console.log(`\u{1F6F0} >>`, userKey, event, ...args);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
options.server({ socket, enableLogging, silo });
|
|
80
|
+
socket.on(`disconnect`, () => {
|
|
81
|
+
console.log(`${userKey} disconnected`);
|
|
82
|
+
});
|
|
51
83
|
});
|
|
52
84
|
const dispose = () => {
|
|
53
85
|
server.close();
|
|
@@ -81,7 +113,7 @@ var setupRealtimeTestClient = (options, name, port) => {
|
|
|
81
113
|
silo.store.valueMap.set(key, [...value]);
|
|
82
114
|
}
|
|
83
115
|
}
|
|
84
|
-
silo.setState(myUsernameState, `${name}-${testNumber}`);
|
|
116
|
+
silo.setState(RTC.myUsernameState, `${name}-${testNumber}`);
|
|
85
117
|
const { document } = new Happy.Window();
|
|
86
118
|
document.body.innerHTML = `<div id="app"></div>`;
|
|
87
119
|
const renderResult = render(
|
|
@@ -93,6 +125,15 @@ var setupRealtimeTestClient = (options, name, port) => {
|
|
|
93
125
|
const prettyPrint = () => {
|
|
94
126
|
console.log(prettyDOM(renderResult.container));
|
|
95
127
|
};
|
|
128
|
+
const enableLogging = () => {
|
|
129
|
+
prefixLogger(silo.store, name);
|
|
130
|
+
socket.onAny((event, ...args) => {
|
|
131
|
+
console.log(`\u{1F4E1} `, name, event, ...args);
|
|
132
|
+
});
|
|
133
|
+
socket.onAnyOutgoing((event, ...args) => {
|
|
134
|
+
console.log(`\u{1F4E1} >>`, name, event, ...args);
|
|
135
|
+
});
|
|
136
|
+
};
|
|
96
137
|
const dispose = () => {
|
|
97
138
|
renderResult.unmount();
|
|
98
139
|
socket.disconnect();
|
|
@@ -104,7 +145,8 @@ var setupRealtimeTestClient = (options, name, port) => {
|
|
|
104
145
|
silo,
|
|
105
146
|
socket,
|
|
106
147
|
renderResult,
|
|
107
|
-
prettyPrint
|
|
148
|
+
prettyPrint,
|
|
149
|
+
enableLogging
|
|
108
150
|
};
|
|
109
151
|
};
|
|
110
152
|
return Object.assign(testClient, { init });
|
|
@@ -123,7 +165,7 @@ var singleClient = (options) => {
|
|
|
123
165
|
};
|
|
124
166
|
var multiClient = (options) => {
|
|
125
167
|
const server = setupRealtimeTestServer(options);
|
|
126
|
-
const clients =
|
|
168
|
+
const clients = toEntries(options.clients).reduce(
|
|
127
169
|
(clientRecord, [name, client]) => {
|
|
128
170
|
clientRecord[name] = setupRealtimeTestClient(
|
|
129
171
|
{ ...options, client },
|
|
@@ -139,7 +181,7 @@ var multiClient = (options) => {
|
|
|
139
181
|
server,
|
|
140
182
|
teardown: () => {
|
|
141
183
|
server.dispose();
|
|
142
|
-
for (const [, client] of
|
|
184
|
+
for (const [, client] of toEntries(clients)) {
|
|
143
185
|
client.dispose();
|
|
144
186
|
}
|
|
145
187
|
}
|
|
@@ -3,7 +3,8 @@ 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 { editRelationsInStore } from "atom.io/data"
|
|
6
|
+
import { editRelationsInStore, findRelationsInStore } from "atom.io/data"
|
|
7
|
+
import type { Store } from "atom.io/internal"
|
|
7
8
|
import {
|
|
8
9
|
clearStore,
|
|
9
10
|
findInStore,
|
|
@@ -11,9 +12,10 @@ import {
|
|
|
11
12
|
IMPLICIT,
|
|
12
13
|
setIntoStore,
|
|
13
14
|
} from "atom.io/internal"
|
|
15
|
+
import { toEntries } from "atom.io/json"
|
|
14
16
|
import * as AR from "atom.io/react"
|
|
15
17
|
import * as RT from "atom.io/realtime"
|
|
16
|
-
import
|
|
18
|
+
import * as RTC from "atom.io/realtime-client"
|
|
17
19
|
import * as RTR from "atom.io/realtime-react"
|
|
18
20
|
import * as RTS from "atom.io/realtime-server"
|
|
19
21
|
import * as Happy from "happy-dom"
|
|
@@ -22,13 +24,31 @@ import * as SocketIO from "socket.io"
|
|
|
22
24
|
import type { Socket as ClientSocket } from "socket.io-client"
|
|
23
25
|
import { io } from "socket.io-client"
|
|
24
26
|
|
|
25
|
-
import { recordToEntries } from "~/packages/anvl/src/object"
|
|
26
|
-
|
|
27
27
|
let testNumber = 0
|
|
28
28
|
|
|
29
|
+
/* eslint-disable no-console */
|
|
30
|
+
|
|
31
|
+
function prefixLogger(store: Store, prefix: string) {
|
|
32
|
+
store.loggers[0] = new AtomIO.AtomIOLogger(`info`, undefined, {
|
|
33
|
+
info: (...args) => {
|
|
34
|
+
console.info(prefix, ...args)
|
|
35
|
+
},
|
|
36
|
+
warn: (...args) => {
|
|
37
|
+
console.warn(prefix, ...args)
|
|
38
|
+
},
|
|
39
|
+
error: (...args) => {
|
|
40
|
+
console.error(prefix, ...args)
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
29
45
|
export type TestSetupOptions = {
|
|
30
46
|
port: number
|
|
31
|
-
server: (tools: {
|
|
47
|
+
server: (tools: {
|
|
48
|
+
socket: SocketIO.Socket
|
|
49
|
+
silo: AtomIO.Silo
|
|
50
|
+
enableLogging: () => void
|
|
51
|
+
}) => void
|
|
32
52
|
}
|
|
33
53
|
export type TestSetupOptions__SingleClient = TestSetupOptions & {
|
|
34
54
|
client: React.FC
|
|
@@ -47,6 +67,7 @@ export type RealtimeTestTools = {
|
|
|
47
67
|
export type RealtimeTestClient = RealtimeTestTools & {
|
|
48
68
|
renderResult: RenderResult
|
|
49
69
|
prettyPrint: () => void
|
|
70
|
+
enableLogging: () => void
|
|
50
71
|
socket: ClientSocket
|
|
51
72
|
}
|
|
52
73
|
export type RealtimeTestClientBuilder = {
|
|
@@ -57,6 +78,7 @@ export type RealtimeTestClientBuilder = {
|
|
|
57
78
|
export type RealtimeTestServer = RealtimeTestTools & {
|
|
58
79
|
dispose: () => void
|
|
59
80
|
port: number
|
|
81
|
+
// enableLogging: () => void
|
|
60
82
|
}
|
|
61
83
|
|
|
62
84
|
export type RealtimeTestAPI = {
|
|
@@ -108,7 +130,26 @@ export const setupRealtimeTestServer = (
|
|
|
108
130
|
})
|
|
109
131
|
|
|
110
132
|
server.on(`connection`, (socket: SocketIO.Socket) => {
|
|
111
|
-
|
|
133
|
+
let userKey: string | null = null
|
|
134
|
+
function enableLogging() {
|
|
135
|
+
const userKeyState = findRelationsInStore(
|
|
136
|
+
RTS.usersOfSockets,
|
|
137
|
+
socket.id,
|
|
138
|
+
silo.store,
|
|
139
|
+
).userKeyOfSocket
|
|
140
|
+
userKey = getFromStore(silo.store, userKeyState)
|
|
141
|
+
prefixLogger(silo.store, `server`)
|
|
142
|
+
socket.onAny((event, ...args) => {
|
|
143
|
+
console.log(`🛰 `, userKey, event, ...args)
|
|
144
|
+
})
|
|
145
|
+
socket.onAnyOutgoing((event, ...args) => {
|
|
146
|
+
console.log(`🛰 >>`, userKey, event, ...args)
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
options.server({ socket, enableLogging, silo })
|
|
150
|
+
socket.on(`disconnect`, () => {
|
|
151
|
+
console.log(`${userKey} disconnected`)
|
|
152
|
+
})
|
|
112
153
|
})
|
|
113
154
|
|
|
114
155
|
const dispose = () => {
|
|
@@ -147,7 +188,7 @@ export const setupRealtimeTestClient = (
|
|
|
147
188
|
silo.store.valueMap.set(key, [...value])
|
|
148
189
|
}
|
|
149
190
|
}
|
|
150
|
-
silo.setState(myUsernameState, `${name}-${testNumber}`)
|
|
191
|
+
silo.setState(RTC.myUsernameState, `${name}-${testNumber}`)
|
|
151
192
|
|
|
152
193
|
const { document } = new Happy.Window()
|
|
153
194
|
document.body.innerHTML = `<div id="app"></div>`
|
|
@@ -166,6 +207,16 @@ export const setupRealtimeTestClient = (
|
|
|
166
207
|
console.log(prettyDOM(renderResult.container))
|
|
167
208
|
}
|
|
168
209
|
|
|
210
|
+
const enableLogging = () => {
|
|
211
|
+
prefixLogger(silo.store, name)
|
|
212
|
+
socket.onAny((event, ...args) => {
|
|
213
|
+
console.log(`📡 `, name, event, ...args)
|
|
214
|
+
})
|
|
215
|
+
socket.onAnyOutgoing((event, ...args) => {
|
|
216
|
+
console.log(`📡 >>`, name, event, ...args)
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
169
220
|
const dispose = () => {
|
|
170
221
|
renderResult.unmount()
|
|
171
222
|
socket.disconnect()
|
|
@@ -179,6 +230,7 @@ export const setupRealtimeTestClient = (
|
|
|
179
230
|
socket,
|
|
180
231
|
renderResult,
|
|
181
232
|
prettyPrint,
|
|
233
|
+
enableLogging,
|
|
182
234
|
}
|
|
183
235
|
}
|
|
184
236
|
return Object.assign(testClient, { init })
|
|
@@ -204,7 +256,7 @@ export const multiClient = <ClientNames extends string>(
|
|
|
204
256
|
options: TestSetupOptions__MultiClient<ClientNames>,
|
|
205
257
|
): RealtimeTestAPI__MultiClient<ClientNames> => {
|
|
206
258
|
const server = setupRealtimeTestServer(options)
|
|
207
|
-
const clients =
|
|
259
|
+
const clients = toEntries(options.clients).reduce(
|
|
208
260
|
(clientRecord, [name, client]) => {
|
|
209
261
|
clientRecord[name] = setupRealtimeTestClient(
|
|
210
262
|
{ ...options, client },
|
|
@@ -221,7 +273,7 @@ export const multiClient = <ClientNames extends string>(
|
|
|
221
273
|
server,
|
|
222
274
|
teardown: () => {
|
|
223
275
|
server.dispose()
|
|
224
|
-
for (const [, client] of
|
|
276
|
+
for (const [, client] of toEntries(clients)) {
|
|
225
277
|
client.dispose()
|
|
226
278
|
}
|
|
227
279
|
},
|
package/src/logger.ts
CHANGED
|
@@ -85,6 +85,7 @@ export type Logger = Record<LogLevel, LogFn>
|
|
|
85
85
|
export const simpleLog =
|
|
86
86
|
(logLevel: keyof Logger): LogFn =>
|
|
87
87
|
(icon, denomination, tokenKey, message, ...rest) => {
|
|
88
|
+
/* eslint-disable-next-line no-console */
|
|
88
89
|
console[logLevel](
|
|
89
90
|
`${icon} ${denomination} "${tokenKey}" ${message}`,
|
|
90
91
|
...rest,
|
package/src/silo.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { findState } from "atom.io/ephemeral"
|
|
2
2
|
import {
|
|
3
|
+
actUponStore,
|
|
4
|
+
arbitrary,
|
|
3
5
|
createAtomFamily,
|
|
4
6
|
createMoleculeFamily,
|
|
5
7
|
createSelectorFamily,
|
|
@@ -30,7 +32,7 @@ import type {
|
|
|
30
32
|
} from "."
|
|
31
33
|
import type { atom, atomFamily } from "./atom"
|
|
32
34
|
import type { selector, selectorFamily } from "./selector"
|
|
33
|
-
import type { transaction } from "./transaction"
|
|
35
|
+
import type { runTransaction, transaction } from "./transaction"
|
|
34
36
|
|
|
35
37
|
export class Silo {
|
|
36
38
|
public store: Store
|
|
@@ -49,6 +51,7 @@ export class Silo {
|
|
|
49
51
|
public redo: typeof redo
|
|
50
52
|
public moleculeFamily: typeof moleculeFamily
|
|
51
53
|
public makeMolecule: typeof makeMolecule
|
|
54
|
+
public runTransaction: typeof runTransaction
|
|
52
55
|
public constructor(config: Store[`config`], fromStore: Store | null = null) {
|
|
53
56
|
const s = new Store(config, fromStore)
|
|
54
57
|
this.store = s
|
|
@@ -83,8 +86,9 @@ export class Silo {
|
|
|
83
86
|
this.moleculeFamily = ((options: Parameters<typeof moleculeFamily>[0]) => {
|
|
84
87
|
return createMoleculeFamily(s, options)
|
|
85
88
|
}) as typeof moleculeFamily
|
|
86
|
-
this.makeMolecule = (
|
|
89
|
+
this.makeMolecule = (...params: Parameters<typeof makeMolecule>) => {
|
|
87
90
|
return makeMoleculeInStore(s, ...params)
|
|
88
|
-
}
|
|
91
|
+
}
|
|
92
|
+
this.runTransaction = (token, id = arbitrary()) => actUponStore(token, id, s)
|
|
89
93
|
}
|
|
90
94
|
}
|
|
@@ -110,6 +110,7 @@ export class SetRTX<P extends primitive>
|
|
|
110
110
|
this.emit(`tx:${this.transactionUpdates.join(`;`)}`)
|
|
111
111
|
}
|
|
112
112
|
} catch (thrown) {
|
|
113
|
+
/* eslint-disable-next-line no-console */
|
|
113
114
|
console.error(`Failed to apply transaction to SetRTX:`, thrown)
|
|
114
115
|
throw thrown
|
|
115
116
|
} finally {
|