atom.io 0.46.0 → 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.
- package/dist/introspection/index.d.ts.map +1 -1
- package/dist/realtime/index.d.ts +6 -3
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js +9 -1
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime-server/index.d.ts +73 -50
- package/dist/realtime-server/index.d.ts.map +1 -1
- package/dist/realtime-server/index.js +222 -182
- package/dist/realtime-server/index.js.map +1 -1
- package/dist/realtime-testing/index.d.ts +1 -2
- package/dist/realtime-testing/index.d.ts.map +1 -1
- package/dist/realtime-testing/index.js +31 -53
- package/dist/realtime-testing/index.js.map +1 -1
- package/package.json +1 -1
- package/src/realtime/cast-socket.ts +1 -0
- package/src/realtime/shared-room-store.ts +15 -0
- package/src/realtime/socket-interface.ts +5 -1
- package/src/realtime-server/index.ts +3 -2
- package/src/realtime-server/ipc-sockets/custom-socket.ts +18 -2
- package/src/realtime-server/ipc-sockets/parent-socket.ts +1 -1
- package/src/realtime-server/{realtime-server-stores/provide-rooms.ts → provide-rooms.ts} +32 -57
- package/src/realtime-server/realtime-state-provider.ts +5 -3
- package/src/realtime-server/realtime-state-receiver.ts +19 -3
- package/src/realtime-server/server-config.ts +112 -1
- package/src/realtime-server/{realtime-server-stores/server-user-store.ts → server-socket-state.ts} +1 -8
- package/src/realtime-testing/setup-realtime-test.tsx +38 -83
- package/src/realtime-server/realtime-server-stores/index.ts +0 -3
- 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
|
|
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 =
|
|
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/
|
|
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
|
|
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
|
-
|
|
906
|
-
const
|
|
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,
|
|
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
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|