atom.io 0.30.1 → 0.30.3

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.
Files changed (28) hide show
  1. package/data/dist/index.js +18 -16
  2. package/data/src/join.ts +18 -16
  3. package/dist/{chunk-SMKF3ZNG.js → chunk-LSCRHXLI.js} +22 -5
  4. package/internal/dist/index.d.ts +3 -1
  5. package/internal/dist/index.js +1 -1
  6. package/internal/src/junction.ts +23 -7
  7. package/json/dist/index.js +1 -1
  8. package/package.json +22 -22
  9. package/react-devtools/dist/index.js +1 -1
  10. package/react-devtools/src/AtomIODevtools.tsx +1 -1
  11. package/react-devtools/src/elastic-input/NumberInput.tsx +0 -1
  12. package/react-devtools/src/elastic-input/TextInput.tsx +0 -1
  13. package/realtime/dist/index.d.ts +2 -1
  14. package/realtime/src/realtime-continuity.ts +3 -2
  15. package/realtime-server/dist/index.d.ts +11 -10
  16. package/realtime-server/dist/index.js +279 -234
  17. package/realtime-server/src/continuity/prepare-to-send-initial-payload.ts +54 -0
  18. package/realtime-server/src/continuity/prepare-to-serve-transaction-request.ts +53 -0
  19. package/realtime-server/src/continuity/prepare-to-sync-realtime-continuity.ts +145 -0
  20. package/realtime-server/src/continuity/prepare-to-track-client-acknowledgement.ts +38 -0
  21. package/realtime-server/src/continuity/subscribe-to-continuity-actions.ts +106 -0
  22. package/realtime-server/src/continuity/subscribe-to-continuity-perpectives.ts +60 -0
  23. package/realtime-server/src/index.ts +1 -1
  24. package/realtime-server/src/realtime-server-stores/server-sync-store.ts +11 -5
  25. package/realtime-server/src/realtime-server-stores/server-user-store.ts +4 -4
  26. package/realtime-testing/dist/index.js +6 -2
  27. package/realtime-testing/src/setup-realtime-test.tsx +4 -2
  28. package/realtime-server/src/realtime-continuity-synchronizer.ts +0 -343
@@ -1,12 +1,12 @@
1
1
  import '../../dist/chunk-XWL6SNVU.js';
2
- import { parseJson, stringifyJson } from 'atom.io/json';
3
- import { Subject, IMPLICIT, getFromStore, subscribeToState, findInStore, getJsonToken, getUpdateToken, actUponStore, isRootStore, subscribeToTransaction, setIntoStore } from 'atom.io/internal';
4
- import { SetRTX } from 'atom.io/transceivers/set-rtx';
5
2
  import { editRelationsInStore, join, findRelationsInStore } from 'atom.io/data';
3
+ import { Subject, getFromStore, subscribeToState, findInStore, IMPLICIT, getJsonToken, getUpdateToken, isRootStore, actUponStore, setIntoStore, subscribeToTransaction } from 'atom.io/internal';
6
4
  import * as AtomIO from 'atom.io';
7
5
  import { atomFamily, selectorFamily, atom } from 'atom.io';
8
6
  import { roomIndex, usersInRooms } from 'atom.io/realtime';
9
7
  import { spawn } from 'node:child_process';
8
+ import { parseJson, stringifyJson } from 'atom.io/json';
9
+ import { SetRTX } from 'atom.io/transceivers/set-rtx';
10
10
 
11
11
  // realtime-server/src/ipc-sockets/custom-socket.ts
12
12
  var CustomSocket = class {
@@ -309,31 +309,8 @@ var ParentSocket = class extends CustomSocket {
309
309
  this.relayServices.push(attachServices);
310
310
  }
311
311
  };
312
- function realtimeActionReceiver({
313
- socket,
314
- store = IMPLICIT.STORE
315
- }) {
316
- return function actionReceiver(tx) {
317
- const fillTransactionRequest = (update) => {
318
- const performanceKey = `tx-run:${tx.key}:${update.id}`;
319
- const performanceKeyStart = `${performanceKey}:start`;
320
- const performanceKeyEnd = `${performanceKey}:end`;
321
- performance.mark(performanceKeyStart);
322
- actUponStore(tx, update.id, store)(...update.params);
323
- performance.mark(performanceKeyEnd);
324
- const metric = performance.measure(
325
- performanceKey,
326
- performanceKeyStart,
327
- performanceKeyEnd
328
- );
329
- store?.logger.info(`\u{1F680}`, `transaction`, tx.key, update.id, metric.duration);
330
- };
331
- socket.on(`tx-run:${tx.key}`, fillTransactionRequest);
332
- return () => {
333
- socket.off(`tx-run:${tx.key}`, fillTransactionRequest);
334
- };
335
- };
336
- }
312
+
313
+ // realtime-server/src/realtime-server-stores/server-room-external-store.ts
337
314
  var roomArgumentsAtoms = atomFamily({
338
315
  key: `roomArguments`,
339
316
  default: [`echo`, [`Hello World!`]]
@@ -439,8 +416,8 @@ function redactTransactionUpdateContent(visibleStateKeys, updates) {
439
416
  }
440
417
  });
441
418
  }
442
- var actionOcclusionAtoms = atomFamily({
443
- key: `transactionRedactor`,
419
+ var redactorAtoms = atomFamily({
420
+ key: `redactor`,
444
421
  default: { occlude: (updates) => updates }
445
422
  });
446
423
  var userUnacknowledgedQueues = atomFamily({
@@ -472,13 +449,227 @@ var usersOfSockets = join({
472
449
  isAType: (s) => s.startsWith(`user::`),
473
450
  isBType: (s) => s.startsWith(`socket::`)
474
451
  });
452
+ function prepareToSendInitialPayload(store, continuity, userKey, socket) {
453
+ const continuityKey = continuity.key;
454
+ return function sendInitialPayload() {
455
+ const initialPayload = [];
456
+ for (const atom2 of continuity.globals) {
457
+ const resourceToken = atom2.type === `mutable_atom` ? getJsonToken(store, atom2) : atom2;
458
+ const resource = getFromStore(store, resourceToken);
459
+ initialPayload.push(resourceToken, resource);
460
+ }
461
+ for (const perspective of continuity.perspectives) {
462
+ const { viewAtoms, resourceAtoms } = perspective;
463
+ const userViewState = findInStore(store, viewAtoms, userKey);
464
+ const userView = getFromStore(store, userViewState);
465
+ store.logger.info(`\u{1F441}`, `atom`, resourceAtoms.key, `${userKey} can see`, {
466
+ viewAtoms,
467
+ resourceAtoms,
468
+ userView
469
+ });
470
+ for (const visibleToken of userView) {
471
+ const resourceToken = visibleToken.type === `mutable_atom` ? getJsonToken(store, visibleToken) : visibleToken;
472
+ const resource = getFromStore(store, resourceToken);
473
+ initialPayload.push(resourceToken, resource);
474
+ }
475
+ }
476
+ const epoch = isRootStore(store) ? store.transactionMeta.epoch.get(continuityKey) ?? null : null;
477
+ socket?.emit(`continuity-init:${continuityKey}`, epoch, initialPayload);
478
+ };
479
+ }
480
+ function prepareToServeTransactionRequest(store, continuity, userKey) {
481
+ const continuityKey = continuity.key;
482
+ return function serveTransactionRequest(update) {
483
+ store.logger.info(`\u{1F6CE}\uFE0F`, `continuity`, continuityKey, `received`, update);
484
+ const transactionKey = update.key;
485
+ const updateId = update.id;
486
+ const performanceKey = `tx-run:${transactionKey}:${updateId}`;
487
+ const performanceKeyStart = `${performanceKey}:start`;
488
+ const performanceKeyEnd = `${performanceKey}:end`;
489
+ performance.mark(performanceKeyStart);
490
+ try {
491
+ actUponStore(
492
+ { type: `transaction`, key: transactionKey },
493
+ updateId,
494
+ store
495
+ )(...update.params);
496
+ } catch (thrown) {
497
+ if (thrown instanceof Error) {
498
+ store.logger.error(
499
+ `\u274C`,
500
+ `continuity`,
501
+ continuityKey,
502
+ `failed to run transaction ${transactionKey} from ${userKey} with update ${updateId}`,
503
+ thrown.message
504
+ );
505
+ }
506
+ }
507
+ performance.mark(performanceKeyEnd);
508
+ const metric = performance.measure(
509
+ performanceKey,
510
+ performanceKeyStart,
511
+ performanceKeyEnd
512
+ );
513
+ store?.logger.info(
514
+ `\u{1F680}`,
515
+ `transaction`,
516
+ transactionKey,
517
+ updateId,
518
+ userKey,
519
+ metric.duration
520
+ );
521
+ };
522
+ }
523
+ function prepareToTrackClientAcknowledgement(store, continuity, userKey, userUnacknowledgedUpdates) {
524
+ const continuityKey = continuity.key;
525
+ return function trackClientAcknowledgement(epoch) {
526
+ store.logger.info(
527
+ `\u{1F44D}`,
528
+ `continuity`,
529
+ continuityKey,
530
+ `${userKey} acknowledged epoch ${epoch}`
531
+ );
532
+ const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch;
533
+ if (isUnacknowledged) {
534
+ setIntoStore(store, userUnacknowledgedQueues, userKey, (updates) => {
535
+ updates.shift();
536
+ store.logger.info(
537
+ `\u{1F44D}`,
538
+ `continuity`,
539
+ continuityKey,
540
+ `${userKey} unacknowledged update queue now has`,
541
+ updates.length,
542
+ `items`
543
+ );
544
+ return updates;
545
+ });
546
+ }
547
+ };
548
+ }
549
+ function subscribeToContinuityActions(store, continuity, userKey, socket) {
550
+ const continuityKey = continuity.key;
551
+ const unsubscribeFunctions = [];
552
+ for (const transaction2 of continuity.actions) {
553
+ const unsubscribeFromTransaction = subscribeToTransaction(
554
+ transaction2,
555
+ (update) => {
556
+ try {
557
+ const visibleKeys = continuity.globals.map((atom2) => {
558
+ if (atom2.type === `atom`) {
559
+ return atom2.key;
560
+ }
561
+ return getUpdateToken(atom2).key;
562
+ }).concat(
563
+ continuity.perspectives.flatMap((perspective) => {
564
+ const { viewAtoms } = perspective;
565
+ const userPerspectiveTokenState = findInStore(
566
+ store,
567
+ viewAtoms,
568
+ userKey
569
+ );
570
+ const visibleTokens = getFromStore(
571
+ store,
572
+ userPerspectiveTokenState
573
+ );
574
+ return visibleTokens.map((token) => {
575
+ const key = token.type === `mutable_atom` ? `*` + token.key : token.key;
576
+ return key;
577
+ });
578
+ })
579
+ );
580
+ const redactedUpdates = redactTransactionUpdateContent(
581
+ visibleKeys,
582
+ update.updates
583
+ );
584
+ const redactedUpdate = {
585
+ ...update,
586
+ updates: redactedUpdates
587
+ };
588
+ setIntoStore(store, userUnacknowledgedQueues, userKey, (updates) => {
589
+ if (redactedUpdate) {
590
+ updates.push(redactedUpdate);
591
+ updates.sort((a, b) => a.epoch - b.epoch);
592
+ store.logger.info(
593
+ `\u{1F44D}`,
594
+ `continuity`,
595
+ continuityKey,
596
+ `${userKey} unacknowledged update queue now has`,
597
+ updates.length,
598
+ `items`
599
+ );
600
+ }
601
+ return updates;
602
+ });
603
+ socket?.emit(
604
+ `tx-new:${continuityKey}`,
605
+ redactedUpdate
606
+ );
607
+ } catch (thrown) {
608
+ if (thrown instanceof Error) {
609
+ store.logger.error(
610
+ `\u274C`,
611
+ `continuity`,
612
+ continuityKey,
613
+ `${userKey} failed to send update from transaction ${transaction2.key} to ${userKey}`,
614
+ thrown.message
615
+ );
616
+ }
617
+ }
618
+ },
619
+ `sync-continuity:${continuityKey}:${userKey}`,
620
+ store
621
+ );
622
+ unsubscribeFunctions.push(unsubscribeFromTransaction);
623
+ }
624
+ return unsubscribeFunctions;
625
+ }
626
+ function subscribeToContinuityPerspectives(store, continuity, userKey, socket) {
627
+ const continuityKey = continuity.key;
628
+ const unsubFns = [];
629
+ for (const perspective of continuity.perspectives) {
630
+ const { viewAtoms } = perspective;
631
+ const userViewState = findInStore(store, viewAtoms, userKey);
632
+ const unsubscribeFromUserView = subscribeToState(
633
+ userViewState,
634
+ ({ oldValue, newValue }) => {
635
+ const oldKeys = oldValue.map((token) => token.key);
636
+ const newKeys = newValue.map((token) => token.key);
637
+ const concealed = oldValue.filter(
638
+ (token) => !newKeys.includes(token.key)
639
+ );
640
+ const revealed = newValue.filter((token) => !oldKeys.includes(token.key)).flatMap((token) => {
641
+ const resourceToken = token.type === `mutable_atom` ? getJsonToken(store, token) : token;
642
+ const resource = getFromStore(store, resourceToken);
643
+ return [resourceToken, resource];
644
+ });
645
+ store.logger.info(
646
+ `\u{1F441}`,
647
+ `atom`,
648
+ perspective.resourceAtoms.key,
649
+ `${userKey} has a new perspective`,
650
+ { oldKeys, newKeys, revealed, concealed }
651
+ );
652
+ if (revealed.length > 0) {
653
+ socket?.emit(`reveal:${continuityKey}`, revealed);
654
+ }
655
+ if (concealed.length > 0) {
656
+ socket?.emit(`conceal:${continuityKey}`, concealed);
657
+ }
658
+ },
659
+ `sync-continuity:${continuityKey}:${userKey}:perspective:${perspective.resourceAtoms.key}`,
660
+ store
661
+ );
662
+ unsubFns.push(unsubscribeFromUserView);
663
+ }
664
+ return unsubFns;
665
+ }
475
666
 
476
- // realtime-server/src/realtime-continuity-synchronizer.ts
477
- function realtimeContinuitySynchronizer({
667
+ // realtime-server/src/continuity/prepare-to-sync-realtime-continuity.ts
668
+ function prepareToExposeRealtimeContinuity({
478
669
  socket: initialSocket,
479
670
  store = IMPLICIT.STORE
480
671
  }) {
481
- return function synchronizer(continuity) {
672
+ return function syncRealtimeContinuity(continuity) {
482
673
  let socket = initialSocket;
483
674
  const continuityKey = continuity.key;
484
675
  const userKeyState = findRelationsInStore(
@@ -533,226 +724,80 @@ function realtimeContinuitySynchronizer({
533
724
  `sync-continuity:${continuityKey}:${userKey}`,
534
725
  store
535
726
  );
536
- const userUnacknowledgedQueue = findInStore(
727
+ const userUnacknowledgedUpdates = getFromStore(
537
728
  store,
538
729
  userUnacknowledgedQueues,
539
730
  userKey
540
731
  );
541
- const userUnacknowledgedUpdates = getFromStore(
732
+ const unsubscribeFunctions = [];
733
+ const unsubscribeFromPerspectives = subscribeToContinuityPerspectives(
542
734
  store,
543
- userUnacknowledgedQueue
735
+ continuity,
736
+ userKey,
737
+ socket
738
+ );
739
+ const unsubscribeFromTransactions = subscribeToContinuityActions(
740
+ store,
741
+ continuity,
742
+ userKey,
743
+ socket
744
+ );
745
+ unsubscribeFunctions.push(
746
+ ...unsubscribeFromPerspectives,
747
+ ...unsubscribeFromTransactions
748
+ );
749
+ const sendInitialPayload = prepareToSendInitialPayload(
750
+ store,
751
+ continuity,
752
+ userKey,
753
+ initialSocket
544
754
  );
545
- const unsubscribeFunctions = [];
546
- const revealPerspectives = () => {
547
- const unsubFns = [];
548
- for (const perspective of continuity.perspectives) {
549
- const { viewAtoms } = perspective;
550
- const userViewState = findInStore(store, viewAtoms, userKey);
551
- const unsubscribe = subscribeToState(
552
- userViewState,
553
- ({ oldValue, newValue }) => {
554
- const oldKeys = oldValue.map((token) => token.key);
555
- const newKeys = newValue.map((token) => token.key);
556
- const concealed = oldValue.filter(
557
- (token) => !newKeys.includes(token.key)
558
- );
559
- const revealed = newValue.filter((token) => !oldKeys.includes(token.key)).flatMap((token) => {
560
- const resourceToken = token.type === `mutable_atom` ? getJsonToken(store, token) : token;
561
- const resource = getFromStore(store, resourceToken);
562
- return [resourceToken, resource];
563
- });
564
- store.logger.info(
565
- `\u{1F441}`,
566
- `atom`,
567
- perspective.resourceAtoms.key,
568
- `${userKey} has a new perspective`,
569
- { oldKeys, newKeys, revealed, concealed }
570
- );
571
- if (revealed.length > 0) {
572
- socket?.emit(`reveal:${continuityKey}`, revealed);
573
- }
574
- if (concealed.length > 0) {
575
- socket?.emit(`conceal:${continuityKey}`, concealed);
576
- }
577
- },
578
- `sync-continuity:${continuityKey}:${userKey}:perspective:${perspective.resourceAtoms.key}`,
579
- store
580
- );
581
- unsubFns.push(unsubscribe);
582
- }
583
- return () => {
584
- for (const unsubscribe of unsubFns) unsubscribe();
585
- };
586
- };
587
- const unsubscribeFromPerspectives = revealPerspectives();
588
- const sendInitialPayload = () => {
589
- const initialPayload = [];
590
- for (const atom2 of continuity.globals) {
591
- const resourceToken = atom2.type === `mutable_atom` ? getJsonToken(store, atom2) : atom2;
592
- const resource = getFromStore(store, resourceToken);
593
- initialPayload.push(resourceToken, resource);
594
- }
595
- for (const perspective of continuity.perspectives) {
596
- const { viewAtoms, resourceAtoms } = perspective;
597
- const userViewState = findInStore(store, viewAtoms, userKey);
598
- const userView = getFromStore(store, userViewState);
599
- store.logger.info(`\u{1F441}`, `atom`, resourceAtoms.key, `${userKey} can see`, {
600
- viewAtoms,
601
- resourceAtoms,
602
- userView
603
- });
604
- for (const visibleToken of userView) {
605
- const resourceToken = visibleToken.type === `mutable_atom` ? getJsonToken(store, visibleToken) : visibleToken;
606
- const resource = getFromStore(store, resourceToken);
607
- initialPayload.push(resourceToken, resource);
608
- }
609
- }
610
- const epoch = isRootStore(store) ? store.transactionMeta.epoch.get(continuityKey) ?? null : null;
611
- socket?.emit(`continuity-init:${continuityKey}`, epoch, initialPayload);
612
- for (const transaction2 of continuity.actions) {
613
- const unsubscribeFromTransaction = subscribeToTransaction(
614
- transaction2,
615
- (update) => {
616
- try {
617
- const visibleKeys = continuity.globals.map((atom2) => {
618
- if (atom2.type === `atom`) {
619
- return atom2.key;
620
- }
621
- return getUpdateToken(atom2).key;
622
- }).concat(
623
- continuity.perspectives.flatMap((perspective) => {
624
- const { viewAtoms } = perspective;
625
- const userPerspectiveTokenState = findInStore(
626
- store,
627
- viewAtoms,
628
- userKey
629
- );
630
- const visibleTokens = getFromStore(
631
- store,
632
- userPerspectiveTokenState
633
- );
634
- return visibleTokens.map((token) => {
635
- const key = token.type === `mutable_atom` ? `*` + token.key : token.key;
636
- return key;
637
- });
638
- })
639
- );
640
- const redactedUpdates = redactTransactionUpdateContent(
641
- visibleKeys,
642
- update.updates
643
- );
644
- const redactedUpdate = {
645
- ...update,
646
- updates: redactedUpdates
647
- };
648
- setIntoStore(store, userUnacknowledgedQueue, (updates) => {
649
- if (redactedUpdate) {
650
- updates.push(redactedUpdate);
651
- updates.sort((a, b) => a.epoch - b.epoch);
652
- }
653
- return updates;
654
- });
655
- socket?.emit(
656
- `tx-new:${continuityKey}`,
657
- redactedUpdate
658
- );
659
- } catch (thrown) {
660
- if (thrown instanceof Error) {
661
- store.logger.error(
662
- `\u274C`,
663
- `continuity`,
664
- continuityKey,
665
- `failed to send update from transaction ${transaction2.key} to ${userKey}`,
666
- thrown.message
667
- );
668
- }
669
- }
670
- },
671
- `sync-continuity:${continuityKey}:${userKey}`,
672
- store
673
- );
674
- unsubscribeFunctions.push(unsubscribeFromTransaction);
675
- }
676
- };
677
755
  socket.off(`get:${continuityKey}`, sendInitialPayload);
678
756
  socket.on(`get:${continuityKey}`, sendInitialPayload);
757
+ const fillTransactionRequest = prepareToServeTransactionRequest(
758
+ store,
759
+ continuity,
760
+ userKey
761
+ );
762
+ socket.off(`tx-run:${continuityKey}`, fillTransactionRequest);
763
+ socket.on(`tx-run:${continuityKey}`, fillTransactionRequest);
764
+ const trackClientAcknowledgement = prepareToTrackClientAcknowledgement(
765
+ store,
766
+ continuity,
767
+ userKey,
768
+ userUnacknowledgedUpdates
769
+ );
770
+ socket?.on(`ack:${continuityKey}`, trackClientAcknowledgement);
771
+ return () => {
772
+ for (const unsubscribe of unsubscribeFunctions) unsubscribe();
773
+ socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement);
774
+ socket?.off(`get:${continuityKey}`, sendInitialPayload);
775
+ socket?.off(`tx-run:${continuityKey}`, fillTransactionRequest);
776
+ };
777
+ };
778
+ }
779
+ function realtimeActionReceiver({
780
+ socket,
781
+ store = IMPLICIT.STORE
782
+ }) {
783
+ return function actionReceiver(tx) {
679
784
  const fillTransactionRequest = (update) => {
680
- store.logger.info(`\u{1F6CE}\uFE0F`, `continuity`, continuityKey, `received`, update);
681
- const transactionKey = update.key;
682
- const updateId = update.id;
683
- const performanceKey = `tx-run:${transactionKey}:${updateId}`;
785
+ const performanceKey = `tx-run:${tx.key}:${update.id}`;
684
786
  const performanceKeyStart = `${performanceKey}:start`;
685
787
  const performanceKeyEnd = `${performanceKey}:end`;
686
788
  performance.mark(performanceKeyStart);
687
- try {
688
- actUponStore(
689
- { type: `transaction`, key: transactionKey },
690
- updateId,
691
- store
692
- )(...update.params);
693
- } catch (thrown) {
694
- if (thrown instanceof Error) {
695
- store.logger.error(
696
- `\u274C`,
697
- `continuity`,
698
- continuityKey,
699
- `failed to run transaction ${transactionKey} with update ${updateId}`,
700
- thrown.message
701
- );
702
- }
703
- }
789
+ actUponStore(tx, update.id, store)(...update.params);
704
790
  performance.mark(performanceKeyEnd);
705
791
  const metric = performance.measure(
706
792
  performanceKey,
707
793
  performanceKeyStart,
708
794
  performanceKeyEnd
709
795
  );
710
- store?.logger.info(
711
- `\u{1F680}`,
712
- `transaction`,
713
- transactionKey,
714
- updateId,
715
- metric.duration
716
- );
717
- const valuesOfCardsViewKey = `valuesOfCardsView("${userKey}")`;
718
- const rootsOfCardValueView = store.selectorAtoms.getRelatedKeys(valuesOfCardsViewKey);
719
- const myCardValueView = store.valueMap.get(valuesOfCardsViewKey);
720
- store.logger.info(
721
- `\u{1F441}`,
722
- `continuity`,
723
- continuityKey,
724
- `seeing ${userKey} card values`,
725
- {
726
- valuesOfCardsViewKey,
727
- rootsOfCardValueView,
728
- myCardValueView
729
- }
730
- );
731
- };
732
- socket.off(`tx-run:${continuityKey}`, fillTransactionRequest);
733
- socket.on(`tx-run:${continuityKey}`, fillTransactionRequest);
734
- const trackClientAcknowledgement = (epoch) => {
735
- store.logger.info(
736
- `\u{1F44D}`,
737
- `continuity`,
738
- continuityKey,
739
- `${userKey} acknowledged epoch ${epoch}`
740
- );
741
- const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch;
742
- if (isUnacknowledged) {
743
- setIntoStore(store, userUnacknowledgedQueue, (updates) => {
744
- updates.shift();
745
- return updates;
746
- });
747
- }
796
+ store?.logger.info(`\u{1F680}`, `transaction`, tx.key, update.id, metric.duration);
748
797
  };
749
- socket?.on(`ack:${continuityKey}`, trackClientAcknowledgement);
798
+ socket.on(`tx-run:${tx.key}`, fillTransactionRequest);
750
799
  return () => {
751
- for (const unsubscribe of unsubscribeFunctions) unsubscribe();
752
- socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement);
753
- unsubscribeFromPerspectives();
754
- socket?.off(`get:${continuityKey}`, sendInitialPayload);
755
- socket?.off(`tx-run:${continuityKey}`, fillTransactionRequest);
800
+ socket.off(`tx-run:${tx.key}`, fillTransactionRequest);
756
801
  };
757
802
  };
758
803
  }
@@ -942,4 +987,4 @@ function realtimeStateReceiver({
942
987
  };
943
988
  }
944
989
 
945
- export { ChildSocket, CustomSocket, ParentSocket, SubjectSocket, actionOcclusionAtoms, createRoomTX, destroyRoomTX, joinRoomTX, leaveRoomTX, realtimeActionReceiver, realtimeAtomFamilyProvider, realtimeContinuitySynchronizer, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, redactTransactionUpdateContent, roomArgumentsAtoms, roomSelectors, socketAtoms, socketIndex, userIndex, userUnacknowledgedQueues, usersOfSockets };
990
+ export { ChildSocket, CustomSocket, ParentSocket, SubjectSocket, createRoomTX, destroyRoomTX, joinRoomTX, leaveRoomTX, prepareToExposeRealtimeContinuity, realtimeActionReceiver, realtimeAtomFamilyProvider, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, redactTransactionUpdateContent, redactorAtoms, roomArgumentsAtoms, roomSelectors, socketAtoms, socketIndex, userIndex, userUnacknowledgedQueues, usersOfSockets };
@@ -0,0 +1,54 @@
1
+ import type { Store } from "atom.io/internal"
2
+ import {
3
+ findInStore,
4
+ getFromStore,
5
+ getJsonToken,
6
+ isRootStore,
7
+ } from "atom.io/internal"
8
+ import type { Json } from "atom.io/json"
9
+ import type { ContinuityToken } from "atom.io/realtime"
10
+
11
+ import type { Socket, UserKey } from ".."
12
+
13
+ export function prepareToSendInitialPayload(
14
+ store: Store,
15
+ continuity: ContinuityToken,
16
+ userKey: UserKey,
17
+ socket: Socket | null,
18
+ ): () => void {
19
+ const continuityKey = continuity.key
20
+ return function sendInitialPayload(): void {
21
+ const initialPayload: Json.Serializable[] = []
22
+ for (const atom of continuity.globals) {
23
+ const resourceToken =
24
+ atom.type === `mutable_atom` ? getJsonToken(store, atom) : atom
25
+ const resource = getFromStore(store, resourceToken)
26
+ initialPayload.push(resourceToken, resource)
27
+ }
28
+ for (const perspective of continuity.perspectives) {
29
+ const { viewAtoms, resourceAtoms } = perspective
30
+ const userViewState = findInStore(store, viewAtoms, userKey)
31
+ const userView = getFromStore(store, userViewState)
32
+ store.logger.info(`👁`, `atom`, resourceAtoms.key, `${userKey} can see`, {
33
+ viewAtoms,
34
+ resourceAtoms,
35
+ userView,
36
+ })
37
+ for (const visibleToken of userView) {
38
+ const resourceToken =
39
+ visibleToken.type === `mutable_atom`
40
+ ? getJsonToken(store, visibleToken)
41
+ : visibleToken
42
+ const resource = getFromStore(store, resourceToken)
43
+
44
+ initialPayload.push(resourceToken, resource)
45
+ }
46
+ }
47
+
48
+ const epoch = isRootStore(store)
49
+ ? (store.transactionMeta.epoch.get(continuityKey) ?? null)
50
+ : null
51
+
52
+ socket?.emit(`continuity-init:${continuityKey}`, epoch, initialPayload)
53
+ }
54
+ }
@@ -0,0 +1,53 @@
1
+ import type { TransactionUpdate } from "atom.io"
2
+ import type { Store } from "atom.io/internal"
3
+ import { actUponStore } from "atom.io/internal"
4
+ import type { JsonIO } from "atom.io/json"
5
+ import type { ContinuityToken } from "atom.io/realtime"
6
+
7
+ export function prepareToServeTransactionRequest(
8
+ store: Store,
9
+ continuity: ContinuityToken,
10
+ userKey: string,
11
+ ): (update: Pick<TransactionUpdate<JsonIO>, `id` | `key` | `params`>) => void {
12
+ const continuityKey = continuity.key
13
+ return function serveTransactionRequest(update) {
14
+ store.logger.info(`🛎️`, `continuity`, continuityKey, `received`, update)
15
+ const transactionKey = update.key
16
+ const updateId = update.id
17
+ const performanceKey = `tx-run:${transactionKey}:${updateId}`
18
+ const performanceKeyStart = `${performanceKey}:start`
19
+ const performanceKeyEnd = `${performanceKey}:end`
20
+ performance.mark(performanceKeyStart)
21
+ try {
22
+ actUponStore(
23
+ { type: `transaction`, key: transactionKey },
24
+ updateId,
25
+ store,
26
+ )(...update.params)
27
+ } catch (thrown) {
28
+ if (thrown instanceof Error) {
29
+ store.logger.error(
30
+ `❌`,
31
+ `continuity`,
32
+ continuityKey,
33
+ `failed to run transaction ${transactionKey} from ${userKey} with update ${updateId}`,
34
+ thrown.message,
35
+ )
36
+ }
37
+ }
38
+ performance.mark(performanceKeyEnd)
39
+ const metric = performance.measure(
40
+ performanceKey,
41
+ performanceKeyStart,
42
+ performanceKeyEnd,
43
+ )
44
+ store?.logger.info(
45
+ `🚀`,
46
+ `transaction`,
47
+ transactionKey,
48
+ updateId,
49
+ userKey,
50
+ metric.duration,
51
+ )
52
+ }
53
+ }