atom.io 0.45.5 → 0.46.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.
Files changed (31) hide show
  1. package/dist/internal/index.js +2 -7
  2. package/dist/internal/index.js.map +1 -1
  3. package/dist/react-devtools/index.d.ts.map +1 -1
  4. package/dist/realtime/index.d.ts +6 -3
  5. package/dist/realtime/index.d.ts.map +1 -1
  6. package/dist/realtime/index.js +9 -1
  7. package/dist/realtime/index.js.map +1 -1
  8. package/dist/realtime-server/index.d.ts +73 -50
  9. package/dist/realtime-server/index.d.ts.map +1 -1
  10. package/dist/realtime-server/index.js +222 -182
  11. package/dist/realtime-server/index.js.map +1 -1
  12. package/dist/realtime-testing/index.d.ts +1 -2
  13. package/dist/realtime-testing/index.d.ts.map +1 -1
  14. package/dist/realtime-testing/index.js +31 -53
  15. package/dist/realtime-testing/index.js.map +1 -1
  16. package/package.json +10 -10
  17. package/src/internal/subscribe/subscribe-to-state.ts +9 -18
  18. package/src/realtime/cast-socket.ts +1 -0
  19. package/src/realtime/shared-room-store.ts +15 -0
  20. package/src/realtime/socket-interface.ts +5 -1
  21. package/src/realtime-server/index.ts +3 -2
  22. package/src/realtime-server/ipc-sockets/custom-socket.ts +18 -2
  23. package/src/realtime-server/ipc-sockets/parent-socket.ts +1 -1
  24. package/src/realtime-server/{realtime-server-stores/provide-rooms.ts → provide-rooms.ts} +32 -57
  25. package/src/realtime-server/realtime-state-provider.ts +5 -3
  26. package/src/realtime-server/realtime-state-receiver.ts +19 -3
  27. package/src/realtime-server/server-config.ts +112 -1
  28. package/src/realtime-server/{realtime-server-stores/server-user-store.ts → server-socket-state.ts} +1 -8
  29. package/src/realtime-testing/setup-realtime-test.tsx +38 -83
  30. package/src/realtime-server/realtime-server-stores/index.ts +0 -3
  31. package/src/realtime-server/realtime-server-stores/provide-identity.ts +0 -18
@@ -1,8 +1,8 @@
1
1
  import { IMPLICIT, OWN_OP, Subject, actUponStore, editRelationsInStore, findInStore, findRelationsInStore, getFromStore, getInternalRelationsFromStore, getJsonToken, getUpdateToken, isRootStore, operateOnStore, setIntoStore, subscribeToState, subscribeToTransaction } from "atom.io/internal";
2
- import { atomFamily, join, mutableAtom, selectorFamily } from "atom.io";
2
+ import { Realm, atomFamily, join, mutableAtom } from "atom.io";
3
3
  import { parseJson, stringifyJson } from "atom.io/json";
4
4
  import { UList } from "atom.io/transceivers/u-list";
5
- import { castSocket, employSocket, isRoomKey, isSocketKey, isUserKey, mutexAtoms, ownersOfRooms, roomKeysAtom, usersInRooms } from "atom.io/realtime";
5
+ import { castSocket, employSocket, isRoomKey, isSocketKey, isUserKey, mutexAtoms, ownersOfRooms, roomKeysAtom, usersInRooms, visibleUsersInRoomsSelector } from "atom.io/realtime";
6
6
  import { spawn } from "node:child_process";
7
7
 
8
8
  //#region src/realtime-server/continuity/continuity-store.ts
@@ -215,6 +215,7 @@ function prepareToProvideContinuity({ socket, store = IMPLICIT.STORE }) {
215
215
  var CustomSocket = class {
216
216
  listeners;
217
217
  globalListeners;
218
+ globalListenersOutgoing;
218
219
  handleEvent(...args) {
219
220
  const [event, ...rest] = args;
220
221
  for (const listener of this.globalListeners) listener(event, ...rest);
@@ -224,9 +225,13 @@ var CustomSocket = class {
224
225
  id = `no_id_retrieved`;
225
226
  emit;
226
227
  constructor(emit) {
227
- this.emit = emit;
228
+ this.emit = (...args) => {
229
+ for (const listener of this.globalListenersOutgoing) listener(...args);
230
+ return emit(...args);
231
+ };
228
232
  this.listeners = /* @__PURE__ */ new Map();
229
233
  this.globalListeners = /* @__PURE__ */ new Set();
234
+ this.globalListenersOutgoing = /* @__PURE__ */ new Set();
230
235
  }
231
236
  on(event, listener) {
232
237
  const listeners = this.listeners.get(event);
@@ -238,6 +243,10 @@ var CustomSocket = class {
238
243
  this.globalListeners.add(listener);
239
244
  return this;
240
245
  }
246
+ onAnyOutgoing(listener) {
247
+ this.globalListenersOutgoing.add(listener);
248
+ return this;
249
+ }
241
250
  off(event, listener) {
242
251
  const listeners = this.listeners.get(event);
243
252
  if (listeners) if (listener) listeners.delete(listener);
@@ -520,77 +529,6 @@ var ParentSocket = class extends CustomSocket {
520
529
  }
521
530
  };
522
531
 
523
- //#endregion
524
- //#region src/realtime-server/realtime-family-provider.ts
525
- function realtimeAtomFamilyProvider({ socket, userKey, store = IMPLICIT.STORE }) {
526
- return function familyProvider(family, index) {
527
- const coreSubscriptions = /* @__PURE__ */ new Set();
528
- const clearCoreSubscriptions = () => {
529
- for (const unsub of coreSubscriptions) unsub();
530
- coreSubscriptions.clear();
531
- };
532
- const familyMemberSubscriptionsWanted = /* @__PURE__ */ new Set();
533
- const familyMemberSubscriptions = /* @__PURE__ */ new Map();
534
- const clearFamilySubscriptions = () => {
535
- for (const unsub of familyMemberSubscriptions.values()) unsub();
536
- familyMemberSubscriptions.clear();
537
- };
538
- const fillUnsubRequest = (key) => {
539
- const unsubUnsub = familyMemberSubscriptions.get(`${key}:unsub`);
540
- if (unsubUnsub) {
541
- unsubUnsub();
542
- familyMemberSubscriptions.delete(`${key}:unsub`);
543
- }
544
- const unsub = familyMemberSubscriptions.get(key);
545
- if (unsub) {
546
- unsub();
547
- familyMemberSubscriptions.delete(key);
548
- }
549
- };
550
- const exposeFamilyMembers = (subKey) => {
551
- const token = findInStore(store, family, subKey);
552
- getFromStore(store, token);
553
- socket.emit(`serve:${token.key}`, getFromStore(store, token));
554
- familyMemberSubscriptions.set(token.key, subscribeToState(store, token, `expose-family:${family.key}:${socket.id}`, ({ newValue }) => {
555
- socket.emit(`serve:${token.key}`, newValue);
556
- }));
557
- familyMemberSubscriptions.set(`${token.key}:unsub`, employSocket(socket, `unsub:${token.key}`, () => {
558
- store.logger.info(`🙈`, `user`, userKey, `unsubscribed from state "${token.key}"`);
559
- fillUnsubRequest(token.key);
560
- }));
561
- };
562
- const isAvailable$1 = (exposedSubKeys, subKey) => {
563
- for (const exposedSubKey of exposedSubKeys) if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) return true;
564
- return false;
565
- };
566
- const start = () => {
567
- store.logger.info(`👀`, `user`, userKey, `can subscribe to family "${family.key}"`);
568
- coreSubscriptions.add(employSocket(socket, `sub:${family.key}`, (subKey) => {
569
- if (isAvailable$1(getFromStore(store, index), subKey)) {
570
- store.logger.info(`👀`, `user`, userKey, `is approved for a subscription to`, subKey, `in family "${family.key}"`);
571
- exposeFamilyMembers(subKey);
572
- } else {
573
- store.logger.info(`❌`, `user`, userKey, `is denied for a subscription to`, subKey, `in family "${family.key}"`);
574
- familyMemberSubscriptionsWanted.add(stringifyJson(subKey));
575
- socket.emit(`unavailable:${family.key}`, subKey);
576
- }
577
- }));
578
- coreSubscriptions.add(subscribeToState(store, index, `expose-family:${family.key}:${socket.id}`, ({ newValue: newExposedSubKeys }) => {
579
- store.logger.info(`👀`, `user`, userKey, `has the following keys available for family "${family.key}"`, newExposedSubKeys);
580
- for (const subKey of newExposedSubKeys) if (familyMemberSubscriptionsWanted.has(stringifyJson(subKey))) {
581
- store.logger.info(`👀`, `user`, userKey, `is retroactively approved for a subscription to`, subKey, `in family "${family.key}"`);
582
- exposeFamilyMembers(subKey);
583
- }
584
- }));
585
- };
586
- start();
587
- return () => {
588
- clearCoreSubscriptions();
589
- clearFamilySubscriptions();
590
- };
591
- };
592
- }
593
-
594
532
  //#endregion
595
533
  //#region src/realtime-server/realtime-mutable-family-provider.ts
596
534
  const isAvailable = (exposedSubKeys, subKey) => {
@@ -696,90 +634,7 @@ function realtimeMutableProvider({ socket, userKey, store = IMPLICIT.STORE }) {
696
634
  }
697
635
 
698
636
  //#endregion
699
- //#region src/realtime-server/realtime-state-provider.ts
700
- function isReadableToken(input) {
701
- return typeof input === `object` && input !== null && `key` in input && `type` in input;
702
- }
703
- function realtimeStateProvider({ socket, userKey, store = IMPLICIT.STORE }) {
704
- store.logger.info(`🔌`, `user`, userKey, `initialized state provider`);
705
- return function stateProvider(clientToken, serverData = clientToken) {
706
- const isStatic = !isReadableToken(serverData);
707
- const subscriptions = /* @__PURE__ */ new Set();
708
- const clearSubscriptions = () => {
709
- for (const unsub of subscriptions) unsub();
710
- subscriptions.clear();
711
- };
712
- const start = () => {
713
- if (isStatic) store.logger.info(`👀`, `user`, userKey, `will be served`, serverData, `as "${clientToken.key}"`);
714
- else store.logger.info(`👀`, `user`, userKey, `can subscribe to state "${serverData.key}" as "${clientToken.key}"`);
715
- subscriptions.add(employSocket(socket, `sub:${clientToken.key}`, () => {
716
- if (isStatic) {
717
- store.logger.info(`👀`, `user`, userKey, `requests`, `"${clientToken.key}"`);
718
- socket.emit(`serve:${clientToken.key}`, serverData);
719
- } else {
720
- store.logger.info(`👀`, `user`, userKey, `subscribes to state "${serverData.key}"`, clientToken === serverData ? `directly` : `as "${clientToken.key}"`);
721
- clearSubscriptions();
722
- socket.emit(`serve:${clientToken.key}`, getFromStore(store, serverData));
723
- subscriptions.add(subscribeToState(store, serverData, `expose-single:${socket.id}`, ({ newValue }) => {
724
- socket.emit(`serve:${clientToken.key}`, newValue);
725
- }));
726
- subscriptions.add(employSocket(socket, `unsub:${serverData.key}`, () => {
727
- store.logger.info(`🙈`, `user`, userKey, `unsubscribes from state "${serverData.key}", served`, clientToken === serverData ? `directly` : `as "${clientToken.key}"`);
728
- clearSubscriptions();
729
- start();
730
- }));
731
- }
732
- }));
733
- };
734
- start();
735
- return clearSubscriptions;
736
- };
737
- }
738
-
739
- //#endregion
740
- //#region src/realtime-server/realtime-server-stores/provide-identity.ts
741
- function provideIdentity({ store = IMPLICIT.STORE, socket, userKey }) {
742
- const unsub = realtimeStateProvider({
743
- socket,
744
- store,
745
- userKey
746
- })({
747
- key: `myUserKey`,
748
- type: `atom`
749
- }, userKey);
750
- socket.on(`disconnect`, () => {
751
- unsub();
752
- });
753
- }
754
-
755
- //#endregion
756
- //#region src/realtime-server/realtime-server-stores/server-user-store.ts
757
- const socketAtoms = atomFamily({
758
- key: `sockets`,
759
- default: null
760
- });
761
- const socketKeysAtom = mutableAtom({
762
- key: `socketsIndex`,
763
- class: UList
764
- });
765
- const userKeysAtom = mutableAtom({
766
- key: `usersIndex`,
767
- class: UList
768
- });
769
- const usersOfSockets = join({
770
- key: `usersOfSockets`,
771
- between: [`user`, `socket`],
772
- cardinality: `1:1`,
773
- isAType: isUserKey,
774
- isBType: isSocketKey
775
- });
776
- const selfListSelectors = selectorFamily({
777
- key: `selfList`,
778
- get: (userKey) => () => [userKey]
779
- });
780
-
781
- //#endregion
782
- //#region src/realtime-server/realtime-server-stores/provide-rooms.ts
637
+ //#region src/realtime-server/provide-rooms.ts
783
638
  const ROOMS = globalThis.ATOM_IO_REALTIME_SERVER_ROOMS ?? (globalThis.ATOM_IO_REALTIME_SERVER_ROOMS = /* @__PURE__ */ new Map());
784
639
  const roomMeta = { count: 0 };
785
640
  function spawnRoom({ store, socket, userKey, resolveRoomScript }) {
@@ -888,33 +743,31 @@ function destroyRoom({ store, socket, userKey }) {
888
743
  store.logger.info(`📡`, `socket`, socket.id ?? `[ID MISSING?!]`, `👤 ${userKey} failed to delete room ${roomKey}; room owner is ${owner}`);
889
744
  };
890
745
  }
891
- function provideRooms({ store = IMPLICIT.STORE, socket, resolveRoomScript, roomNames }) {
892
- const socketKey = `socket::${socket.id}`;
893
- const userKey = getFromStore(store, findRelationsInStore(store, usersOfSockets, socketKey).userKeyOfSocket);
746
+ function provideRooms({ store = IMPLICIT.STORE, socket, resolveRoomScript, roomNames, userKey }) {
894
747
  const roomSocket = castSocket(socket, createRoomSocketGuard(roomNames));
895
- const exposeMutable = realtimeMutableProvider({
748
+ const unsubFromRoomKeys = realtimeMutableProvider({
896
749
  socket,
897
750
  store,
898
751
  userKey
899
- });
752
+ })(roomKeysAtom);
753
+ const usersInRoomsAtoms = getInternalRelationsFromStore(store, usersInRooms);
754
+ const [, usersInRoomsAtomsUsersOnly] = getInternalRelationsFromStore(store, usersInRooms, `split`);
755
+ const usersWhoseRoomsCanBeSeenSelector = findInStore(store, visibleUsersInRoomsSelector, userKey);
756
+ const ownersOfRoomsAtoms = getInternalRelationsFromStore(store, ownersOfRooms);
900
757
  const exposeMutableFamily = realtimeMutableFamilyProvider({
901
758
  socket,
902
759
  store,
903
760
  userKey
904
761
  });
905
- exposeMutable(roomKeysAtom);
906
- const [, usersInRoomsAtoms] = getInternalRelationsFromStore(store, usersInRooms, `split`);
907
- const usersWhoseRoomsCanBeSeenSelector = findInStore(store, selfListSelectors, userKey);
908
- exposeMutableFamily(usersInRoomsAtoms, usersWhoseRoomsCanBeSeenSelector);
909
- const [ownersOfRoomsAtoms] = getInternalRelationsFromStore(store, ownersOfRooms, `split`);
910
- exposeMutableFamily(ownersOfRoomsAtoms, usersWhoseRoomsCanBeSeenSelector);
762
+ const unsubFromUsersInRooms = exposeMutableFamily(usersInRoomsAtoms, usersWhoseRoomsCanBeSeenSelector);
763
+ const unsubFromOwnersOfRooms = exposeMutableFamily(ownersOfRoomsAtoms, usersWhoseRoomsCanBeSeenSelector);
911
764
  const enterRoom = provideEnterAndExit({
912
765
  store,
913
766
  socket,
914
767
  roomSocket,
915
768
  userKey
916
769
  });
917
- const userRoomSet = getFromStore(store, usersInRoomsAtoms, userKey);
770
+ const userRoomSet = getFromStore(store, usersInRoomsAtomsUsersOnly, userKey);
918
771
  for (const userRoomKey of userRoomSet) {
919
772
  enterRoom(userRoomKey);
920
773
  break;
@@ -930,12 +783,11 @@ function provideRooms({ store = IMPLICIT.STORE, socket, resolveRoomScript, roomN
930
783
  socket,
931
784
  userKey
932
785
  }));
933
- socket.on(`disconnect`, () => {
934
- store.logger.info(`📡`, `socket`, socket.id ?? `[ID MISSING?!]`, `👤 ${userKey} disconnects`);
935
- editRelationsInStore(store, usersOfSockets, (rel) => rel.delete(socketKey));
936
- setIntoStore(store, userKeysAtom, (keys) => (keys.delete(userKey), keys));
937
- setIntoStore(store, socketKeysAtom, (keys) => (keys.delete(socketKey), keys));
938
- });
786
+ return () => {
787
+ unsubFromRoomKeys();
788
+ unsubFromUsersInRooms();
789
+ unsubFromOwnersOfRooms();
790
+ };
939
791
  }
940
792
  const roomKeySchema = { "~standard": {
941
793
  version: 1,
@@ -968,10 +820,123 @@ function createRoomSocketGuard(roomNames) {
968
820
  };
969
821
  }
970
822
 
823
+ //#endregion
824
+ //#region src/realtime-server/realtime-family-provider.ts
825
+ function realtimeAtomFamilyProvider({ socket, userKey, store = IMPLICIT.STORE }) {
826
+ return function familyProvider(family, index) {
827
+ const coreSubscriptions = /* @__PURE__ */ new Set();
828
+ const clearCoreSubscriptions = () => {
829
+ for (const unsub of coreSubscriptions) unsub();
830
+ coreSubscriptions.clear();
831
+ };
832
+ const familyMemberSubscriptionsWanted = /* @__PURE__ */ new Set();
833
+ const familyMemberSubscriptions = /* @__PURE__ */ new Map();
834
+ const clearFamilySubscriptions = () => {
835
+ for (const unsub of familyMemberSubscriptions.values()) unsub();
836
+ familyMemberSubscriptions.clear();
837
+ };
838
+ const fillUnsubRequest = (key) => {
839
+ const unsubUnsub = familyMemberSubscriptions.get(`${key}:unsub`);
840
+ if (unsubUnsub) {
841
+ unsubUnsub();
842
+ familyMemberSubscriptions.delete(`${key}:unsub`);
843
+ }
844
+ const unsub = familyMemberSubscriptions.get(key);
845
+ if (unsub) {
846
+ unsub();
847
+ familyMemberSubscriptions.delete(key);
848
+ }
849
+ };
850
+ const exposeFamilyMembers = (subKey) => {
851
+ const token = findInStore(store, family, subKey);
852
+ getFromStore(store, token);
853
+ socket.emit(`serve:${token.key}`, getFromStore(store, token));
854
+ familyMemberSubscriptions.set(token.key, subscribeToState(store, token, `expose-family:${family.key}:${socket.id}`, ({ newValue }) => {
855
+ socket.emit(`serve:${token.key}`, newValue);
856
+ }));
857
+ familyMemberSubscriptions.set(`${token.key}:unsub`, employSocket(socket, `unsub:${token.key}`, () => {
858
+ store.logger.info(`🙈`, `user`, userKey, `unsubscribed from state "${token.key}"`);
859
+ fillUnsubRequest(token.key);
860
+ }));
861
+ };
862
+ const isAvailable$1 = (exposedSubKeys, subKey) => {
863
+ for (const exposedSubKey of exposedSubKeys) if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) return true;
864
+ return false;
865
+ };
866
+ const start = () => {
867
+ store.logger.info(`👀`, `user`, userKey, `can subscribe to family "${family.key}"`);
868
+ coreSubscriptions.add(employSocket(socket, `sub:${family.key}`, (subKey) => {
869
+ if (isAvailable$1(getFromStore(store, index), subKey)) {
870
+ store.logger.info(`👀`, `user`, userKey, `is approved for a subscription to`, subKey, `in family "${family.key}"`);
871
+ exposeFamilyMembers(subKey);
872
+ } else {
873
+ store.logger.info(`❌`, `user`, userKey, `is denied for a subscription to`, subKey, `in family "${family.key}"`);
874
+ familyMemberSubscriptionsWanted.add(stringifyJson(subKey));
875
+ socket.emit(`unavailable:${family.key}`, subKey);
876
+ }
877
+ }));
878
+ coreSubscriptions.add(subscribeToState(store, index, `expose-family:${family.key}:${socket.id}`, ({ newValue: newExposedSubKeys }) => {
879
+ store.logger.info(`👀`, `user`, userKey, `has the following keys available for family "${family.key}"`, newExposedSubKeys);
880
+ for (const subKey of newExposedSubKeys) if (familyMemberSubscriptionsWanted.has(stringifyJson(subKey))) {
881
+ store.logger.info(`👀`, `user`, userKey, `is retroactively approved for a subscription to`, subKey, `in family "${family.key}"`);
882
+ exposeFamilyMembers(subKey);
883
+ }
884
+ }));
885
+ };
886
+ start();
887
+ return () => {
888
+ clearCoreSubscriptions();
889
+ clearFamilySubscriptions();
890
+ };
891
+ };
892
+ }
893
+
894
+ //#endregion
895
+ //#region src/realtime-server/realtime-state-provider.ts
896
+ function isReadableToken(input) {
897
+ return typeof input === `object` && input !== null && `key` in input && `type` in input;
898
+ }
899
+ function realtimeStateProvider({ socket, userKey, store = IMPLICIT.STORE }) {
900
+ store.logger.info(`🔌`, `user`, userKey, `initialized state provider`);
901
+ return function stateProvider(clientToken, serverData = clientToken) {
902
+ const isStatic = !isReadableToken(serverData);
903
+ const subscriptions = /* @__PURE__ */ new Set();
904
+ const clearSubscriptions = () => {
905
+ for (const unsub of subscriptions) unsub();
906
+ subscriptions.clear();
907
+ };
908
+ const start = () => {
909
+ if (isStatic) store.logger.info(`👀`, `user`, userKey, `will be served`, serverData, `as "${clientToken.key}"`);
910
+ else store.logger.info(`👀`, `user`, userKey, `can subscribe to state "${serverData.key}" as "${clientToken.key}"`);
911
+ subscriptions.add(employSocket(socket, `sub:${clientToken.key}`, () => {
912
+ if (isStatic) {
913
+ store.logger.info(`👀`, `user`, userKey, `requests`, `"${clientToken.key}"`);
914
+ socket.emit(`serve:${clientToken.key}`, serverData);
915
+ } else {
916
+ store.logger.info(`👀`, `user`, userKey, `subscribes to state "${serverData.key}"`, clientToken === serverData ? `directly` : `as "${clientToken.key}"`);
917
+ clearSubscriptions();
918
+ socket.emit(`serve:${clientToken.key}`, getFromStore(store, serverData));
919
+ subscriptions.add(subscribeToState(store, serverData, `expose-single:${socket.id}`, ({ newValue }) => {
920
+ socket.emit(`serve:${clientToken.key}`, newValue);
921
+ }));
922
+ subscriptions.add(employSocket(socket, `unsub:${serverData.key}`, () => {
923
+ store.logger.info(`🙈`, `user`, userKey, `unsubscribes from state "${serverData.key}", served`, clientToken === serverData ? `directly` : `as "${clientToken.key}"`);
924
+ clearSubscriptions();
925
+ start();
926
+ }));
927
+ }
928
+ }));
929
+ };
930
+ start();
931
+ return clearSubscriptions;
932
+ };
933
+ }
934
+
971
935
  //#endregion
972
936
  //#region src/realtime-server/realtime-state-receiver.ts
973
- function realtimeStateReceiver({ socket, store = IMPLICIT.STORE }) {
974
- return function stateReceiver(clientToken, serverToken = clientToken) {
937
+ function realtimeStateReceiver({ socket, userKey, store = IMPLICIT.STORE }) {
938
+ return function stateReceiver(schema, clientToken, serverToken = clientToken) {
939
+ const socketKey = `socket::${socket.id}`;
975
940
  const mutexAtom = findInStore(store, mutexAtoms, serverToken.key);
976
941
  const subscriptions = /* @__PURE__ */ new Set();
977
942
  const clearSubscriptions = () => {
@@ -980,8 +945,13 @@ function realtimeStateReceiver({ socket, store = IMPLICIT.STORE }) {
980
945
  };
981
946
  const permitPublish = () => {
982
947
  clearSubscriptions();
983
- subscriptions.add(employSocket(socket, `pub:${clientToken.key}`, (newValue) => {
984
- setIntoStore(store, serverToken, newValue);
948
+ subscriptions.add(employSocket(socket, `pub:${clientToken.key}`, async (newValue) => {
949
+ const parsed = await schema[`~standard`].validate(newValue);
950
+ if (parsed.issues) {
951
+ store.logger.error(`❌`, `user`, userKey, `attempted to publish invalid value`, newValue, `to state "${serverToken.key}"`);
952
+ return;
953
+ }
954
+ setIntoStore(store, serverToken, parsed.value);
985
955
  }));
986
956
  subscriptions.add(employSocket(socket, `unclaim:${clientToken.key}`, () => {
987
957
  setIntoStore(store, mutexAtom, false);
@@ -993,7 +963,7 @@ function realtimeStateReceiver({ socket, store = IMPLICIT.STORE }) {
993
963
  subscriptions.add(employSocket(socket, `claim:${clientToken.key}`, () => {
994
964
  if (getFromStore(store, mutexAtom)) {
995
965
  clearSubscriptions();
996
- subscriptions.add(subscribeToState(store, mutexAtom, socket.id, () => {
966
+ subscriptions.add(subscribeToState(store, mutexAtom, socketKey, () => {
997
967
  if (getFromStore(store, mutexAtom) === false) {
998
968
  operateOnStore(OWN_OP, store, mutexAtom, true);
999
969
  permitPublish();
@@ -1014,5 +984,75 @@ function realtimeStateReceiver({ socket, store = IMPLICIT.STORE }) {
1014
984
  }
1015
985
 
1016
986
  //#endregion
1017
- export { ChildSocket, CustomSocket, ParentSocket, ROOMS, SubjectSocket, destroyRoom, prepareToProvideContinuity, provideEnterAndExit, provideIdentity, provideRooms, realtimeAtomFamilyProvider, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, roomMeta, selfListSelectors, socketAtoms, socketKeysAtom, spawnRoom, userKeysAtom, usersOfSockets };
987
+ //#region src/realtime-server/server-socket-state.ts
988
+ const socketAtoms = atomFamily({
989
+ key: `sockets`,
990
+ default: null
991
+ });
992
+ const socketKeysAtom = mutableAtom({
993
+ key: `socketsIndex`,
994
+ class: UList
995
+ });
996
+ const userKeysAtom = mutableAtom({
997
+ key: `usersIndex`,
998
+ class: UList
999
+ });
1000
+ const usersOfSockets = join({
1001
+ key: `usersOfSockets`,
1002
+ between: [`user`, `socket`],
1003
+ cardinality: `1:1`,
1004
+ isAType: isUserKey,
1005
+ isBType: isSocketKey
1006
+ });
1007
+
1008
+ //#endregion
1009
+ //#region src/realtime-server/server-config.ts
1010
+ function realtime(server, auth, onConnect, store = IMPLICIT.STORE) {
1011
+ const socketRealm = new Realm(store);
1012
+ server.use((socket, next) => {
1013
+ const result = auth(socket.handshake);
1014
+ if (result instanceof Error) {
1015
+ next(result);
1016
+ return;
1017
+ }
1018
+ const userClaim = socketRealm.allocate(`root`, result);
1019
+ const socketClaim = socketRealm.allocate(`root`, `socket::${socket.id}`);
1020
+ setIntoStore(store, findInStore(store, socketAtoms, socketClaim), socket);
1021
+ editRelationsInStore(store, usersOfSockets, (relations) => {
1022
+ relations.set(userClaim, socketClaim);
1023
+ });
1024
+ setIntoStore(store, userKeysAtom, (index) => index.add(userClaim));
1025
+ setIntoStore(store, socketKeysAtom, (index) => index.add(socketClaim));
1026
+ next();
1027
+ }).on(`connection`, (socket) => {
1028
+ const socketKey = `socket::${socket.id}`;
1029
+ const userKeyState = findRelationsInStore(store, usersOfSockets, socketKey).userKeyOfSocket;
1030
+ const userKey = getFromStore(store, userKeyState);
1031
+ const serverConfig = {
1032
+ store,
1033
+ socket,
1034
+ userKey
1035
+ };
1036
+ const unsubFromMyUserKey = realtimeStateProvider(serverConfig)({
1037
+ key: `myUserKey`,
1038
+ type: `atom`
1039
+ }, userKey);
1040
+ const disposeServices = onConnect(serverConfig);
1041
+ socket.on(`disconnect`, () => {
1042
+ store.logger.info(`📡`, `socket`, socketKey, `👤 ${userKey} disconnects`);
1043
+ disposeServices();
1044
+ unsubFromMyUserKey();
1045
+ editRelationsInStore(store, usersOfSockets, (rel) => rel.delete(socketKey));
1046
+ setIntoStore(store, userKeysAtom, (keys) => (keys.delete(userKey), keys));
1047
+ setIntoStore(store, socketKeysAtom, (keys) => (keys.delete(socketKey), keys));
1048
+ });
1049
+ });
1050
+ const disposeAll = async () => {
1051
+ await server.close();
1052
+ };
1053
+ return disposeAll;
1054
+ }
1055
+
1056
+ //#endregion
1057
+ export { ChildSocket, CustomSocket, ParentSocket, ROOMS, SubjectSocket, destroyRoom, prepareToProvideContinuity, provideEnterAndExit, provideRooms, realtime, realtimeAtomFamilyProvider, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, roomMeta, socketAtoms, socketKeysAtom, spawnRoom, userKeysAtom, usersOfSockets };
1018
1058
  //# sourceMappingURL=index.js.map