atom.io 0.40.6 → 0.40.8
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/README.md +1 -1
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +1 -2
- package/dist/data/index.js.map +1 -1
- package/dist/employ-socket-D6wgByWh.js +12 -0
- package/dist/employ-socket-D6wgByWh.js.map +1 -0
- package/dist/eslint-plugin/index.js.map +1 -1
- package/dist/has-role-CMlaUlaf.js +1133 -0
- package/dist/has-role-CMlaUlaf.js.map +1 -0
- package/dist/internal/index.d.ts +248 -248
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +590 -1803
- package/dist/internal/index.js.map +1 -1
- package/dist/introspection/index.d.ts +1 -1
- package/dist/introspection/index.d.ts.map +1 -1
- package/dist/introspection/index.js +13 -32
- package/dist/introspection/index.js.map +1 -1
- package/dist/is-fn-DY1wZ-md.js +10 -0
- package/dist/is-fn-DY1wZ-md.js.map +1 -0
- package/dist/json/index.d.ts.map +1 -1
- package/dist/json/index.js.map +1 -1
- package/dist/main/index.d.ts +33 -33
- package/dist/main/index.d.ts.map +1 -1
- package/dist/main/index.js +3 -4
- package/dist/main/index.js.map +1 -1
- package/dist/mutex-store-CSvxY9i3.js +11 -0
- package/dist/mutex-store-CSvxY9i3.js.map +1 -0
- package/dist/react/index.d.ts +5 -5
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react-devtools/index.d.ts.map +1 -1
- package/dist/react-devtools/index.js +9 -11
- package/dist/react-devtools/index.js.map +1 -1
- package/dist/realtime/index.d.ts +7 -15
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js +4 -35
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime-client/index.d.ts +6 -9
- package/dist/realtime-client/index.d.ts.map +1 -1
- package/dist/realtime-client/index.js +96 -88
- package/dist/realtime-client/index.js.map +1 -1
- package/dist/realtime-react/index.d.ts +17 -13
- package/dist/realtime-react/index.d.ts.map +1 -1
- package/dist/realtime-react/index.js +39 -50
- package/dist/realtime-react/index.js.map +1 -1
- package/dist/realtime-server/index.d.ts +83 -84
- package/dist/realtime-server/index.d.ts.map +1 -1
- package/dist/realtime-server/index.js +604 -543
- package/dist/realtime-server/index.js.map +1 -1
- package/dist/realtime-testing/index.d.ts +5 -4
- package/dist/realtime-testing/index.d.ts.map +1 -1
- package/dist/realtime-testing/index.js +35 -22
- package/dist/realtime-testing/index.js.map +1 -1
- package/dist/shared-room-store-BfW3nWif.js +31 -0
- package/dist/shared-room-store-BfW3nWif.js.map +1 -0
- package/dist/shared-room-store-D2o4ZLjC.d.ts +15 -0
- package/dist/shared-room-store-D2o4ZLjC.d.ts.map +1 -0
- package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
- package/dist/transceivers/set-rtx/index.js +4 -8
- package/dist/transceivers/set-rtx/index.js.map +1 -1
- package/dist/web/index.d.ts +3 -3
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +4 -3
- package/dist/web/index.js.map +1 -1
- package/package.json +13 -13
- package/src/internal/atom/create-regular-atom.ts +5 -4
- package/src/internal/atom/dispose-atom.ts +7 -2
- package/src/internal/atom/has-role.ts +3 -3
- package/src/internal/caching.ts +4 -2
- package/src/internal/families/create-readonly-held-selector-family.ts +2 -1
- package/src/internal/families/create-readonly-pure-selector-family.ts +5 -2
- package/src/internal/families/create-regular-atom-family.ts +2 -1
- package/src/internal/families/create-writable-held-selector-family.ts +2 -1
- package/src/internal/families/create-writable-pure-selector-family.ts +5 -2
- package/src/internal/families/dispose-from-store.ts +4 -4
- package/src/internal/families/find-in-store.ts +10 -10
- package/src/internal/families/get-family-of-token.ts +2 -2
- package/src/internal/families/index.ts +1 -0
- package/src/internal/families/mint-in-store.ts +54 -19
- package/src/internal/families/seek-in-store.ts +1 -1
- package/src/internal/get-state/get-fallback.ts +2 -2
- package/src/internal/get-state/get-from-store.ts +5 -5
- package/src/internal/get-state/read-or-compute-value.ts +1 -1
- package/src/internal/get-state/reduce-reference.ts +8 -6
- package/src/internal/index.ts +2 -220
- package/src/internal/molecule.ts +1 -2
- package/src/internal/mutable/create-mutable-atom-family.ts +3 -2
- package/src/internal/mutable/create-mutable-atom.ts +4 -2
- package/src/internal/mutable/get-json-family.ts +1 -1
- package/src/internal/mutable/get-update-family.ts +1 -1
- package/src/internal/mutable/tracker-family.ts +2 -1
- package/src/internal/mutable/tracker.ts +71 -59
- package/src/internal/safe-compute.ts +1 -1
- package/src/internal/selector/create-readonly-held-selector.ts +2 -1
- package/src/internal/selector/create-readonly-pure-selector.ts +2 -1
- package/src/internal/selector/create-writable-held-selector.ts +2 -1
- package/src/internal/selector/create-writable-pure-selector.ts +2 -1
- package/src/internal/selector/dispose-selector.ts +3 -2
- package/src/internal/selector/register-selector.ts +8 -5
- package/src/internal/selector/trace-selector-atoms.ts +2 -1
- package/src/internal/set-state/dispatch-state-update.ts +3 -2
- package/src/internal/set-state/evict-downstream.ts +1 -1
- package/src/internal/set-state/operate-on-store.ts +16 -22
- package/src/internal/set-state/reset-atom-or-selector.ts +5 -3
- package/src/internal/set-state/reset-in-store.ts +5 -5
- package/src/internal/set-state/set-atom-or-selector.ts +2 -2
- package/src/internal/set-state/set-atom.ts +4 -2
- package/src/internal/set-state/set-into-store.ts +21 -39
- package/src/internal/set-state/set-selector.ts +3 -2
- package/src/internal/state-types.ts +228 -0
- package/src/internal/store/deposit.ts +4 -4
- package/src/internal/store/index.ts +0 -1
- package/src/internal/store/store.ts +9 -9
- package/src/internal/store/withdraw.ts +4 -4
- package/src/internal/subscribe/recall-state.ts +1 -1
- package/src/internal/subscribe/subscribe-to-root-atoms.ts +1 -12
- package/src/internal/subscribe/subscribe-to-state.ts +9 -0
- package/src/internal/subscribe/subscribe-to-transaction.ts +3 -2
- package/src/internal/transaction/build-transaction.ts +3 -2
- package/src/internal/transaction/index.ts +1 -23
- package/src/internal/transaction/is-root-store.ts +4 -1
- package/src/internal/transaction/transaction-meta-progress.ts +22 -0
- package/src/main/atom.ts +1 -2
- package/src/main/find-state.ts +5 -5
- package/src/main/get-state.ts +4 -4
- package/src/main/realm.ts +2 -2
- package/src/main/set-state.ts +10 -10
- package/src/react/parse-state-overloads.ts +3 -3
- package/src/react/use-i.ts +6 -4
- package/src/react/use-loadable.ts +4 -10
- package/src/react/use-o.ts +6 -4
- package/src/react-devtools/store.ts +6 -6
- package/src/realtime/index.ts +1 -0
- package/src/realtime/mutex-store.ts +11 -0
- package/src/realtime/realtime-continuity.ts +1 -5
- package/src/realtime-client/index.ts +0 -1
- package/src/realtime-client/pull-atom-family-member.ts +14 -17
- package/src/realtime-client/pull-atom.ts +1 -1
- package/src/realtime-client/pull-mutable-atom-family-member.ts +16 -12
- package/src/realtime-client/pull-selector-family-member.ts +8 -35
- package/src/realtime-client/pull-selector-roots.ts +90 -0
- package/src/realtime-client/pull-selector.ts +2 -27
- package/src/realtime-client/push-state.ts +33 -5
- package/src/realtime-client/realtime-client-stores/client-main-store.ts +2 -5
- package/src/realtime-react/index.ts +2 -2
- package/src/realtime-react/realtime-context.tsx +9 -5
- package/src/realtime-react/use-pull-atom-family-member.ts +2 -3
- package/src/realtime-react/use-pull-mutable-family-member.ts +2 -3
- package/src/realtime-react/use-pull-selector-family-member.ts +5 -6
- package/src/realtime-react/use-push.ts +7 -3
- package/src/realtime-react/use-realtime-service.ts +11 -11
- package/src/realtime-react/use-single-effect.ts +11 -14
- package/src/realtime-server/{realtime-server-stores/server-sync-store.ts → continuity/continuity-store.ts} +2 -27
- package/src/realtime-server/continuity/provide-continuity.ts +50 -0
- package/src/realtime-server/continuity/{subscribe-to-continuity-actions.ts → provide-outcomes.ts} +15 -13
- package/src/realtime-server/continuity/{subscribe-to-continuity-perpectives.ts → provide-perspectives.ts} +10 -8
- package/src/realtime-server/continuity/{prepare-to-send-initial-payload.ts → provide-startup-payloads.ts} +6 -4
- package/src/realtime-server/continuity/receive-action-requests.ts +68 -0
- package/src/realtime-server/continuity/track-acknowledgements.ts +46 -0
- package/src/realtime-server/employ-socket.ts +14 -0
- package/src/realtime-server/index.ts +3 -22
- package/src/realtime-server/ipc-sockets/child-socket.ts +125 -66
- package/src/realtime-server/ipc-sockets/custom-socket.ts +16 -14
- package/src/realtime-server/ipc-sockets/parent-socket.ts +98 -69
- package/src/realtime-server/realtime-family-provider.ts +78 -29
- package/src/realtime-server/realtime-mutable-family-provider.ts +80 -31
- package/src/realtime-server/realtime-mutable-provider.ts +30 -22
- package/src/realtime-server/realtime-server-stores/index.ts +0 -2
- package/src/realtime-server/realtime-server-stores/server-room-external-store.ts +77 -36
- package/src/realtime-server/realtime-server-stores/server-user-store.ts +12 -1
- package/src/realtime-server/realtime-state-provider.ts +30 -29
- package/src/realtime-server/realtime-state-receiver.ts +62 -16
- package/src/realtime-server/server-config.ts +8 -0
- package/src/realtime-server/socket-interface.ts +14 -0
- package/src/realtime-testing/setup-realtime-test.tsx +70 -31
- package/src/web/index.ts +1 -1
- package/src/web/{persist-sync.ts → storage-sync.ts} +5 -2
- package/src/internal/store/mint-or-counterfeit.ts +0 -108
- package/src/realtime-client/server-action.ts +0 -23
- package/src/realtime-react/on-mount.ts +0 -5
- package/src/realtime-react/use-server-action.ts +0 -19
- package/src/realtime-server/continuity/prepare-to-serve-transaction-request.ts +0 -59
- package/src/realtime-server/continuity/prepare-to-sync-realtime-continuity.ts +0 -145
- package/src/realtime-server/continuity/prepare-to-track-client-acknowledgement.ts +0 -41
- package/src/realtime-server/realtime-action-receiver.ts +0 -40
- package/src/realtime-server/realtime-server-stores/server-room-external-actions.ts +0 -79
|
@@ -1,19 +1,234 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OWN_OP, operateOnStore } from "../has-role-CMlaUlaf.js";
|
|
2
|
+
import "../is-fn-DY1wZ-md.js";
|
|
3
|
+
import { mutexAtoms } from "../mutex-store-CSvxY9i3.js";
|
|
4
|
+
import { roomIndex, usersInRooms } from "../shared-room-store-BfW3nWif.js";
|
|
5
|
+
import { employSocket } from "../employ-socket-D6wgByWh.js";
|
|
6
|
+
import { IMPLICIT, Subject, actUponStore, editRelationsInStore, findInStore, getFromStore, getJsonToken, getUpdateToken, isRootStore, setIntoStore, subscribeToState, subscribeToTransaction } from "atom.io/internal";
|
|
7
|
+
import { atomFamily, join, mutableAtom, selectorFamily, transaction } from "atom.io";
|
|
2
8
|
import { parseJson, stringifyJson } from "atom.io/json";
|
|
3
|
-
import * as AtomIO from "atom.io";
|
|
4
|
-
import { atomFamily, join, mutableAtom, selectorFamily } from "atom.io";
|
|
5
9
|
import { SetRTX } from "atom.io/transceivers/set-rtx";
|
|
6
|
-
import { roomIndex, usersInRooms } from "atom.io/realtime";
|
|
7
10
|
import { spawn } from "node:child_process";
|
|
8
11
|
|
|
12
|
+
//#region src/realtime-server/continuity/continuity-store.ts
|
|
13
|
+
function redactTransactionUpdateContent(visibleStateKeys, updates) {
|
|
14
|
+
return updates.map((update) => {
|
|
15
|
+
switch (update.type) {
|
|
16
|
+
case `transaction_outcome`: {
|
|
17
|
+
const redacted = redactTransactionUpdateContent(visibleStateKeys, update.subEvents);
|
|
18
|
+
return {
|
|
19
|
+
...update,
|
|
20
|
+
subEvents: redacted
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
case `atom_update`:
|
|
24
|
+
case `molecule_creation`:
|
|
25
|
+
case `molecule_disposal`:
|
|
26
|
+
case `molecule_transfer`:
|
|
27
|
+
case `state_creation`:
|
|
28
|
+
case `state_disposal`: return update;
|
|
29
|
+
}
|
|
30
|
+
}).filter((update) => {
|
|
31
|
+
switch (update.type) {
|
|
32
|
+
case `atom_update`:
|
|
33
|
+
case `state_creation`:
|
|
34
|
+
case `state_disposal`: return visibleStateKeys.includes(update.token.key);
|
|
35
|
+
case `molecule_creation`:
|
|
36
|
+
case `transaction_outcome`:
|
|
37
|
+
case `molecule_disposal`:
|
|
38
|
+
case `molecule_transfer`: return true;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const redactorAtoms = atomFamily({
|
|
43
|
+
key: `redactor`,
|
|
44
|
+
default: { occlude: (updates) => updates }
|
|
45
|
+
});
|
|
46
|
+
const unacknowledgedUpdatesAtoms = atomFamily({
|
|
47
|
+
key: `unacknowledgedUpdates`,
|
|
48
|
+
default: () => []
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/realtime-server/continuity/provide-outcomes.ts
|
|
53
|
+
function provideOutcomes(store, socket, continuity, userKey) {
|
|
54
|
+
const continuityKey = continuity.key;
|
|
55
|
+
const unsubscribeFunctions = /* @__PURE__ */ new Set();
|
|
56
|
+
for (const transaction$1 of continuity.actions) {
|
|
57
|
+
const unsubscribeFromTransaction = subscribeToTransaction(store, transaction$1, `sync-continuity:${continuityKey}:${userKey}`, (outcomes) => {
|
|
58
|
+
try {
|
|
59
|
+
const visibleKeys = continuity.globals.map((atom) => {
|
|
60
|
+
if (atom.type === `atom`) return atom.key;
|
|
61
|
+
return getUpdateToken(atom).key;
|
|
62
|
+
}).concat(continuity.perspectives.flatMap((perspective) => {
|
|
63
|
+
const { viewAtoms } = perspective;
|
|
64
|
+
const userPerspectiveTokenState = findInStore(store, viewAtoms, userKey);
|
|
65
|
+
return getFromStore(store, userPerspectiveTokenState).map((token) => {
|
|
66
|
+
return token.type === `mutable_atom` ? `*` + token.key : token.key;
|
|
67
|
+
});
|
|
68
|
+
}));
|
|
69
|
+
const redactedUpdates = redactTransactionUpdateContent(visibleKeys, outcomes.subEvents);
|
|
70
|
+
const redactedUpdate = {
|
|
71
|
+
...outcomes,
|
|
72
|
+
updates: redactedUpdates
|
|
73
|
+
};
|
|
74
|
+
setIntoStore(store, unacknowledgedUpdatesAtoms, userKey, (updates) => {
|
|
75
|
+
if (redactedUpdate) {
|
|
76
|
+
updates.push(redactedUpdate);
|
|
77
|
+
updates.sort((a, b) => a.epoch - b.epoch);
|
|
78
|
+
store.logger.info(`👍`, `continuity`, continuityKey, `${userKey} unacknowledged update queue now has`, updates.length, `items`);
|
|
79
|
+
}
|
|
80
|
+
return updates;
|
|
81
|
+
});
|
|
82
|
+
socket.emit(`tx-new:${continuityKey}`, redactedUpdate);
|
|
83
|
+
} catch (thrown) {
|
|
84
|
+
if (thrown instanceof Error) store.logger.error(`❌`, `continuity`, continuityKey, `${userKey} failed to send update from transaction ${transaction$1.key} to ${userKey}`, thrown.message);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
unsubscribeFunctions.add(unsubscribeFromTransaction);
|
|
88
|
+
}
|
|
89
|
+
return () => {
|
|
90
|
+
for (const unsubscribe of unsubscribeFunctions) unsubscribe();
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/realtime-server/continuity/provide-perspectives.ts
|
|
96
|
+
function providePerspectives(store, socket, continuity, userKey) {
|
|
97
|
+
const continuityKey = continuity.key;
|
|
98
|
+
const unsubFns = /* @__PURE__ */ new Set();
|
|
99
|
+
for (const perspective of continuity.perspectives) {
|
|
100
|
+
const { viewAtoms } = perspective;
|
|
101
|
+
const userViewState = findInStore(store, viewAtoms, userKey);
|
|
102
|
+
const unsubscribeFromUserView = subscribeToState(store, userViewState, `sync-continuity:${continuityKey}:${userKey}:perspective:${perspective.resourceAtoms.key}`, ({ oldValue, newValue }) => {
|
|
103
|
+
const oldKeys = oldValue?.map((token) => token.key);
|
|
104
|
+
const newKeys = newValue.map((token) => token.key);
|
|
105
|
+
const concealed = oldValue?.filter((token) => !newKeys.includes(token.key));
|
|
106
|
+
const revealed = newValue.filter((token) => !oldKeys?.includes(token.key)).flatMap((token) => {
|
|
107
|
+
const resourceToken = token.type === `mutable_atom` ? getJsonToken(store, token) : token;
|
|
108
|
+
const resource = getFromStore(store, resourceToken);
|
|
109
|
+
return [resourceToken, resource];
|
|
110
|
+
});
|
|
111
|
+
store.logger.info(`👁`, `atom`, perspective.resourceAtoms.key, `${userKey} has a new perspective`, {
|
|
112
|
+
oldKeys,
|
|
113
|
+
newKeys,
|
|
114
|
+
revealed,
|
|
115
|
+
concealed
|
|
116
|
+
});
|
|
117
|
+
if (revealed.length > 0) socket.emit(`reveal:${continuityKey}`, revealed);
|
|
118
|
+
if (concealed && concealed.length > 0) socket.emit(`conceal:${continuityKey}`, concealed);
|
|
119
|
+
});
|
|
120
|
+
unsubFns.add(unsubscribeFromUserView);
|
|
121
|
+
}
|
|
122
|
+
return () => {
|
|
123
|
+
for (const unsubscribe of unsubFns) unsubscribe();
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/realtime-server/continuity/provide-startup-payloads.ts
|
|
129
|
+
function provideStartupPayloads(store, socket, continuity, userKey) {
|
|
130
|
+
const continuityKey = continuity.key;
|
|
131
|
+
function sendInitialPayload() {
|
|
132
|
+
const initialPayload = [];
|
|
133
|
+
for (const atom of continuity.globals) {
|
|
134
|
+
const resourceToken = atom.type === `mutable_atom` ? getJsonToken(store, atom) : atom;
|
|
135
|
+
const resource = getFromStore(store, resourceToken);
|
|
136
|
+
initialPayload.push(resourceToken, resource);
|
|
137
|
+
}
|
|
138
|
+
for (const perspective of continuity.perspectives) {
|
|
139
|
+
const { viewAtoms, resourceAtoms } = perspective;
|
|
140
|
+
const userViewState = findInStore(store, viewAtoms, userKey);
|
|
141
|
+
const userView = getFromStore(store, userViewState);
|
|
142
|
+
store.logger.info(`👁`, `atom`, resourceAtoms.key, `${userKey} can see`, {
|
|
143
|
+
viewAtoms,
|
|
144
|
+
resourceAtoms,
|
|
145
|
+
userView
|
|
146
|
+
});
|
|
147
|
+
for (const visibleToken of userView) {
|
|
148
|
+
const resourceToken = visibleToken.type === `mutable_atom` ? getJsonToken(store, visibleToken) : visibleToken;
|
|
149
|
+
const resource = getFromStore(store, resourceToken);
|
|
150
|
+
initialPayload.push(resourceToken, resource);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const epoch = isRootStore(store) ? store.transactionMeta.epoch.get(continuityKey) ?? null : null;
|
|
154
|
+
socket.emit(`continuity-init:${continuityKey}`, epoch, initialPayload);
|
|
155
|
+
}
|
|
156
|
+
return employSocket(socket, `get:${continuityKey}`, sendInitialPayload);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/realtime-server/continuity/receive-action-requests.ts
|
|
161
|
+
function receiveActionRequests(store, socket, continuity, userKey) {
|
|
162
|
+
const continuityKey = continuity.key;
|
|
163
|
+
return employSocket(socket, `tx-run:${continuityKey}`, function serveTransactionRequest(txOutcome) {
|
|
164
|
+
store.logger.info(`🛎️`, `continuity`, continuityKey, `received`, txOutcome);
|
|
165
|
+
const transactionKey = txOutcome.token.key;
|
|
166
|
+
const updateId = txOutcome.id;
|
|
167
|
+
const performanceKey = `tx-run:${transactionKey}:${updateId}`;
|
|
168
|
+
const performanceKeyStart = `${performanceKey}:start`;
|
|
169
|
+
const performanceKeyEnd = `${performanceKey}:end`;
|
|
170
|
+
performance.mark(performanceKeyStart);
|
|
171
|
+
try {
|
|
172
|
+
actUponStore(store, {
|
|
173
|
+
type: `transaction`,
|
|
174
|
+
key: transactionKey
|
|
175
|
+
}, updateId)(...txOutcome.params);
|
|
176
|
+
} catch (thrown) {
|
|
177
|
+
if (thrown instanceof Error) store.logger.error(`❌`, `continuity`, continuityKey, `failed to run transaction ${transactionKey} from ${userKey} with update ${updateId}`, thrown.message);
|
|
178
|
+
}
|
|
179
|
+
performance.mark(performanceKeyEnd);
|
|
180
|
+
const metric = performance.measure(performanceKey, performanceKeyStart, performanceKeyEnd);
|
|
181
|
+
store.logger.info(`🚀`, `transaction`, transactionKey, updateId, userKey, metric.duration);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/realtime-server/continuity/track-acknowledgements.ts
|
|
187
|
+
function trackAcknowledgements(store, socket, continuity, userKey) {
|
|
188
|
+
const continuityKey = continuity.key;
|
|
189
|
+
const userUnacknowledgedUpdates = getFromStore(store, unacknowledgedUpdatesAtoms, userKey);
|
|
190
|
+
function trackClientAcknowledgement(epoch) {
|
|
191
|
+
store.logger.info(`👍`, `continuity`, continuityKey, `${userKey} acknowledged epoch ${epoch}`);
|
|
192
|
+
if (userUnacknowledgedUpdates[0]?.epoch === epoch) setIntoStore(store, unacknowledgedUpdatesAtoms, userKey, (updates) => {
|
|
193
|
+
updates.shift();
|
|
194
|
+
store.logger.info(`👍`, `continuity`, continuityKey, `${userKey} unacknowledged update queue now has`, updates.length, `items`);
|
|
195
|
+
return updates;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return employSocket(socket, `ack:${continuityKey}`, trackClientAcknowledgement);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/realtime-server/continuity/provide-continuity.ts
|
|
203
|
+
function prepareToProvideContinuity({ socket, store = IMPLICIT.STORE }) {
|
|
204
|
+
return function syncRealtimeContinuity(continuity, userKey) {
|
|
205
|
+
const continuityKey = continuity.key;
|
|
206
|
+
const unacknowledgedUpdates = getFromStore(store, unacknowledgedUpdatesAtoms, userKey);
|
|
207
|
+
for (const unacknowledgedUpdate of unacknowledgedUpdates) socket.emit(`tx-new:${continuityKey}`, unacknowledgedUpdate);
|
|
208
|
+
const subscriptions = /* @__PURE__ */ new Set();
|
|
209
|
+
const clearSubscriptions = () => {
|
|
210
|
+
for (const unsubscribe of subscriptions) unsubscribe();
|
|
211
|
+
subscriptions.clear();
|
|
212
|
+
};
|
|
213
|
+
subscriptions.add(providePerspectives(store, socket, continuity, userKey));
|
|
214
|
+
subscriptions.add(provideOutcomes(store, socket, continuity, userKey));
|
|
215
|
+
subscriptions.add(provideStartupPayloads(store, socket, continuity, userKey));
|
|
216
|
+
subscriptions.add(receiveActionRequests(store, socket, continuity, userKey));
|
|
217
|
+
subscriptions.add(trackAcknowledgements(store, socket, continuity, userKey));
|
|
218
|
+
return clearSubscriptions;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
//#endregion
|
|
9
223
|
//#region src/realtime-server/ipc-sockets/custom-socket.ts
|
|
10
224
|
var CustomSocket = class {
|
|
11
225
|
listeners;
|
|
12
226
|
globalListeners;
|
|
13
|
-
handleEvent(
|
|
14
|
-
|
|
227
|
+
handleEvent(...args) {
|
|
228
|
+
const [event, ...rest] = args;
|
|
229
|
+
for (const listener of this.globalListeners) listener(event, ...rest);
|
|
15
230
|
const listeners = this.listeners.get(event);
|
|
16
|
-
if (listeners) for (const listener of listeners) listener(...
|
|
231
|
+
if (listeners) for (const listener of listeners) listener(...rest);
|
|
17
232
|
}
|
|
18
233
|
id = `no_id_retrieved`;
|
|
19
234
|
emit;
|
|
@@ -52,12 +267,12 @@ var ChildSocket = class extends CustomSocket {
|
|
|
52
267
|
incompleteLog = ``;
|
|
53
268
|
unprocessedLogs = [];
|
|
54
269
|
id = `#####`;
|
|
55
|
-
|
|
270
|
+
proc;
|
|
56
271
|
key;
|
|
57
272
|
logger;
|
|
58
|
-
handleLog(
|
|
59
|
-
if (Array.isArray(
|
|
60
|
-
const [level, ...rest] =
|
|
273
|
+
handleLog(log) {
|
|
274
|
+
if (Array.isArray(log)) {
|
|
275
|
+
const [level, ...rest] = log;
|
|
61
276
|
switch (level) {
|
|
62
277
|
case `i`:
|
|
63
278
|
this.logger.info(...rest);
|
|
@@ -68,22 +283,21 @@ var ChildSocket = class extends CustomSocket {
|
|
|
68
283
|
case `e`:
|
|
69
284
|
this.logger.error(...rest);
|
|
70
285
|
break;
|
|
71
|
-
default: return;
|
|
72
286
|
}
|
|
73
287
|
}
|
|
74
288
|
}
|
|
75
|
-
constructor(
|
|
289
|
+
constructor(proc, key, logger) {
|
|
76
290
|
super((event, ...args) => {
|
|
77
291
|
const stringifiedEvent = JSON.stringify([event, ...args]) + `\x03`;
|
|
78
292
|
const errorHandler = (err) => {
|
|
79
|
-
if (err.code === `EPIPE`) console.error(`EPIPE error during write`, this.
|
|
80
|
-
this.
|
|
293
|
+
if (err.code === `EPIPE`) console.error(`EPIPE error during write`, this.proc.stdin);
|
|
294
|
+
this.proc.stdin.removeListener(`error`, errorHandler);
|
|
81
295
|
};
|
|
82
|
-
this.
|
|
83
|
-
this.
|
|
296
|
+
this.proc.stdin.once(`error`, errorHandler);
|
|
297
|
+
this.proc.stdin.write(stringifiedEvent);
|
|
84
298
|
return this;
|
|
85
299
|
});
|
|
86
|
-
this.
|
|
300
|
+
this.proc = proc;
|
|
87
301
|
this.key = key;
|
|
88
302
|
this.logger = logger ?? {
|
|
89
303
|
info: (...args) => {
|
|
@@ -96,55 +310,90 @@ var ChildSocket = class extends CustomSocket {
|
|
|
96
310
|
console.error(this.id, this.key, ...args);
|
|
97
311
|
}
|
|
98
312
|
};
|
|
99
|
-
this.
|
|
313
|
+
this.proc.stdout.on(`data`, (buffer) => {
|
|
100
314
|
const chunk = buffer.toString();
|
|
101
|
-
if (chunk === `ALIVE`)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
315
|
+
if (chunk === `ALIVE`) {
|
|
316
|
+
this.logger.info(chunk);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const pieces = chunk.split(`\x03`);
|
|
320
|
+
const initialMaybeWellFormed = pieces[0];
|
|
321
|
+
pieces[0] = this.incompleteData + initialMaybeWellFormed;
|
|
322
|
+
let idx = 0;
|
|
323
|
+
for (const piece of pieces) {
|
|
324
|
+
if (piece === ``) continue;
|
|
325
|
+
try {
|
|
326
|
+
const jsonPiece = parseJson(piece);
|
|
327
|
+
this.handleEvent(...jsonPiece);
|
|
328
|
+
this.incompleteData = ``;
|
|
329
|
+
} catch (thrown0) {
|
|
330
|
+
if (thrown0 instanceof Error) console.error([
|
|
331
|
+
`❌ Malformed data received from child process`,
|
|
332
|
+
``,
|
|
333
|
+
piece,
|
|
334
|
+
``,
|
|
335
|
+
thrown0.message
|
|
336
|
+
].join(`\n❌\t`));
|
|
337
|
+
try {
|
|
338
|
+
if (idx === 0) {
|
|
339
|
+
this.incompleteData = piece;
|
|
340
|
+
const maybeActualJsonPiece = parseJson(initialMaybeWellFormed);
|
|
341
|
+
this.handleEvent(...maybeActualJsonPiece);
|
|
342
|
+
this.incompleteData = ``;
|
|
343
|
+
} else this.incompleteData += piece;
|
|
344
|
+
} catch (thrown1) {
|
|
345
|
+
if (thrown1 instanceof Error) console.error([
|
|
346
|
+
`❌ Malformed data received from child process`,
|
|
347
|
+
``,
|
|
348
|
+
initialMaybeWellFormed,
|
|
349
|
+
``,
|
|
350
|
+
thrown1.message
|
|
351
|
+
].join(`\n❌\t`));
|
|
115
352
|
}
|
|
116
353
|
}
|
|
117
|
-
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.warn(`⚠️----------------⚠️`);
|
|
120
|
-
console.warn(this.incompleteData);
|
|
121
|
-
console.warn(`⚠️----------------⚠️`);
|
|
122
|
-
console.error(error);
|
|
354
|
+
++idx;
|
|
123
355
|
}
|
|
124
356
|
});
|
|
125
|
-
this.
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
this.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
357
|
+
this.proc.stderr.on(`data`, (buffer) => {
|
|
358
|
+
const pieces = buffer.toString().split(`\x03`);
|
|
359
|
+
const initialMaybeWellFormed = pieces[0];
|
|
360
|
+
pieces[0] = this.incompleteData + initialMaybeWellFormed;
|
|
361
|
+
let idx = 0;
|
|
362
|
+
for (const piece of pieces) {
|
|
363
|
+
if (piece === ``) continue;
|
|
364
|
+
try {
|
|
365
|
+
const jsonPiece = parseJson(piece);
|
|
366
|
+
this.handleLog(jsonPiece);
|
|
367
|
+
this.incompleteData = ``;
|
|
368
|
+
} catch (thrown0) {
|
|
369
|
+
if (thrown0 instanceof Error) console.error([
|
|
370
|
+
`❌ Malformed log received from child process`,
|
|
371
|
+
``,
|
|
372
|
+
piece,
|
|
373
|
+
``,
|
|
374
|
+
thrown0.message
|
|
375
|
+
].join(`\n❌\t`));
|
|
376
|
+
try {
|
|
377
|
+
if (idx === 0) {
|
|
378
|
+
this.incompleteData = piece;
|
|
379
|
+
const maybeActualJsonPiece = parseJson(initialMaybeWellFormed);
|
|
380
|
+
this.handleLog(maybeActualJsonPiece);
|
|
381
|
+
this.incompleteData = ``;
|
|
382
|
+
} else this.incompleteData += piece;
|
|
383
|
+
} catch (thrown1) {
|
|
384
|
+
if (thrown1 instanceof Error) console.error([
|
|
385
|
+
`❌ Malformed log received from child process...`,
|
|
386
|
+
``,
|
|
387
|
+
initialMaybeWellFormed,
|
|
388
|
+
``,
|
|
389
|
+
thrown1.message
|
|
390
|
+
].join(`\n❌\t`));
|
|
138
391
|
}
|
|
139
392
|
}
|
|
140
|
-
|
|
141
|
-
console.error(`❌❌❌`);
|
|
142
|
-
console.error(this.incompleteLog);
|
|
143
|
-
console.error(error);
|
|
144
|
-
console.error(`❌❌❌️`);
|
|
393
|
+
++idx;
|
|
145
394
|
}
|
|
146
395
|
});
|
|
147
|
-
if (
|
|
396
|
+
if (proc.pid) this.id = proc.pid.toString();
|
|
148
397
|
}
|
|
149
398
|
};
|
|
150
399
|
|
|
@@ -176,10 +425,10 @@ var ParentSocket = class extends CustomSocket {
|
|
|
176
425
|
unprocessedEvents = [];
|
|
177
426
|
relays;
|
|
178
427
|
relayServices;
|
|
179
|
-
|
|
428
|
+
proc;
|
|
180
429
|
id = `#####`;
|
|
181
430
|
log(...args) {
|
|
182
|
-
this.
|
|
431
|
+
this.proc.stderr.write(stringifyJson(args.map((arg) => arg instanceof SetRTX ? `{ ${arg.toJSON().members.join(` | `)} }` : arg)) + `\x03`);
|
|
183
432
|
}
|
|
184
433
|
logger = {
|
|
185
434
|
info: (...args) => {
|
|
@@ -192,68 +441,73 @@ var ParentSocket = class extends CustomSocket {
|
|
|
192
441
|
this.log(`e`, ...args);
|
|
193
442
|
}
|
|
194
443
|
};
|
|
195
|
-
constructor() {
|
|
444
|
+
constructor(proc) {
|
|
196
445
|
super((event, ...args) => {
|
|
197
446
|
const stringifiedEvent = JSON.stringify([event, ...args]);
|
|
198
|
-
this.
|
|
447
|
+
this.proc.stdout.write(stringifiedEvent + `\x03`);
|
|
199
448
|
return this;
|
|
200
449
|
});
|
|
201
|
-
this.
|
|
202
|
-
this.
|
|
450
|
+
this.proc = proc;
|
|
451
|
+
this.proc.stdin.resume();
|
|
203
452
|
this.relays = /* @__PURE__ */ new Map();
|
|
204
453
|
this.relayServices = [];
|
|
205
|
-
this.
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
454
|
+
this.proc.stdin.on(`data`, (buffer) => {
|
|
455
|
+
const pieces = buffer.toString().split(`\x03`);
|
|
456
|
+
const initialMaybeWellFormed = pieces[0];
|
|
457
|
+
pieces[0] = this.incompleteData + initialMaybeWellFormed;
|
|
458
|
+
let idx = 0;
|
|
459
|
+
for (const piece of pieces) {
|
|
460
|
+
if (piece === ``) continue;
|
|
461
|
+
try {
|
|
462
|
+
const jsonPiece = parseJson(piece);
|
|
463
|
+
this.logger.info(`🎰`, `received`, jsonPiece);
|
|
464
|
+
this.handleEvent(...jsonPiece);
|
|
465
|
+
this.incompleteData = ``;
|
|
466
|
+
} catch (thrown0) {
|
|
467
|
+
if (thrown0 instanceof Error) this.logger.error([
|
|
468
|
+
`received malformed data from parent process:`,
|
|
469
|
+
``,
|
|
470
|
+
piece,
|
|
471
|
+
``,
|
|
472
|
+
thrown0.message
|
|
473
|
+
].join(`\n❌\t`));
|
|
474
|
+
try {
|
|
475
|
+
if (idx === 0) {
|
|
476
|
+
this.incompleteData = piece;
|
|
477
|
+
const maybeActualJsonPiece = parseJson(initialMaybeWellFormed);
|
|
478
|
+
this.logger.info(`🎰`, `received`, maybeActualJsonPiece);
|
|
479
|
+
this.handleEvent(...maybeActualJsonPiece);
|
|
480
|
+
this.incompleteData = ``;
|
|
481
|
+
} else this.incompleteData += piece;
|
|
482
|
+
} catch (thrown1) {
|
|
483
|
+
if (thrown1 instanceof Error) this.logger.error([
|
|
484
|
+
`received malformed data from parent process:`,
|
|
485
|
+
``,
|
|
486
|
+
initialMaybeWellFormed,
|
|
487
|
+
``,
|
|
488
|
+
thrown1.message
|
|
489
|
+
].join(`\n❌\t`));
|
|
220
490
|
}
|
|
221
491
|
}
|
|
222
|
-
|
|
223
|
-
} catch (thrown) {
|
|
224
|
-
if (thrown instanceof Error) this.logger.error(`❗`, thrown.message, thrown.cause, thrown.stack);
|
|
492
|
+
++idx;
|
|
225
493
|
}
|
|
226
494
|
});
|
|
227
495
|
this.on(`exit`, () => {
|
|
228
496
|
this.logger.info(`🔥`, this.id, `received "exit"`);
|
|
229
|
-
|
|
230
|
-
});
|
|
231
|
-
process.on(`exit`, (code) => {
|
|
232
|
-
this.logger.info(`🔥`, this.id, `exited with code ${code}`);
|
|
233
|
-
});
|
|
234
|
-
process.on(`end`, () => {
|
|
235
|
-
this.logger.info(`🔥`, this.id, `ended`);
|
|
236
|
-
process.exit(0);
|
|
497
|
+
this.proc.exit(0);
|
|
237
498
|
});
|
|
238
|
-
|
|
239
|
-
this.logger.error(`🔥`, this.id, `terminated`);
|
|
240
|
-
process.exit(0);
|
|
241
|
-
});
|
|
242
|
-
process.on(`SIGINT`, () => {
|
|
243
|
-
this.logger.error(`🔥`, this.id, `interrupted`);
|
|
244
|
-
process.exit(0);
|
|
245
|
-
});
|
|
246
|
-
if (process.pid) this.id = process.pid?.toString();
|
|
499
|
+
if (this.proc.pid) this.id = this.proc.pid?.toString();
|
|
247
500
|
this.on(`user-joins`, (username) => {
|
|
248
501
|
this.logger.info(`👤`, `user`, username, `joined`);
|
|
249
|
-
const
|
|
502
|
+
const userKey = `user::${username}`;
|
|
503
|
+
const relay = new SubjectSocket(userKey);
|
|
250
504
|
this.relays.set(username, relay);
|
|
251
505
|
this.logger.info(`🔗`, `attaching services:`, `[${[...this.relayServices.keys()].join(`, `)}]`);
|
|
252
|
-
for (const
|
|
253
|
-
const
|
|
254
|
-
if (
|
|
506
|
+
for (const attachRelay of this.relayServices) {
|
|
507
|
+
const cleanupRelay = attachRelay(relay, userKey);
|
|
508
|
+
if (cleanupRelay) relay.disposalFunctions.push(cleanupRelay);
|
|
255
509
|
}
|
|
256
|
-
this.on(
|
|
510
|
+
this.on(userKey, (...data) => {
|
|
257
511
|
relay.in.next(data);
|
|
258
512
|
});
|
|
259
513
|
relay.out.subscribe(`socket`, (data) => {
|
|
@@ -268,55 +522,189 @@ var ParentSocket = class extends CustomSocket {
|
|
|
268
522
|
this.relays.delete(username);
|
|
269
523
|
}
|
|
270
524
|
});
|
|
271
|
-
|
|
525
|
+
this.proc.stdout.write(`ALIVE`);
|
|
272
526
|
}
|
|
273
|
-
|
|
527
|
+
receiveRelay(attachServices) {
|
|
274
528
|
this.logger.info(`🔗`, `running relay method`);
|
|
275
529
|
this.relayServices.push(attachServices);
|
|
276
530
|
}
|
|
277
531
|
};
|
|
278
532
|
|
|
279
533
|
//#endregion
|
|
280
|
-
//#region src/realtime-server/realtime-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
const
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
534
|
+
//#region src/realtime-server/realtime-family-provider.ts
|
|
535
|
+
function realtimeAtomFamilyProvider({ socket, store = IMPLICIT.STORE }) {
|
|
536
|
+
return function familyProvider(family, index) {
|
|
537
|
+
const coreSubscriptions = /* @__PURE__ */ new Set();
|
|
538
|
+
const clearCoreSubscriptions = () => {
|
|
539
|
+
for (const unsub of coreSubscriptions) unsub();
|
|
540
|
+
coreSubscriptions.clear();
|
|
541
|
+
};
|
|
542
|
+
const familyMemberSubscriptionsWanted = /* @__PURE__ */ new Set();
|
|
543
|
+
const familyMemberSubscriptions = /* @__PURE__ */ new Map();
|
|
544
|
+
const clearFamilySubscriptions = () => {
|
|
545
|
+
for (const unsub of familyMemberSubscriptions.values()) unsub();
|
|
546
|
+
familyMemberSubscriptions.clear();
|
|
547
|
+
};
|
|
548
|
+
const fillUnsubRequest = (key) => {
|
|
549
|
+
const unsubUnsub = familyMemberSubscriptions.get(`${key}:unsub`);
|
|
550
|
+
if (unsubUnsub) {
|
|
551
|
+
unsubUnsub();
|
|
552
|
+
familyMemberSubscriptions.delete(`${key}:unsub`);
|
|
553
|
+
}
|
|
554
|
+
const unsub = familyMemberSubscriptions.get(key);
|
|
555
|
+
if (unsub) {
|
|
556
|
+
unsub();
|
|
557
|
+
familyMemberSubscriptions.delete(key);
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
const exposeFamilyMembers = (subKey) => {
|
|
561
|
+
const token = findInStore(store, family, subKey);
|
|
562
|
+
getFromStore(store, token);
|
|
563
|
+
socket.emit(`serve:${token.key}`, getFromStore(store, token));
|
|
564
|
+
familyMemberSubscriptions.set(token.key, subscribeToState(store, token, `expose-family:${family.key}:${socket.id}`, ({ newValue }) => {
|
|
565
|
+
socket.emit(`serve:${token.key}`, newValue);
|
|
566
|
+
}));
|
|
567
|
+
familyMemberSubscriptions.set(`${token.key}:unsub`, employSocket(socket, `unsub:${token.key}`, () => {
|
|
568
|
+
fillUnsubRequest(token.key);
|
|
569
|
+
}));
|
|
570
|
+
};
|
|
571
|
+
const isAvailable = (exposedSubKeys, subKey) => {
|
|
572
|
+
for (const exposedSubKey of exposedSubKeys) if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) return true;
|
|
573
|
+
return false;
|
|
574
|
+
};
|
|
575
|
+
const start = () => {
|
|
576
|
+
coreSubscriptions.add(employSocket(socket, `sub:${family.key}`, (subKey) => {
|
|
577
|
+
const exposedSubKeys = getFromStore(store, index);
|
|
578
|
+
if (isAvailable(exposedSubKeys, subKey)) exposeFamilyMembers(subKey);
|
|
579
|
+
else {
|
|
580
|
+
familyMemberSubscriptionsWanted.add(stringifyJson(subKey));
|
|
581
|
+
socket.emit(`unavailable:${family.key}`, subKey);
|
|
297
582
|
}
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
583
|
+
}));
|
|
584
|
+
coreSubscriptions.add(subscribeToState(store, index, `expose-family:${family.key}:${socket.id}`, ({ newValue: newExposedSubKeys }) => {
|
|
585
|
+
for (const subKey of newExposedSubKeys) if (familyMemberSubscriptionsWanted.has(stringifyJson(subKey))) exposeFamilyMembers(subKey);
|
|
586
|
+
}));
|
|
587
|
+
};
|
|
588
|
+
start();
|
|
589
|
+
return () => {
|
|
590
|
+
clearCoreSubscriptions();
|
|
591
|
+
clearFamilySubscriptions();
|
|
592
|
+
};
|
|
593
|
+
};
|
|
594
|
+
}
|
|
304
595
|
|
|
305
596
|
//#endregion
|
|
306
|
-
//#region src/realtime-server/realtime-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
597
|
+
//#region src/realtime-server/realtime-mutable-family-provider.ts
|
|
598
|
+
function realtimeMutableFamilyProvider({ socket, store = IMPLICIT.STORE }) {
|
|
599
|
+
return function mutableFamilyProvider(family, index) {
|
|
600
|
+
const coreSubscriptions = /* @__PURE__ */ new Set();
|
|
601
|
+
const clearCoreSubscriptions = () => {
|
|
602
|
+
for (const unsub of coreSubscriptions) unsub();
|
|
603
|
+
coreSubscriptions.clear();
|
|
604
|
+
};
|
|
605
|
+
const familyMemberSubscriptionsWanted = /* @__PURE__ */ new Set();
|
|
606
|
+
const familyMemberSubscriptions = /* @__PURE__ */ new Map();
|
|
607
|
+
const clearFamilySubscriptions = () => {
|
|
608
|
+
for (const unsub of familyMemberSubscriptions.values()) unsub();
|
|
609
|
+
familyMemberSubscriptions.clear();
|
|
610
|
+
};
|
|
611
|
+
const fillUnsubRequest = (key) => {
|
|
612
|
+
const unsubUnsub = familyMemberSubscriptions.get(`${key}:unsub`);
|
|
613
|
+
if (unsubUnsub) {
|
|
614
|
+
unsubUnsub();
|
|
615
|
+
familyMemberSubscriptions.delete(`${key}:unsub`);
|
|
616
|
+
}
|
|
617
|
+
const unsub = familyMemberSubscriptions.get(key);
|
|
618
|
+
if (unsub) {
|
|
619
|
+
unsub();
|
|
620
|
+
familyMemberSubscriptions.delete(key);
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
const exposeFamilyMembers = (subKey) => {
|
|
624
|
+
const token = findInStore(store, family, subKey);
|
|
625
|
+
getFromStore(store, token);
|
|
626
|
+
const jsonToken = getJsonToken(store, token);
|
|
627
|
+
const updateToken = getUpdateToken(token);
|
|
628
|
+
socket.emit(`init:${token.key}`, getFromStore(store, jsonToken));
|
|
629
|
+
familyMemberSubscriptions.set(token.key, subscribeToState(store, updateToken, `expose-family:${family.key}:${socket.id}`, ({ newValue }) => {
|
|
630
|
+
socket.emit(`next:${token.key}`, newValue);
|
|
631
|
+
}));
|
|
632
|
+
familyMemberSubscriptions.set(`${token.key}:unsub`, employSocket(socket, `unsub:${token.key}`, () => {
|
|
633
|
+
fillUnsubRequest(token.key);
|
|
634
|
+
}));
|
|
635
|
+
};
|
|
636
|
+
const isAvailable = (exposedSubKeys, subKey) => {
|
|
637
|
+
for (const exposedSubKey of exposedSubKeys) if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) return true;
|
|
638
|
+
return false;
|
|
639
|
+
};
|
|
640
|
+
const start = () => {
|
|
641
|
+
coreSubscriptions.add(employSocket(socket, `sub:${family.key}`, (subKey) => {
|
|
642
|
+
const exposedSubKeys = getFromStore(store, index);
|
|
643
|
+
if (isAvailable(exposedSubKeys, subKey)) exposeFamilyMembers(subKey);
|
|
644
|
+
else {
|
|
645
|
+
familyMemberSubscriptionsWanted.add(stringifyJson(subKey));
|
|
646
|
+
socket.emit(`unavailable:${family.key}`, subKey);
|
|
647
|
+
}
|
|
648
|
+
}));
|
|
649
|
+
coreSubscriptions.add(subscribeToState(store, index, `expose-family:${family.key}:${socket.id}`, ({ newValue: newExposedSubKeys }) => {
|
|
650
|
+
for (const subKey of newExposedSubKeys) if (familyMemberSubscriptionsWanted.has(stringifyJson(subKey))) exposeFamilyMembers(subKey);
|
|
651
|
+
}));
|
|
652
|
+
};
|
|
653
|
+
start();
|
|
654
|
+
return () => {
|
|
655
|
+
clearCoreSubscriptions();
|
|
656
|
+
clearFamilySubscriptions();
|
|
657
|
+
};
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
//#endregion
|
|
662
|
+
//#region src/realtime-server/realtime-mutable-provider.ts
|
|
663
|
+
function realtimeMutableProvider({ socket, store = IMPLICIT.STORE }) {
|
|
664
|
+
return function mutableProvider(token) {
|
|
665
|
+
const subscriptions = /* @__PURE__ */ new Set();
|
|
666
|
+
const clearSubscriptions = () => {
|
|
667
|
+
for (const unsub of subscriptions) unsub();
|
|
668
|
+
subscriptions.clear();
|
|
669
|
+
};
|
|
670
|
+
const jsonToken = getJsonToken(store, token);
|
|
671
|
+
const trackerToken = getUpdateToken(token);
|
|
672
|
+
const start = () => {
|
|
673
|
+
subscriptions.add(employSocket(socket, `sub:${token.key}`, () => {
|
|
674
|
+
clearSubscriptions();
|
|
675
|
+
socket.emit(`init:${token.key}`, getFromStore(store, jsonToken));
|
|
676
|
+
subscriptions.add(subscribeToState(store, trackerToken, `expose-single:${socket.id}`, ({ newValue }) => {
|
|
677
|
+
socket.emit(`next:${token.key}`, newValue);
|
|
678
|
+
}));
|
|
679
|
+
subscriptions.add(employSocket(socket, `unsub:${token.key}`, () => {
|
|
680
|
+
clearSubscriptions();
|
|
681
|
+
start();
|
|
682
|
+
}));
|
|
683
|
+
}));
|
|
684
|
+
};
|
|
685
|
+
start();
|
|
686
|
+
return clearSubscriptions;
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
//#endregion
|
|
691
|
+
//#region src/realtime-server/realtime-server-stores/server-room-external-store.ts
|
|
692
|
+
const ROOMS = /* @__PURE__ */ new Map();
|
|
693
|
+
async function spawnRoom(roomId, script, options) {
|
|
694
|
+
const child = await new Promise((resolve) => {
|
|
695
|
+
const room = spawn(script, options, { env: process.env });
|
|
696
|
+
const resolver = (data) => {
|
|
697
|
+
if (data.toString() === `ALIVE`) {
|
|
698
|
+
room.stdout.off(`data`, resolver);
|
|
699
|
+
resolve(room);
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
room.stdout.on(`data`, resolver);
|
|
703
|
+
});
|
|
704
|
+
ROOMS.set(roomId, new ChildSocket(child, roomId));
|
|
705
|
+
return new ChildSocket(child, roomId);
|
|
706
|
+
}
|
|
707
|
+
const joinRoomTX = transaction({
|
|
320
708
|
key: `joinRoom`,
|
|
321
709
|
do: (tools, roomId, userId, enteredAtEpoch) => {
|
|
322
710
|
const meta = { enteredAtEpoch };
|
|
@@ -329,65 +717,30 @@ const joinRoomTX = AtomIO.transaction({
|
|
|
329
717
|
return meta;
|
|
330
718
|
}
|
|
331
719
|
});
|
|
332
|
-
const leaveRoomTX =
|
|
720
|
+
const leaveRoomTX = transaction({
|
|
333
721
|
key: `leaveRoom`,
|
|
334
|
-
do: (
|
|
722
|
+
do: ({ env }, roomId, userId) => {
|
|
335
723
|
editRelationsInStore(usersInRooms, (relations) => {
|
|
336
724
|
relations.delete({
|
|
337
725
|
room: roomId,
|
|
338
726
|
user: userId
|
|
339
727
|
});
|
|
340
|
-
},
|
|
728
|
+
}, env().store);
|
|
341
729
|
}
|
|
342
730
|
});
|
|
343
|
-
const destroyRoomTX =
|
|
731
|
+
const destroyRoomTX = transaction({
|
|
344
732
|
key: `destroyRoom`,
|
|
345
|
-
do: (
|
|
733
|
+
do: ({ set, env }, roomId) => {
|
|
346
734
|
editRelationsInStore(usersInRooms, (relations) => {
|
|
347
|
-
relations.delete({ room:
|
|
348
|
-
},
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//#region src/realtime-server/realtime-server-stores/server-sync-store.ts
|
|
355
|
-
function redactTransactionUpdateContent(visibleStateKeys, updates) {
|
|
356
|
-
return updates.map((update) => {
|
|
357
|
-
switch (update.type) {
|
|
358
|
-
case `transaction_outcome`: {
|
|
359
|
-
const redacted = redactTransactionUpdateContent(visibleStateKeys, update.subEvents);
|
|
360
|
-
return {
|
|
361
|
-
...update,
|
|
362
|
-
subEvents: redacted
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
case `atom_update`:
|
|
366
|
-
case `molecule_creation`:
|
|
367
|
-
case `molecule_disposal`:
|
|
368
|
-
case `molecule_transfer`:
|
|
369
|
-
case `state_creation`:
|
|
370
|
-
case `state_disposal`: return update;
|
|
371
|
-
}
|
|
372
|
-
}).filter((update) => {
|
|
373
|
-
switch (update.type) {
|
|
374
|
-
case `atom_update`:
|
|
375
|
-
case `state_creation`:
|
|
376
|
-
case `state_disposal`: return visibleStateKeys.includes(update.token.key);
|
|
377
|
-
case `molecule_creation`:
|
|
378
|
-
case `transaction_outcome`:
|
|
379
|
-
case `molecule_disposal`:
|
|
380
|
-
case `molecule_transfer`: return true;
|
|
735
|
+
relations.delete({ room: roomId });
|
|
736
|
+
}, env().store);
|
|
737
|
+
set(roomIndex, (s) => (s.delete(roomId), s));
|
|
738
|
+
const room = ROOMS.get(roomId);
|
|
739
|
+
if (room) {
|
|
740
|
+
room.emit(`exit`);
|
|
741
|
+
ROOMS.delete(roomId);
|
|
381
742
|
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
const redactorAtoms = atomFamily({
|
|
385
|
-
key: `redactor`,
|
|
386
|
-
default: { occlude: (updates) => updates }
|
|
387
|
-
});
|
|
388
|
-
const userUnacknowledgedQueues = atomFamily({
|
|
389
|
-
key: `unacknowledgedUpdates`,
|
|
390
|
-
default: () => []
|
|
743
|
+
}
|
|
391
744
|
});
|
|
392
745
|
|
|
393
746
|
//#endregion
|
|
@@ -411,377 +764,85 @@ const usersOfSockets = join({
|
|
|
411
764
|
isAType: (s) => s.startsWith(`user::`),
|
|
412
765
|
isBType: (s) => s.startsWith(`socket::`)
|
|
413
766
|
});
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const continuityKey = continuity.key;
|
|
419
|
-
return function sendInitialPayload() {
|
|
420
|
-
const initialPayload = [];
|
|
421
|
-
for (const atom of continuity.globals) {
|
|
422
|
-
const resourceToken = atom.type === `mutable_atom` ? getJsonToken(store, atom) : atom;
|
|
423
|
-
const resource = getFromStore(store, resourceToken);
|
|
424
|
-
initialPayload.push(resourceToken, resource);
|
|
425
|
-
}
|
|
426
|
-
for (const perspective of continuity.perspectives) {
|
|
427
|
-
const { viewAtoms, resourceAtoms } = perspective;
|
|
428
|
-
const userViewState = findInStore(store, viewAtoms, userKey);
|
|
429
|
-
const userView = getFromStore(store, userViewState);
|
|
430
|
-
store.logger.info(`👁`, `atom`, resourceAtoms.key, `${userKey} can see`, {
|
|
431
|
-
viewAtoms,
|
|
432
|
-
resourceAtoms,
|
|
433
|
-
userView
|
|
434
|
-
});
|
|
435
|
-
for (const visibleToken of userView) {
|
|
436
|
-
const resourceToken = visibleToken.type === `mutable_atom` ? getJsonToken(store, visibleToken) : visibleToken;
|
|
437
|
-
const resource = getFromStore(store, resourceToken);
|
|
438
|
-
initialPayload.push(resourceToken, resource);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
const epoch = isRootStore(store) ? store.transactionMeta.epoch.get(continuityKey) ?? null : null;
|
|
442
|
-
socket?.emit(`continuity-init:${continuityKey}`, epoch, initialPayload);
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
//#endregion
|
|
447
|
-
//#region src/realtime-server/continuity/prepare-to-serve-transaction-request.ts
|
|
448
|
-
function prepareToServeTransactionRequest(store, continuity, userKey) {
|
|
449
|
-
const continuityKey = continuity.key;
|
|
450
|
-
return function serveTransactionRequest(txOutcome) {
|
|
451
|
-
store.logger.info(`🛎️`, `continuity`, continuityKey, `received`, txOutcome);
|
|
452
|
-
const transactionKey = txOutcome.token.key;
|
|
453
|
-
const updateId = txOutcome.id;
|
|
454
|
-
const performanceKey = `tx-run:${transactionKey}:${updateId}`;
|
|
455
|
-
const performanceKeyStart = `${performanceKey}:start`;
|
|
456
|
-
const performanceKeyEnd = `${performanceKey}:end`;
|
|
457
|
-
performance.mark(performanceKeyStart);
|
|
458
|
-
try {
|
|
459
|
-
actUponStore(store, {
|
|
460
|
-
type: `transaction`,
|
|
461
|
-
key: transactionKey
|
|
462
|
-
}, updateId)(...txOutcome.params);
|
|
463
|
-
} catch (thrown) {
|
|
464
|
-
if (thrown instanceof Error) store.logger.error(`❌`, `continuity`, continuityKey, `failed to run transaction ${transactionKey} from ${userKey} with update ${updateId}`, thrown.message);
|
|
465
|
-
}
|
|
466
|
-
performance.mark(performanceKeyEnd);
|
|
467
|
-
const metric = performance.measure(performanceKey, performanceKeyStart, performanceKeyEnd);
|
|
468
|
-
store?.logger.info(`🚀`, `transaction`, transactionKey, updateId, userKey, metric.duration);
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
//#endregion
|
|
473
|
-
//#region src/realtime-server/continuity/prepare-to-track-client-acknowledgement.ts
|
|
474
|
-
function prepareToTrackClientAcknowledgement(store, continuity, userKey, userUnacknowledgedUpdates) {
|
|
475
|
-
const continuityKey = continuity.key;
|
|
476
|
-
return function trackClientAcknowledgement(epoch) {
|
|
477
|
-
store.logger.info(`👍`, `continuity`, continuityKey, `${userKey} acknowledged epoch ${epoch}`);
|
|
478
|
-
const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch;
|
|
479
|
-
if (isUnacknowledged) setIntoStore(store, userUnacknowledgedQueues, userKey, (updates) => {
|
|
480
|
-
updates.shift();
|
|
481
|
-
store.logger.info(`👍`, `continuity`, continuityKey, `${userKey} unacknowledged update queue now has`, updates.length, `items`);
|
|
482
|
-
return updates;
|
|
483
|
-
});
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
//#endregion
|
|
488
|
-
//#region src/realtime-server/continuity/subscribe-to-continuity-actions.ts
|
|
489
|
-
function subscribeToContinuityActions(store, continuity, userKey, socket) {
|
|
490
|
-
const continuityKey = continuity.key;
|
|
491
|
-
const unsubscribeFunctions = [];
|
|
492
|
-
for (const transaction of continuity.actions) {
|
|
493
|
-
const unsubscribeFromTransaction = subscribeToTransaction(store, transaction, `sync-continuity:${continuityKey}:${userKey}`, (update) => {
|
|
494
|
-
try {
|
|
495
|
-
const visibleKeys = continuity.globals.map((atom) => {
|
|
496
|
-
if (atom.type === `atom`) return atom.key;
|
|
497
|
-
return getUpdateToken(atom).key;
|
|
498
|
-
}).concat(continuity.perspectives.flatMap((perspective) => {
|
|
499
|
-
const { viewAtoms } = perspective;
|
|
500
|
-
const userPerspectiveTokenState = findInStore(store, viewAtoms, userKey);
|
|
501
|
-
const visibleTokens = getFromStore(store, userPerspectiveTokenState);
|
|
502
|
-
return visibleTokens.map((token) => {
|
|
503
|
-
const key = token.type === `mutable_atom` ? `*` + token.key : token.key;
|
|
504
|
-
return key;
|
|
505
|
-
});
|
|
506
|
-
}));
|
|
507
|
-
const redactedUpdates = redactTransactionUpdateContent(visibleKeys, update.subEvents);
|
|
508
|
-
const redactedUpdate = {
|
|
509
|
-
...update,
|
|
510
|
-
updates: redactedUpdates
|
|
511
|
-
};
|
|
512
|
-
setIntoStore(store, userUnacknowledgedQueues, userKey, (updates) => {
|
|
513
|
-
if (redactedUpdate) {
|
|
514
|
-
updates.push(redactedUpdate);
|
|
515
|
-
updates.sort((a, b) => a.epoch - b.epoch);
|
|
516
|
-
store.logger.info(`👍`, `continuity`, continuityKey, `${userKey} unacknowledged update queue now has`, updates.length, `items`);
|
|
517
|
-
}
|
|
518
|
-
return updates;
|
|
519
|
-
});
|
|
520
|
-
socket?.emit(`tx-new:${continuityKey}`, redactedUpdate);
|
|
521
|
-
} catch (thrown) {
|
|
522
|
-
if (thrown instanceof Error) store.logger.error(`❌`, `continuity`, continuityKey, `${userKey} failed to send update from transaction ${transaction.key} to ${userKey}`, thrown.message);
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
unsubscribeFunctions.push(unsubscribeFromTransaction);
|
|
526
|
-
}
|
|
527
|
-
return unsubscribeFunctions;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
//#endregion
|
|
531
|
-
//#region src/realtime-server/continuity/subscribe-to-continuity-perpectives.ts
|
|
532
|
-
function subscribeToContinuityPerspectives(store, continuity, userKey, socket) {
|
|
533
|
-
const continuityKey = continuity.key;
|
|
534
|
-
const unsubFns = [];
|
|
535
|
-
for (const perspective of continuity.perspectives) {
|
|
536
|
-
const { viewAtoms } = perspective;
|
|
537
|
-
const userViewState = findInStore(store, viewAtoms, userKey);
|
|
538
|
-
const unsubscribeFromUserView = subscribeToState(store, userViewState, `sync-continuity:${continuityKey}:${userKey}:perspective:${perspective.resourceAtoms.key}`, ({ oldValue, newValue }) => {
|
|
539
|
-
const oldKeys = oldValue?.map((token) => token.key);
|
|
540
|
-
const newKeys = newValue.map((token) => token.key);
|
|
541
|
-
const concealed = oldValue?.filter((token) => !newKeys.includes(token.key));
|
|
542
|
-
const revealed = newValue.filter((token) => !oldKeys?.includes(token.key)).flatMap((token) => {
|
|
543
|
-
const resourceToken = token.type === `mutable_atom` ? getJsonToken(store, token) : token;
|
|
544
|
-
const resource = getFromStore(store, resourceToken);
|
|
545
|
-
return [resourceToken, resource];
|
|
546
|
-
});
|
|
547
|
-
store.logger.info(`👁`, `atom`, perspective.resourceAtoms.key, `${userKey} has a new perspective`, {
|
|
548
|
-
oldKeys,
|
|
549
|
-
newKeys,
|
|
550
|
-
revealed,
|
|
551
|
-
concealed
|
|
552
|
-
});
|
|
553
|
-
if (revealed.length > 0) socket?.emit(`reveal:${continuityKey}`, revealed);
|
|
554
|
-
if (concealed && concealed.length > 0) socket?.emit(`conceal:${continuityKey}`, concealed);
|
|
555
|
-
});
|
|
556
|
-
unsubFns.push(unsubscribeFromUserView);
|
|
767
|
+
const userMutualSituationalAwarenessIndexes = selectorFamily({
|
|
768
|
+
key: `userMutualSituationalAwarenessIndexes`,
|
|
769
|
+
get: (userId) => () => {
|
|
770
|
+
return [userId];
|
|
557
771
|
}
|
|
558
|
-
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
//#endregion
|
|
562
|
-
//#region src/realtime-server/continuity/prepare-to-sync-realtime-continuity.ts
|
|
563
|
-
function prepareToExposeRealtimeContinuity({ socket: initialSocket, store = IMPLICIT.STORE }) {
|
|
564
|
-
return function syncRealtimeContinuity(continuity) {
|
|
565
|
-
let socket = initialSocket;
|
|
566
|
-
const continuityKey = continuity.key;
|
|
567
|
-
const userKeyState = findRelationsInStore(usersOfSockets, `socket::${socket.id}`, store).userKeyOfSocket;
|
|
568
|
-
const userKey = getFromStore(store, userKeyState);
|
|
569
|
-
if (!userKey) {
|
|
570
|
-
store.logger.error(`❌`, `continuity`, continuityKey, `Tried to create a synchronizer for a socket (${socket.id}) that is not connected to a user.`);
|
|
571
|
-
return () => {};
|
|
572
|
-
}
|
|
573
|
-
const socketKeyState = findRelationsInStore(usersOfSockets, userKey, store).socketKeyOfUser;
|
|
574
|
-
subscribeToState(store, socketKeyState, `sync-continuity:${continuityKey}:${userKey}`, ({ newValue: newSocketKey }) => {
|
|
575
|
-
store.logger.info(`👋`, `continuity`, continuityKey, `seeing ${userKey} on new socket ${newSocketKey}`);
|
|
576
|
-
if (newSocketKey === null) {
|
|
577
|
-
store.logger.warn(`❌`, `continuity`, continuityKey, `User (${userKey}) is not connected to a socket, waiting for them to reappear.`);
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
const newSocketState = findInStore(store, socketAtoms, newSocketKey);
|
|
581
|
-
const newSocket = getFromStore(store, newSocketState);
|
|
582
|
-
socket = newSocket;
|
|
583
|
-
for (const unacknowledgedUpdate of userUnacknowledgedUpdates) socket?.emit(`tx-new:${continuityKey}`, unacknowledgedUpdate);
|
|
584
|
-
});
|
|
585
|
-
const userUnacknowledgedUpdates = getFromStore(store, userUnacknowledgedQueues, userKey);
|
|
586
|
-
const unsubscribeFunctions = [];
|
|
587
|
-
const unsubscribeFromPerspectives = subscribeToContinuityPerspectives(store, continuity, userKey, socket);
|
|
588
|
-
const unsubscribeFromTransactions = subscribeToContinuityActions(store, continuity, userKey, socket);
|
|
589
|
-
unsubscribeFunctions.push(...unsubscribeFromPerspectives, ...unsubscribeFromTransactions);
|
|
590
|
-
const sendInitialPayload = prepareToSendInitialPayload(store, continuity, userKey, initialSocket);
|
|
591
|
-
socket.off(`get:${continuityKey}`, sendInitialPayload);
|
|
592
|
-
socket.on(`get:${continuityKey}`, sendInitialPayload);
|
|
593
|
-
const fillTransactionRequest = prepareToServeTransactionRequest(store, continuity, userKey);
|
|
594
|
-
socket.off(`tx-run:${continuityKey}`, fillTransactionRequest);
|
|
595
|
-
socket.on(`tx-run:${continuityKey}`, fillTransactionRequest);
|
|
596
|
-
const trackClientAcknowledgement = prepareToTrackClientAcknowledgement(store, continuity, userKey, userUnacknowledgedUpdates);
|
|
597
|
-
socket?.on(`ack:${continuityKey}`, trackClientAcknowledgement);
|
|
598
|
-
return () => {
|
|
599
|
-
for (const unsubscribe of unsubscribeFunctions) unsubscribe();
|
|
600
|
-
socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement);
|
|
601
|
-
socket?.off(`get:${continuityKey}`, sendInitialPayload);
|
|
602
|
-
socket?.off(`tx-run:${continuityKey}`, fillTransactionRequest);
|
|
603
|
-
};
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
//#endregion
|
|
608
|
-
//#region src/realtime-server/realtime-action-receiver.ts
|
|
609
|
-
function realtimeActionReceiver({ socket, store = IMPLICIT.STORE }) {
|
|
610
|
-
return function actionReceiver(tx) {
|
|
611
|
-
const fillTransactionRequest = (update) => {
|
|
612
|
-
const performanceKey = `tx-run:${tx.key}:${update.id}`;
|
|
613
|
-
const performanceKeyStart = `${performanceKey}:start`;
|
|
614
|
-
const performanceKeyEnd = `${performanceKey}:end`;
|
|
615
|
-
performance.mark(performanceKeyStart);
|
|
616
|
-
actUponStore(store, tx, update.id)(...update.params);
|
|
617
|
-
performance.mark(performanceKeyEnd);
|
|
618
|
-
const metric = performance.measure(performanceKey, performanceKeyStart, performanceKeyEnd);
|
|
619
|
-
store?.logger.info(`🚀`, `transaction`, tx.key, update.id, metric.duration);
|
|
620
|
-
};
|
|
621
|
-
socket.on(`tx-run:${tx.key}`, fillTransactionRequest);
|
|
622
|
-
return () => {
|
|
623
|
-
socket.off(`tx-run:${tx.key}`, fillTransactionRequest);
|
|
624
|
-
};
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
//#endregion
|
|
629
|
-
//#region src/realtime-server/realtime-family-provider.ts
|
|
630
|
-
function realtimeAtomFamilyProvider({ socket, store = IMPLICIT.STORE }) {
|
|
631
|
-
return function familyProvider(family, index) {
|
|
632
|
-
const unsubCallbacksByKey = /* @__PURE__ */ new Map();
|
|
633
|
-
const fillUnsubRequest = (key) => {
|
|
634
|
-
socket.off(`unsub:${key}`, fillUnsubRequest);
|
|
635
|
-
const unsub = unsubCallbacksByKey.get(key);
|
|
636
|
-
if (unsub) {
|
|
637
|
-
unsub();
|
|
638
|
-
unsubCallbacksByKey.delete(key);
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
const fillSubRequest = (subKey) => {
|
|
642
|
-
const exposedSubKeys = getFromStore(store, index);
|
|
643
|
-
for (const exposedSubKey of exposedSubKeys) if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) {
|
|
644
|
-
const token = findInStore(store, family, subKey);
|
|
645
|
-
socket.emit(`serve:${token.key}`, getFromStore(store, token));
|
|
646
|
-
const unsubscribe = subscribeToState(store, token, `expose-family:${family.key}:${socket.id}`, ({ newValue }) => {
|
|
647
|
-
socket.emit(`serve:${token.key}`, newValue);
|
|
648
|
-
});
|
|
649
|
-
unsubCallbacksByKey.set(token.key, unsubscribe);
|
|
650
|
-
socket.on(`unsub:${token.key}`, () => {
|
|
651
|
-
fillUnsubRequest(token.key);
|
|
652
|
-
});
|
|
653
|
-
break;
|
|
654
|
-
}
|
|
655
|
-
};
|
|
656
|
-
socket.on(`sub:${family.key}`, fillSubRequest);
|
|
657
|
-
return () => {
|
|
658
|
-
socket.off(`sub:${family.key}`, fillSubRequest);
|
|
659
|
-
for (const [, unsub] of unsubCallbacksByKey) unsub();
|
|
660
|
-
unsubCallbacksByKey.clear();
|
|
661
|
-
};
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
//#endregion
|
|
666
|
-
//#region src/realtime-server/realtime-mutable-family-provider.ts
|
|
667
|
-
function realtimeMutableFamilyProvider({ socket, store = IMPLICIT.STORE }) {
|
|
668
|
-
return function mutableFamilyProvider(family, index) {
|
|
669
|
-
const unsubCallbacksByKey = /* @__PURE__ */ new Map();
|
|
670
|
-
const fillUnsubRequest = (key) => {
|
|
671
|
-
socket.off(`unsub:${key}`, fillUnsubRequest);
|
|
672
|
-
const unsub = unsubCallbacksByKey.get(key);
|
|
673
|
-
if (unsub) {
|
|
674
|
-
unsub();
|
|
675
|
-
unsubCallbacksByKey.delete(key);
|
|
676
|
-
}
|
|
677
|
-
};
|
|
678
|
-
const fillSubRequest = (subKey) => {
|
|
679
|
-
const exposedSubKeys = getFromStore(store, index);
|
|
680
|
-
for (const exposedSubKey of exposedSubKeys) if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) {
|
|
681
|
-
const token = findInStore(store, family, subKey);
|
|
682
|
-
getFromStore(store, token);
|
|
683
|
-
const jsonToken = getJsonToken(store, token);
|
|
684
|
-
const updateToken = getUpdateToken(token);
|
|
685
|
-
socket.emit(`init:${token.key}`, getFromStore(store, jsonToken));
|
|
686
|
-
const unsubscribe = subscribeToState(store, updateToken, `expose-family:${family.key}:${socket.id}`, ({ newValue }) => {
|
|
687
|
-
socket.emit(`next:${token.key}`, newValue);
|
|
688
|
-
});
|
|
689
|
-
unsubCallbacksByKey.set(token.key, unsubscribe);
|
|
690
|
-
socket.on(`unsub:${token.key}`, () => {
|
|
691
|
-
fillUnsubRequest(token.key);
|
|
692
|
-
});
|
|
693
|
-
break;
|
|
694
|
-
}
|
|
695
|
-
};
|
|
696
|
-
socket.on(`sub:${family.key}`, fillSubRequest);
|
|
697
|
-
return () => {
|
|
698
|
-
socket.off(`sub:${family.key}`, fillSubRequest);
|
|
699
|
-
for (const [, unsub] of unsubCallbacksByKey) unsub();
|
|
700
|
-
unsubCallbacksByKey.clear();
|
|
701
|
-
};
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
//#endregion
|
|
706
|
-
//#region src/realtime-server/realtime-mutable-provider.ts
|
|
707
|
-
function realtimeMutableProvider({ socket, store = IMPLICIT.STORE }) {
|
|
708
|
-
return function mutableProvider(token) {
|
|
709
|
-
let unsubscribeFromStateUpdates = null;
|
|
710
|
-
const jsonToken = getJsonToken(store, token);
|
|
711
|
-
const trackerToken = getUpdateToken(token);
|
|
712
|
-
const fillUnsubRequest = () => {
|
|
713
|
-
socket.off(`unsub:${token.key}`, fillUnsubRequest);
|
|
714
|
-
unsubscribeFromStateUpdates?.();
|
|
715
|
-
unsubscribeFromStateUpdates = null;
|
|
716
|
-
};
|
|
717
|
-
const fillSubRequest = () => {
|
|
718
|
-
socket.emit(`init:${token.key}`, getFromStore(store, jsonToken));
|
|
719
|
-
unsubscribeFromStateUpdates = subscribeToState(store, trackerToken, `expose-single:${socket.id}`, ({ newValue }) => {
|
|
720
|
-
socket.emit(`next:${token.key}`, newValue);
|
|
721
|
-
});
|
|
722
|
-
socket.on(`unsub:${token.key}`, fillUnsubRequest);
|
|
723
|
-
};
|
|
724
|
-
socket.on(`sub:${token.key}`, fillSubRequest);
|
|
725
|
-
return () => {
|
|
726
|
-
socket.off(`sub:${token.key}`, fillSubRequest);
|
|
727
|
-
unsubscribeFromStateUpdates?.();
|
|
728
|
-
};
|
|
729
|
-
};
|
|
730
|
-
}
|
|
772
|
+
});
|
|
731
773
|
|
|
732
774
|
//#endregion
|
|
733
775
|
//#region src/realtime-server/realtime-state-provider.ts
|
|
734
776
|
function realtimeStateProvider({ socket, store = IMPLICIT.STORE }) {
|
|
735
777
|
return function stateProvider(token) {
|
|
736
|
-
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
socket.emit(`serve:${token.key}`, newValue);
|
|
741
|
-
});
|
|
742
|
-
const fillUnsubRequest = () => {
|
|
743
|
-
socket.off(`unsub:${token.key}`, fillUnsubRequest);
|
|
744
|
-
if (unsubscribeFromStateUpdates) {
|
|
745
|
-
unsubscribeFromStateUpdates();
|
|
746
|
-
unsubscribeFromStateUpdates = void 0;
|
|
747
|
-
}
|
|
748
|
-
};
|
|
749
|
-
socket.on(`unsub:${token.key}`, fillUnsubRequest);
|
|
778
|
+
const subscriptions = /* @__PURE__ */ new Set();
|
|
779
|
+
const clearSubscriptions = () => {
|
|
780
|
+
for (const unsub of subscriptions) unsub();
|
|
781
|
+
subscriptions.clear();
|
|
750
782
|
};
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
783
|
+
const start = () => {
|
|
784
|
+
subscriptions.add(employSocket(socket, `sub:${token.key}`, () => {
|
|
785
|
+
clearSubscriptions();
|
|
786
|
+
socket.emit(`serve:${token.key}`, getFromStore(store, token));
|
|
787
|
+
subscriptions.add(subscribeToState(store, token, `expose-single:${socket.id}`, ({ newValue }) => {
|
|
788
|
+
socket.emit(`serve:${token.key}`, newValue);
|
|
789
|
+
}));
|
|
790
|
+
subscriptions.add(employSocket(socket, `unsub:${token.key}`, () => {
|
|
791
|
+
clearSubscriptions();
|
|
792
|
+
start();
|
|
793
|
+
}));
|
|
794
|
+
}));
|
|
758
795
|
};
|
|
796
|
+
start();
|
|
797
|
+
return clearSubscriptions;
|
|
759
798
|
};
|
|
760
799
|
}
|
|
761
800
|
|
|
762
801
|
//#endregion
|
|
763
802
|
//#region src/realtime-server/realtime-state-receiver.ts
|
|
764
803
|
function realtimeStateReceiver({ socket, store = IMPLICIT.STORE }) {
|
|
765
|
-
return function stateReceiver(
|
|
766
|
-
const
|
|
767
|
-
|
|
804
|
+
return function stateReceiver(clientToken, serverToken = clientToken) {
|
|
805
|
+
const mutexAtom = findInStore(store, mutexAtoms, serverToken.key);
|
|
806
|
+
const subscriptions = /* @__PURE__ */ new Set();
|
|
807
|
+
const clearSubscriptions = () => {
|
|
808
|
+
for (const unsub of subscriptions) unsub();
|
|
809
|
+
subscriptions.clear();
|
|
768
810
|
};
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
811
|
+
const permitPublish = () => {
|
|
812
|
+
clearSubscriptions();
|
|
813
|
+
subscriptions.add(employSocket(socket, `pub:${clientToken.key}`, (newValue) => {
|
|
814
|
+
setIntoStore(store, serverToken, newValue);
|
|
815
|
+
}));
|
|
816
|
+
subscriptions.add(employSocket(socket, `unclaim:${clientToken.key}`, () => {
|
|
817
|
+
setIntoStore(store, mutexAtom, false);
|
|
818
|
+
clearSubscriptions();
|
|
819
|
+
start();
|
|
820
|
+
}));
|
|
772
821
|
};
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
822
|
+
const start = () => {
|
|
823
|
+
subscriptions.add(employSocket(socket, `claim:${clientToken.key}`, () => {
|
|
824
|
+
if (getFromStore(store, mutexAtom)) {
|
|
825
|
+
clearSubscriptions();
|
|
826
|
+
subscriptions.add(subscribeToState(store, mutexAtom, socket.id, () => {
|
|
827
|
+
if (getFromStore(store, mutexAtom) === false) {
|
|
828
|
+
operateOnStore(OWN_OP, store, mutexAtom, true);
|
|
829
|
+
permitPublish();
|
|
830
|
+
socket.emit(`claim-result:${clientToken.key}`, true);
|
|
831
|
+
}
|
|
832
|
+
}));
|
|
833
|
+
socket.emit(`claim-result:${clientToken.key}`, false);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
setIntoStore(store, mutexAtom, true);
|
|
837
|
+
permitPublish();
|
|
838
|
+
socket.emit(`claim-result:${clientToken.key}`, true);
|
|
839
|
+
}));
|
|
781
840
|
};
|
|
841
|
+
start();
|
|
842
|
+
return clearSubscriptions;
|
|
782
843
|
};
|
|
783
844
|
}
|
|
784
845
|
|
|
785
846
|
//#endregion
|
|
786
|
-
export { ChildSocket, CustomSocket, ParentSocket,
|
|
847
|
+
export { ChildSocket, CustomSocket, ParentSocket, ROOMS, SubjectSocket, destroyRoomTX, joinRoomTX, leaveRoomTX, prepareToProvideContinuity, realtimeAtomFamilyProvider, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, socketAtoms, socketIndex, spawnRoom, userIndex, userMutualSituationalAwarenessIndexes, usersOfSockets };
|
|
787
848
|
//# sourceMappingURL=index.js.map
|