atom.io 0.16.1 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/dist/index.cjs +31 -14
- package/data/dist/index.cjs.map +1 -1
- package/data/dist/index.js +31 -14
- package/data/dist/index.js.map +1 -1
- package/data/src/join.ts +31 -14
- package/dist/chunk-H4Q5FTPZ.js +11 -0
- package/dist/chunk-H4Q5FTPZ.js.map +1 -0
- package/dist/index.cjs +7 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +8 -14
- package/dist/index.js.map +1 -1
- package/internal/dist/index.cjs +240 -193
- package/internal/dist/index.cjs.map +1 -1
- package/internal/dist/index.d.ts +30 -9
- package/internal/dist/index.js +233 -194
- package/internal/dist/index.js.map +1 -1
- package/internal/src/families/find-in-store.ts +74 -0
- package/internal/src/families/index.ts +1 -0
- package/internal/src/ingest-updates/ingest-transaction-update.ts +1 -0
- package/internal/src/mutable/tracker.ts +37 -32
- package/internal/src/mutable/transceiver.ts +1 -1
- package/internal/src/not-found-error.ts +14 -3
- package/internal/src/operation.ts +2 -1
- package/internal/src/selector/create-writable-selector.ts +2 -1
- package/internal/src/selector/register-selector.ts +5 -4
- package/internal/src/set-state/set-atom.ts +23 -6
- package/internal/src/set-state/stow-update.ts +2 -4
- package/internal/src/store/store.ts +13 -4
- package/internal/src/timeline/add-atom-to-timeline.ts +5 -5
- package/internal/src/transaction/abort-transaction.ts +2 -1
- package/internal/src/transaction/apply-transaction.ts +5 -3
- package/internal/src/transaction/build-transaction.ts +17 -10
- package/internal/src/transaction/create-transaction.ts +2 -3
- package/internal/src/transaction/index.ts +3 -2
- package/internal/src/transaction/is-root-store.ts +23 -0
- package/package.json +10 -10
- package/react/dist/index.cjs +27 -21
- package/react/dist/index.cjs.map +1 -1
- package/react/dist/index.d.ts +8 -2
- package/react/dist/index.js +27 -21
- package/react/dist/index.js.map +1 -1
- package/react/src/index.ts +4 -1
- package/react/src/use-i.ts +36 -0
- package/react/src/use-json.ts +38 -0
- package/react/src/use-o.ts +34 -0
- package/react/src/use-tl.ts +45 -0
- package/realtime-client/dist/index.cjs +163 -62
- package/realtime-client/dist/index.cjs.map +1 -1
- package/realtime-client/dist/index.d.ts +10 -6
- package/realtime-client/dist/index.js +153 -60
- package/realtime-client/dist/index.js.map +1 -1
- package/realtime-client/src/index.ts +2 -1
- package/realtime-client/src/pull-state.ts +4 -3
- package/realtime-client/src/{realtime-client-store.ts → realtime-client-stores/client-main-store.ts} +0 -8
- package/realtime-client/src/realtime-client-stores/client-sync-store.ts +15 -0
- package/realtime-client/src/realtime-client-stores/index.ts +2 -0
- package/realtime-client/src/sync-server-action.ts +131 -39
- package/realtime-client/src/sync-state.ts +19 -0
- package/realtime-react/dist/index.cjs +43 -26
- package/realtime-react/dist/index.cjs.map +1 -1
- package/realtime-react/dist/index.d.ts +3 -1
- package/realtime-react/dist/index.js +41 -25
- package/realtime-react/dist/index.js.map +1 -1
- package/realtime-react/src/index.ts +1 -0
- package/realtime-react/src/on-mount.ts +3 -21
- package/realtime-react/src/use-realtime-service.ts +1 -1
- package/realtime-react/src/use-server-action.ts +1 -1
- package/realtime-react/src/use-single-effect.ts +29 -0
- package/realtime-react/src/use-sync-server-action.ts +5 -8
- package/realtime-react/src/use-sync.ts +17 -0
- package/realtime-server/dist/index.cjs +242 -48
- package/realtime-server/dist/index.cjs.map +1 -1
- package/realtime-server/dist/index.d.ts +147 -9
- package/realtime-server/dist/index.js +232 -51
- package/realtime-server/dist/index.js.map +1 -1
- package/realtime-server/src/index.ts +2 -0
- package/realtime-server/src/realtime-action-receiver.ts +4 -3
- package/realtime-server/src/realtime-action-synchronizer.ts +100 -13
- package/realtime-server/src/realtime-family-provider.ts +10 -6
- package/realtime-server/src/realtime-mutable-family-provider.ts +15 -18
- package/realtime-server/src/realtime-mutable-provider.ts +1 -0
- package/realtime-server/src/realtime-server-stores/index.ts +2 -0
- package/realtime-server/src/realtime-server-stores/server-sync-store.ts +115 -0
- package/realtime-server/src/realtime-server-stores/server-user-store.ts +45 -0
- package/realtime-server/src/realtime-state-provider.ts +16 -8
- package/realtime-server/src/realtime-state-receiver.ts +1 -0
- package/realtime-server/src/realtime-state-synchronizer.ts +23 -0
- package/realtime-testing/dist/index.cjs +65 -26
- package/realtime-testing/dist/index.cjs.map +1 -1
- package/realtime-testing/dist/index.d.ts +11 -7
- package/realtime-testing/dist/index.js +64 -26
- package/realtime-testing/dist/index.js.map +1 -1
- package/realtime-testing/src/setup-realtime-test.tsx +83 -43
- package/src/find-state.ts +8 -16
- package/src/logger.ts +16 -11
- package/src/transaction.ts +4 -4
- package/react/src/store-hooks.ts +0 -87
- package/realtime-server/src/realtime-server-store.ts +0 -39
|
@@ -1,28 +1,18 @@
|
|
|
1
|
+
import { isRootStore } from '../../dist/chunk-H4Q5FTPZ.js';
|
|
1
2
|
import '../../dist/chunk-PZLG2HP3.js';
|
|
2
|
-
import * as
|
|
3
|
+
import * as AtomIO7 from 'atom.io';
|
|
3
4
|
import { parseJson } from 'atom.io/json';
|
|
4
5
|
import * as Internal3 from 'atom.io/internal';
|
|
5
6
|
import { getJsonToken, getUpdateToken } from 'atom.io/internal';
|
|
6
7
|
|
|
7
|
-
var myIdState__INTERNAL = AtomIO.atom({
|
|
8
|
-
key: `myId__INTERNAL`,
|
|
9
|
-
default: void 0
|
|
10
|
-
});
|
|
11
|
-
var myIdState = AtomIO.selector({
|
|
12
|
-
key: `myId`,
|
|
13
|
-
get: ({ get }) => get(myIdState__INTERNAL)
|
|
14
|
-
});
|
|
15
|
-
var updateQueueAtoms = AtomIO.atomFamily({
|
|
16
|
-
key: `updateQueue`,
|
|
17
|
-
default: []
|
|
18
|
-
});
|
|
19
8
|
function pullState(token, socket, store) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
9
|
+
const setServedValue = (data) => {
|
|
10
|
+
AtomIO7.setState(token, data, store);
|
|
11
|
+
};
|
|
12
|
+
socket.on(`serve:${token.key}`, setServedValue);
|
|
23
13
|
socket.emit(`sub:${token.key}`);
|
|
24
14
|
return () => {
|
|
25
|
-
socket.off(`serve:${token.key}
|
|
15
|
+
socket.off(`serve:${token.key}`, setServedValue);
|
|
26
16
|
socket.emit(`unsub:${token.key}`);
|
|
27
17
|
};
|
|
28
18
|
}
|
|
@@ -35,7 +25,7 @@ function pullFamilyMember(token, socket, store) {
|
|
|
35
25
|
const { key: familyKey, subKey: serializedSubKey } = token.family;
|
|
36
26
|
const subKey = parseJson(serializedSubKey);
|
|
37
27
|
socket == null ? void 0 : socket.on(`serve:${token.key}`, (data) => {
|
|
38
|
-
|
|
28
|
+
AtomIO7.setState(token, data, store);
|
|
39
29
|
});
|
|
40
30
|
socket == null ? void 0 : socket.emit(`sub:${familyKey}`, subKey);
|
|
41
31
|
return () => {
|
|
@@ -47,12 +37,12 @@ function pullMutableState(token, socket, store) {
|
|
|
47
37
|
const jsonToken = getJsonToken(token);
|
|
48
38
|
const updateToken = getUpdateToken(token);
|
|
49
39
|
socket.on(`init:${token.key}`, (data) => {
|
|
50
|
-
|
|
40
|
+
AtomIO7.setState(jsonToken, data, store);
|
|
51
41
|
});
|
|
52
42
|
socket.on(
|
|
53
43
|
`next:${token.key}`,
|
|
54
44
|
(data) => {
|
|
55
|
-
|
|
45
|
+
AtomIO7.setState(updateToken, data, store);
|
|
56
46
|
}
|
|
57
47
|
);
|
|
58
48
|
socket.emit(`sub:${token.key}`);
|
|
@@ -72,13 +62,13 @@ function pullMutableFamilyMember(token, socket, store) {
|
|
|
72
62
|
const subKey = parseJson(serializedSubKey);
|
|
73
63
|
socket == null ? void 0 : socket.on(`init:${token.key}`, (data) => {
|
|
74
64
|
const jsonToken = getJsonToken(token);
|
|
75
|
-
|
|
65
|
+
AtomIO7.setState(jsonToken, data, store);
|
|
76
66
|
});
|
|
77
67
|
socket == null ? void 0 : socket.on(
|
|
78
68
|
`next:${token.key}`,
|
|
79
69
|
(data) => {
|
|
80
70
|
const trackerToken = getUpdateToken(token);
|
|
81
|
-
|
|
71
|
+
AtomIO7.setState(trackerToken, data, store);
|
|
82
72
|
}
|
|
83
73
|
);
|
|
84
74
|
socket == null ? void 0 : socket.emit(`sub:${familyKey}`, subKey);
|
|
@@ -115,62 +105,155 @@ function serverAction(token, socket, store) {
|
|
|
115
105
|
unsubscribeFromLocalUpdates();
|
|
116
106
|
};
|
|
117
107
|
}
|
|
118
|
-
|
|
119
|
-
|
|
108
|
+
var myIdState__INTERNAL = AtomIO7.atom({
|
|
109
|
+
key: `myId__INTERNAL`,
|
|
110
|
+
default: void 0
|
|
111
|
+
});
|
|
112
|
+
var myIdState = AtomIO7.selector({
|
|
113
|
+
key: `myId`,
|
|
114
|
+
get: ({ get }) => get(myIdState__INTERNAL)
|
|
115
|
+
});
|
|
116
|
+
var optimisticUpdateQueueState = AtomIO7.atom({
|
|
117
|
+
key: `updateQueue`,
|
|
118
|
+
default: []
|
|
119
|
+
});
|
|
120
|
+
var confirmedUpdateQueueState = AtomIO7.atom({
|
|
121
|
+
key: `serverConfirmedUpdateQueue`,
|
|
122
|
+
default: []
|
|
123
|
+
});
|
|
124
|
+
function syncAction(token, socket, store) {
|
|
125
|
+
const optimisticQueue = AtomIO7.getState(optimisticUpdateQueueState, store);
|
|
126
|
+
const confirmedQueue = AtomIO7.getState(confirmedUpdateQueueState, store);
|
|
120
127
|
const unsubscribeFromLocalUpdates = Internal3.subscribeToTransaction(
|
|
121
128
|
token,
|
|
122
129
|
(clientUpdate) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
const optimisticUpdateQueueIndex = optimisticQueue.findIndex(
|
|
131
|
+
(update) => update.id === clientUpdate.id
|
|
132
|
+
);
|
|
133
|
+
if (optimisticUpdateQueueIndex === -1) {
|
|
134
|
+
AtomIO7.setState(
|
|
135
|
+
optimisticUpdateQueueState,
|
|
136
|
+
(queue) => {
|
|
137
|
+
queue.push(clientUpdate);
|
|
138
|
+
queue.sort((a, b) => a.epoch - b.epoch);
|
|
139
|
+
return queue;
|
|
140
|
+
},
|
|
141
|
+
store
|
|
142
|
+
);
|
|
143
|
+
socket.emit(`tx-run:${token.key}`, clientUpdate);
|
|
144
|
+
} else {
|
|
145
|
+
AtomIO7.setState(
|
|
146
|
+
optimisticUpdateQueueState,
|
|
147
|
+
(queue) => {
|
|
148
|
+
queue[optimisticUpdateQueueIndex] = clientUpdate;
|
|
149
|
+
return queue;
|
|
150
|
+
},
|
|
151
|
+
store
|
|
152
|
+
);
|
|
153
|
+
socket.emit(`tx-run:${token.key}`, clientUpdate);
|
|
154
|
+
}
|
|
128
155
|
},
|
|
129
|
-
`tx-run:${token.key}
|
|
156
|
+
`tx-run:${token.key}`,
|
|
130
157
|
store
|
|
131
158
|
);
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
store.logger.error(
|
|
148
|
-
`\u274C`,
|
|
159
|
+
const reconcileUpdates = (optimisticUpdate, confirmedUpdate) => {
|
|
160
|
+
AtomIO7.setState(
|
|
161
|
+
optimisticUpdateQueueState,
|
|
162
|
+
(queue) => {
|
|
163
|
+
queue.shift();
|
|
164
|
+
return queue;
|
|
165
|
+
},
|
|
166
|
+
store
|
|
167
|
+
);
|
|
168
|
+
if (optimisticUpdate.id === confirmedUpdate.id) {
|
|
169
|
+
const clientResult = JSON.stringify(optimisticUpdate.updates);
|
|
170
|
+
const serverResult = JSON.stringify(confirmedUpdate.updates);
|
|
171
|
+
if (clientResult === serverResult) {
|
|
172
|
+
store.logger.info(
|
|
173
|
+
`\u2705`,
|
|
149
174
|
`transaction`,
|
|
150
175
|
token.key,
|
|
151
|
-
`results
|
|
152
|
-
{ clientResult, serverResult }
|
|
176
|
+
`results for ${optimisticUpdate.id} match between client and server`
|
|
153
177
|
);
|
|
154
|
-
|
|
178
|
+
socket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
store.logger.info(
|
|
183
|
+
`\u274C`,
|
|
184
|
+
`transaction`,
|
|
185
|
+
token.key,
|
|
186
|
+
`${store.config.name} thought update #${confirmedUpdate.epoch} was ${optimisticUpdate.key}:${optimisticUpdate.id}, but it was actually ${confirmedUpdate.key}:${confirmedUpdate.id}`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
for (const subsequentOptimistic of optimisticQueue.toReversed()) {
|
|
190
|
+
Internal3.ingestTransactionUpdate(`oldValue`, subsequentOptimistic, store);
|
|
191
|
+
}
|
|
192
|
+
Internal3.ingestTransactionUpdate(`oldValue`, optimisticUpdate, store);
|
|
193
|
+
Internal3.ingestTransactionUpdate(`newValue`, confirmedUpdate, store);
|
|
194
|
+
socket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch);
|
|
195
|
+
for (const subsequentOptimistic of optimisticQueue) {
|
|
196
|
+
const token2 = Object.assign(
|
|
197
|
+
{ type: `transaction` },
|
|
198
|
+
subsequentOptimistic
|
|
199
|
+
);
|
|
200
|
+
const { id, params } = subsequentOptimistic;
|
|
201
|
+
AtomIO7.runTransaction(token2, id, store)(...params);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const registerAndAttemptConfirmedUpdate = (confirmedUpdate) => {
|
|
205
|
+
const zerothOptimisticUpdate = optimisticQueue[0];
|
|
206
|
+
if (zerothOptimisticUpdate) {
|
|
207
|
+
if (zerothOptimisticUpdate.epoch === confirmedUpdate.epoch) {
|
|
208
|
+
reconcileUpdates(zerothOptimisticUpdate, confirmedUpdate);
|
|
209
|
+
for (const nextConfirmed of confirmedQueue) {
|
|
210
|
+
const nextOptimistic = optimisticQueue[0];
|
|
211
|
+
if (nextConfirmed.epoch === nextOptimistic.epoch) {
|
|
212
|
+
reconcileUpdates(nextOptimistic, nextConfirmed);
|
|
213
|
+
} else {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
155
217
|
} else {
|
|
218
|
+
const hasEnqueuedOptimisticUpdate = optimisticQueue.some(
|
|
219
|
+
(update) => update.epoch === confirmedUpdate.epoch
|
|
220
|
+
);
|
|
221
|
+
if (hasEnqueuedOptimisticUpdate) {
|
|
222
|
+
AtomIO7.setState(
|
|
223
|
+
confirmedUpdateQueueState,
|
|
224
|
+
(queue) => {
|
|
225
|
+
queue.push(confirmedUpdate);
|
|
226
|
+
queue.sort((a, b) => a.epoch - b.epoch);
|
|
227
|
+
return queue;
|
|
228
|
+
},
|
|
229
|
+
store
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
if (isRootStore(store) && store.transactionMeta.epoch === confirmedUpdate.epoch - 1) {
|
|
235
|
+
Internal3.ingestTransactionUpdate(`newValue`, confirmedUpdate, store);
|
|
236
|
+
socket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch);
|
|
237
|
+
store.transactionMeta.epoch = confirmedUpdate.epoch;
|
|
238
|
+
} else if (isRootStore(store)) {
|
|
156
239
|
store.logger.info(
|
|
157
|
-
`\
|
|
240
|
+
`\u274C`,
|
|
158
241
|
`transaction`,
|
|
159
242
|
token.key,
|
|
160
|
-
`
|
|
243
|
+
`received out-of-order update from server`,
|
|
244
|
+
{
|
|
245
|
+
clientEpoch: store.transactionMeta.epoch,
|
|
246
|
+
serverEpoch: confirmedUpdate.epoch
|
|
247
|
+
}
|
|
161
248
|
);
|
|
162
249
|
}
|
|
163
|
-
AtomIO.setState(updateQueueState, (queue) => {
|
|
164
|
-
queue.shift();
|
|
165
|
-
return queue;
|
|
166
|
-
});
|
|
167
250
|
}
|
|
168
|
-
Internal3.ingestTransactionUpdate(`newValue`, serverUpdate, store);
|
|
169
251
|
};
|
|
170
|
-
socket.
|
|
252
|
+
socket.off(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate);
|
|
253
|
+
socket.on(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate);
|
|
171
254
|
socket.emit(`tx-sub:${token.key}`);
|
|
172
255
|
const unsubscribeFromIncomingUpdates = () => {
|
|
173
|
-
socket.off(`tx-new:${token.key}`,
|
|
256
|
+
socket.off(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate);
|
|
174
257
|
socket.emit(`tx-unsub:${token.key}`);
|
|
175
258
|
};
|
|
176
259
|
return () => {
|
|
@@ -178,7 +261,17 @@ function syncAction(token, socket, updateQueue, store) {
|
|
|
178
261
|
unsubscribeFromIncomingUpdates();
|
|
179
262
|
};
|
|
180
263
|
}
|
|
264
|
+
function syncState(token, socket, store) {
|
|
265
|
+
const setServedValue = (data) => {
|
|
266
|
+
AtomIO7.setState(token, data, store);
|
|
267
|
+
};
|
|
268
|
+
socket.on(`value:${token.key}`, setServedValue);
|
|
269
|
+
socket.emit(`get:${token.key}`);
|
|
270
|
+
return () => {
|
|
271
|
+
socket.off(`value:${token.key}`, setServedValue);
|
|
272
|
+
};
|
|
273
|
+
}
|
|
181
274
|
|
|
182
|
-
export { myIdState, myIdState__INTERNAL, pullFamilyMember, pullMutableFamilyMember, pullMutableState, pullState, pushState, serverAction, syncAction,
|
|
275
|
+
export { confirmedUpdateQueueState, myIdState, myIdState__INTERNAL, optimisticUpdateQueueState, pullFamilyMember, pullMutableFamilyMember, pullMutableState, pullState, pushState, serverAction, syncAction, syncState };
|
|
183
276
|
//# sourceMappingURL=out.js.map
|
|
184
277
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/realtime-client-store.ts","../src/pull-state.ts","../src/pull-family-member.ts","../src/pull-mutable.ts","../src/pull-mutable-family-member.ts","../src/push-state.ts","../src/server-action.ts","../src/sync-server-action.ts"],"names":["AtomIO","getJsonToken","getUpdateToken","parseJson","Internal"],"mappings":"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","sourcesContent":["import * as AtomIO from \"atom.io\"\n\nexport const myIdState__INTERNAL = AtomIO.atom<string | undefined>({\n\tkey: `myId__INTERNAL`,\n\tdefault: undefined,\n})\nexport const myIdState = AtomIO.selector<string | undefined>({\n\tkey: `myId`,\n\tget: ({ get }) => get(myIdState__INTERNAL),\n})\n\nexport const updateQueueAtoms = AtomIO.atomFamily<\n\tAtomIO.TransactionUpdate<any>[],\n\tAtomIO.TransactionToken<any>\n>({\n\tkey: `updateQueue`,\n\tdefault: [],\n})\n","import * as AtomIO from \"atom.io\"\nimport type { Store } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pullState<J extends Json.Serializable>(\n\ttoken: AtomIO.WritableToken<J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tsocket.on(`serve:${token.key}`, (data) => {\n\t\tAtomIO.setState(token, data, store)\n\t})\n\tsocket.emit(`sub:${token.key}`)\n\treturn () => {\n\t\tsocket.off(`serve:${token.key}`)\n\t\tsocket.emit(`unsub:${token.key}`)\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport type { Store } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport { parseJson } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pullFamilyMember<J extends Json.Serializable>(\n\ttoken: AtomIO.WritableToken<J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tif (!(`family` in token)) {\n\t\tconsole.error(`Token is not a family member:`, token)\n\t\treturn () => {}\n\t}\n\tconst { key: familyKey, subKey: serializedSubKey } = token.family\n\tconst subKey = parseJson(serializedSubKey)\n\tsocket?.on(`serve:${token.key}`, (data: J) => {\n\t\tAtomIO.setState(token, data, store)\n\t})\n\tsocket?.emit(`sub:${familyKey}`, subKey)\n\treturn () => {\n\t\tsocket?.off(`serve:${token.key}`)\n\t\tsocket?.emit(`unsub:${token.key}`)\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport type { Store, Transceiver } from \"atom.io/internal\"\nimport { getJsonToken, getUpdateToken } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pullMutableState<\n\tT extends Transceiver<any>,\n\tJ extends Json.Serializable,\n>(\n\ttoken: AtomIO.MutableAtomToken<T, J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tconst jsonToken = getJsonToken(token)\n\tconst updateToken = getUpdateToken(token)\n\tsocket.on(`init:${token.key}`, (data: J) => {\n\t\tAtomIO.setState(jsonToken, data, store)\n\t})\n\tsocket.on(\n\t\t`next:${token.key}`,\n\t\t(data: T extends Transceiver<infer Update> ? Update : never) => {\n\t\t\tAtomIO.setState(updateToken, data, store)\n\t\t},\n\t)\n\tsocket.emit(`sub:${token.key}`)\n\treturn () => {\n\t\tsocket.off(`init:${token.key}`)\n\t\tsocket.off(`next:${token.key}`)\n\t\tsocket.emit(`unsub:${token.key}`)\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport { getJsonToken, getUpdateToken } from \"atom.io/internal\"\nimport type { Store, Transceiver } from \"atom.io/internal\"\nimport { parseJson } from \"atom.io/json\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pullMutableFamilyMember<\n\tT extends Transceiver<any>,\n\tJ extends Json.Serializable,\n>(\n\ttoken: AtomIO.MutableAtomToken<T, J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tif (!(`family` in token)) {\n\t\tconsole.error(`Token is not a family member:`, token)\n\t\treturn () => {}\n\t}\n\tconst { key: familyKey, subKey: serializedSubKey } = token.family\n\tconst subKey = parseJson(serializedSubKey)\n\tsocket?.on(`init:${token.key}`, (data: J) => {\n\t\tconst jsonToken = getJsonToken(token)\n\t\tAtomIO.setState(jsonToken, data, store)\n\t})\n\tsocket?.on(\n\t\t`next:${token.key}`,\n\t\t(data: T extends Transceiver<infer Signal> ? Signal : never) => {\n\t\t\tconst trackerToken = getUpdateToken(token)\n\t\t\tAtomIO.setState(trackerToken, data, store)\n\t\t},\n\t)\n\tsocket?.emit(`sub:${familyKey}`, subKey)\n\treturn () => {\n\t\tsocket?.off(`serve:${token.key}`)\n\t\tsocket?.emit(`unsub:${token.key}`)\n\t}\n}\n","import type * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pushState<J extends Json.Serializable>(\n\ttoken: AtomIO.WritableToken<J>,\n\tsocket: Socket,\n\tstore: Internal.Store,\n): () => void {\n\tsocket.emit(`claim:${token.key}`)\n\tInternal.subscribeToState(\n\t\ttoken,\n\t\t({ newValue }) => {\n\t\t\tsocket.emit(`pub:${token.key}`, newValue)\n\t\t},\n\t\t`push`,\n\t\tstore,\n\t)\n\treturn () => {\n\t\tsocket.off(`pub:${token.key}`)\n\t\tsocket.emit(`unclaim:${token.key}`)\n\t}\n}\n","import type * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function serverAction<ƒ extends AtomIO.ƒn>(\n\ttoken: AtomIO.TransactionToken<ƒ>,\n\tsocket: Socket,\n\tstore: Internal.Store,\n): () => void {\n\tconst unsubscribeFromLocalUpdates = Internal.subscribeToTransaction(\n\t\ttoken,\n\t\t(clientUpdate) => {\n\t\t\tsocket.emit(`tx-run:${token.key}`, clientUpdate)\n\t\t},\n\t\t`tx-run:${token.key}:${socket.id}`,\n\t\tstore,\n\t)\n\n\treturn () => {\n\t\tunsubscribeFromLocalUpdates()\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport type { Socket } from \"socket.io-client\"\nimport { updateQueueAtoms } from \"./realtime-client-store\"\n\nexport function syncAction<ƒ extends AtomIO.ƒn>(\n\ttoken: AtomIO.TransactionToken<ƒ>,\n\tsocket: Socket,\n\tupdateQueue: AtomIO.TransactionUpdate<any>[],\n\tstore: Internal.Store,\n): () => void {\n\tconst updateQueueState = AtomIO.findState(updateQueueAtoms, token)\n\n\tconst unsubscribeFromLocalUpdates = Internal.subscribeToTransaction(\n\t\ttoken,\n\t\t(clientUpdate) => {\n\t\t\tAtomIO.setState(updateQueueState, (queue) => {\n\t\t\t\tqueue.push(clientUpdate)\n\t\t\t\treturn queue\n\t\t\t})\n\t\t\tsocket.emit(`tx-run:${token.key}`, clientUpdate)\n\t\t},\n\t\t`tx-run:${token.key}:${socket.id}`,\n\t\tstore,\n\t)\n\n\tconst applyIncomingUpdate = (serverUpdate: AtomIO.TransactionUpdate<ƒ>) => {\n\t\tconst clientUpdate = updateQueue[0]\n\t\tif (clientUpdate) {\n\t\t\tif (clientUpdate.id !== serverUpdate.id) {\n\t\t\t\tstore.logger.error(\n\t\t\t\t\t`❌`,\n\t\t\t\t\t`transaction`,\n\t\t\t\t\tserverUpdate.key,\n\t\t\t\t\t`did not match position 0 in queue of updates awaiting sync:`,\n\t\t\t\t\tupdateQueue,\n\t\t\t\t)\n\t\t\t}\n\t\t\tconst clientResult = JSON.stringify(clientUpdate)\n\t\t\tconst serverResult = JSON.stringify(serverUpdate)\n\t\t\tif (clientResult !== serverResult) {\n\t\t\t\tstore.logger.error(\n\t\t\t\t\t`❌`,\n\t\t\t\t\t`transaction`,\n\t\t\t\t\ttoken.key,\n\t\t\t\t\t`results do not match between client and server:`,\n\t\t\t\t\t{ clientResult, serverResult },\n\t\t\t\t)\n\t\t\t\tInternal.ingestTransactionUpdate(`oldValue`, clientUpdate, store)\n\t\t\t} else {\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`✅`,\n\t\t\t\t\t`transaction`,\n\t\t\t\t\ttoken.key,\n\t\t\t\t\t`results match between client and server`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tAtomIO.setState(updateQueueState, (queue) => {\n\t\t\t\tqueue.shift()\n\t\t\t\treturn queue\n\t\t\t})\n\t\t}\n\t\tInternal.ingestTransactionUpdate(`newValue`, serverUpdate, store)\n\t}\n\tsocket.on(`tx-new:${token.key}`, applyIncomingUpdate)\n\tsocket.emit(`tx-sub:${token.key}`)\n\tconst unsubscribeFromIncomingUpdates = () => {\n\t\tsocket.off(`tx-new:${token.key}`, applyIncomingUpdate)\n\t\tsocket.emit(`tx-unsub:${token.key}`)\n\t}\n\treturn () => {\n\t\tunsubscribeFromLocalUpdates()\n\t\tunsubscribeFromIncomingUpdates()\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/pull-state.ts","../src/pull-family-member.ts","../src/pull-mutable.ts","../src/pull-mutable-family-member.ts","../src/push-state.ts","../src/server-action.ts","../src/realtime-client-stores/client-main-store.ts","../src/realtime-client-stores/client-sync-store.ts","../src/sync-server-action.ts","../src/sync-state.ts"],"names":["AtomIO","getJsonToken","getUpdateToken","parseJson","Internal","token"],"mappings":"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","sourcesContent":["import * as AtomIO from \"atom.io\"\nimport type { Store } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pullState<J extends Json.Serializable>(\n\ttoken: AtomIO.WritableToken<J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tconst setServedValue = (data: J) => {\n\t\tAtomIO.setState(token, data, store)\n\t}\n\tsocket.on(`serve:${token.key}`, setServedValue)\n\tsocket.emit(`sub:${token.key}`)\n\treturn () => {\n\t\tsocket.off(`serve:${token.key}`, setServedValue)\n\t\tsocket.emit(`unsub:${token.key}`)\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport type { Store } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport { parseJson } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pullFamilyMember<J extends Json.Serializable>(\n\ttoken: AtomIO.WritableToken<J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tif (!(`family` in token)) {\n\t\tconsole.error(`Token is not a family member:`, token)\n\t\treturn () => {}\n\t}\n\tconst { key: familyKey, subKey: serializedSubKey } = token.family\n\tconst subKey = parseJson(serializedSubKey)\n\tsocket?.on(`serve:${token.key}`, (data: J) => {\n\t\tAtomIO.setState(token, data, store)\n\t})\n\tsocket?.emit(`sub:${familyKey}`, subKey)\n\treturn () => {\n\t\tsocket?.off(`serve:${token.key}`)\n\t\tsocket?.emit(`unsub:${token.key}`)\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport type { Store, Transceiver } from \"atom.io/internal\"\nimport { getJsonToken, getUpdateToken } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pullMutableState<\n\tT extends Transceiver<any>,\n\tJ extends Json.Serializable,\n>(\n\ttoken: AtomIO.MutableAtomToken<T, J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tconst jsonToken = getJsonToken(token)\n\tconst updateToken = getUpdateToken(token)\n\tsocket.on(`init:${token.key}`, (data: J) => {\n\t\tAtomIO.setState(jsonToken, data, store)\n\t})\n\tsocket.on(\n\t\t`next:${token.key}`,\n\t\t(data: T extends Transceiver<infer Update> ? Update : never) => {\n\t\t\tAtomIO.setState(updateToken, data, store)\n\t\t},\n\t)\n\tsocket.emit(`sub:${token.key}`)\n\treturn () => {\n\t\tsocket.off(`init:${token.key}`)\n\t\tsocket.off(`next:${token.key}`)\n\t\tsocket.emit(`unsub:${token.key}`)\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport { getJsonToken, getUpdateToken } from \"atom.io/internal\"\nimport type { Store, Transceiver } from \"atom.io/internal\"\nimport { parseJson } from \"atom.io/json\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pullMutableFamilyMember<\n\tT extends Transceiver<any>,\n\tJ extends Json.Serializable,\n>(\n\ttoken: AtomIO.MutableAtomToken<T, J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tif (!(`family` in token)) {\n\t\tconsole.error(`Token is not a family member:`, token)\n\t\treturn () => {}\n\t}\n\tconst { key: familyKey, subKey: serializedSubKey } = token.family\n\tconst subKey = parseJson(serializedSubKey)\n\tsocket?.on(`init:${token.key}`, (data: J) => {\n\t\tconst jsonToken = getJsonToken(token)\n\t\tAtomIO.setState(jsonToken, data, store)\n\t})\n\tsocket?.on(\n\t\t`next:${token.key}`,\n\t\t(data: T extends Transceiver<infer Signal> ? Signal : never) => {\n\t\t\tconst trackerToken = getUpdateToken(token)\n\t\t\tAtomIO.setState(trackerToken, data, store)\n\t\t},\n\t)\n\tsocket?.emit(`sub:${familyKey}`, subKey)\n\treturn () => {\n\t\tsocket?.off(`serve:${token.key}`)\n\t\tsocket?.emit(`unsub:${token.key}`)\n\t}\n}\n","import type * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function pushState<J extends Json.Serializable>(\n\ttoken: AtomIO.WritableToken<J>,\n\tsocket: Socket,\n\tstore: Internal.Store,\n): () => void {\n\tsocket.emit(`claim:${token.key}`)\n\tInternal.subscribeToState(\n\t\ttoken,\n\t\t({ newValue }) => {\n\t\t\tsocket.emit(`pub:${token.key}`, newValue)\n\t\t},\n\t\t`push`,\n\t\tstore,\n\t)\n\treturn () => {\n\t\tsocket.off(`pub:${token.key}`)\n\t\tsocket.emit(`unclaim:${token.key}`)\n\t}\n}\n","import type * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function serverAction<ƒ extends AtomIO.ƒn>(\n\ttoken: AtomIO.TransactionToken<ƒ>,\n\tsocket: Socket,\n\tstore: Internal.Store,\n): () => void {\n\tconst unsubscribeFromLocalUpdates = Internal.subscribeToTransaction(\n\t\ttoken,\n\t\t(clientUpdate) => {\n\t\t\tsocket.emit(`tx-run:${token.key}`, clientUpdate)\n\t\t},\n\t\t`tx-run:${token.key}:${socket.id}`,\n\t\tstore,\n\t)\n\n\treturn () => {\n\t\tunsubscribeFromLocalUpdates()\n\t}\n}\n","import * as AtomIO from \"atom.io\"\n\nexport const myIdState__INTERNAL = AtomIO.atom<string | undefined>({\n\tkey: `myId__INTERNAL`,\n\tdefault: undefined,\n})\nexport const myIdState = AtomIO.selector<string | undefined>({\n\tkey: `myId`,\n\tget: ({ get }) => get(myIdState__INTERNAL),\n})\n","import * as AtomIO from \"atom.io\"\n\nexport const optimisticUpdateQueueState = AtomIO.atom<\n\tAtomIO.TransactionUpdate<any>[]\n>({\n\tkey: `updateQueue`,\n\tdefault: [],\n})\n\nexport const confirmedUpdateQueueState = AtomIO.atom<\n\tAtomIO.TransactionUpdate<any>[]\n>({\n\tkey: `serverConfirmedUpdateQueue`,\n\tdefault: [],\n})\n","import * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport type { Socket } from \"socket.io-client\"\n\nimport { isRootStore } from \"../../internal/src/transaction/is-root-store\"\nimport {\n\tconfirmedUpdateQueueState,\n\toptimisticUpdateQueueState,\n} from \"./realtime-client-stores\"\n\nexport function syncAction<ƒ extends AtomIO.ƒn>(\n\ttoken: AtomIO.TransactionToken<ƒ>,\n\tsocket: Socket,\n\tstore: Internal.Store,\n): () => void {\n\tconst optimisticQueue = AtomIO.getState(optimisticUpdateQueueState, store)\n\tconst confirmedQueue = AtomIO.getState(confirmedUpdateQueueState, store)\n\n\tconst unsubscribeFromLocalUpdates = Internal.subscribeToTransaction(\n\t\ttoken,\n\t\t(clientUpdate) => {\n\t\t\tconst optimisticUpdateQueueIndex = optimisticQueue.findIndex(\n\t\t\t\t(update) => update.id === clientUpdate.id,\n\t\t\t)\n\t\t\tif (optimisticUpdateQueueIndex === -1) {\n\t\t\t\tAtomIO.setState(\n\t\t\t\t\toptimisticUpdateQueueState,\n\t\t\t\t\t(queue) => {\n\t\t\t\t\t\tqueue.push(clientUpdate)\n\t\t\t\t\t\tqueue.sort((a, b) => a.epoch - b.epoch)\n\t\t\t\t\t\treturn queue\n\t\t\t\t\t},\n\t\t\t\t\tstore,\n\t\t\t\t)\n\t\t\t\tsocket.emit(`tx-run:${token.key}`, clientUpdate)\n\t\t\t} else {\n\t\t\t\tAtomIO.setState(\n\t\t\t\t\toptimisticUpdateQueueState,\n\t\t\t\t\t(queue) => {\n\t\t\t\t\t\tqueue[optimisticUpdateQueueIndex] = clientUpdate\n\t\t\t\t\t\treturn queue\n\t\t\t\t\t},\n\t\t\t\t\tstore,\n\t\t\t\t)\n\t\t\t\tsocket.emit(`tx-run:${token.key}`, clientUpdate)\n\t\t\t}\n\t\t},\n\t\t`tx-run:${token.key}`,\n\t\tstore,\n\t)\n\tconst reconcileUpdates = (\n\t\toptimisticUpdate: AtomIO.TransactionUpdate<ƒ>,\n\t\tconfirmedUpdate: AtomIO.TransactionUpdate<ƒ>,\n\t) => {\n\t\tAtomIO.setState(\n\t\t\toptimisticUpdateQueueState,\n\t\t\t(queue) => {\n\t\t\t\tqueue.shift()\n\t\t\t\treturn queue\n\t\t\t},\n\t\t\tstore,\n\t\t)\n\t\tif (optimisticUpdate.id === confirmedUpdate.id) {\n\t\t\tconst clientResult = JSON.stringify(optimisticUpdate.updates)\n\t\t\tconst serverResult = JSON.stringify(confirmedUpdate.updates)\n\t\t\tif (clientResult === serverResult) {\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`✅`,\n\t\t\t\t\t`transaction`,\n\t\t\t\t\ttoken.key,\n\t\t\t\t\t`results for ${optimisticUpdate.id} match between client and server`,\n\t\t\t\t)\n\t\t\t\tsocket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch)\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\t// id mismatch\n\t\t\tstore.logger.info(\n\t\t\t\t`❌`,\n\t\t\t\t`transaction`,\n\t\t\t\ttoken.key,\n\t\t\t\t`${store.config.name} thought update #${confirmedUpdate.epoch} was ${optimisticUpdate.key}:${optimisticUpdate.id}, but it was actually ${confirmedUpdate.key}:${confirmedUpdate.id}`,\n\t\t\t)\n\t\t}\n\t\tfor (const subsequentOptimistic of optimisticQueue.toReversed()) {\n\t\t\tInternal.ingestTransactionUpdate(`oldValue`, subsequentOptimistic, store)\n\t\t}\n\t\tInternal.ingestTransactionUpdate(`oldValue`, optimisticUpdate, store)\n\t\tInternal.ingestTransactionUpdate(`newValue`, confirmedUpdate, store)\n\t\tsocket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch)\n\t\tfor (const subsequentOptimistic of optimisticQueue) {\n\t\t\tconst token = Object.assign(\n\t\t\t\t{ type: `transaction` } as const,\n\t\t\t\tsubsequentOptimistic,\n\t\t\t)\n\t\t\tconst { id, params } = subsequentOptimistic\n\t\t\tAtomIO.runTransaction(token, id, store)(...params)\n\t\t}\n\t}\n\n\tconst registerAndAttemptConfirmedUpdate = (\n\t\tconfirmedUpdate: AtomIO.TransactionUpdate<ƒ>,\n\t) => {\n\t\tconst zerothOptimisticUpdate = optimisticQueue[0]\n\t\tif (zerothOptimisticUpdate) {\n\t\t\tif (zerothOptimisticUpdate.epoch === confirmedUpdate.epoch) {\n\t\t\t\treconcileUpdates(zerothOptimisticUpdate, confirmedUpdate)\n\t\t\t\tfor (const nextConfirmed of confirmedQueue) {\n\t\t\t\t\tconst nextOptimistic = optimisticQueue[0]\n\t\t\t\t\tif (nextConfirmed.epoch === nextOptimistic.epoch) {\n\t\t\t\t\t\treconcileUpdates(nextOptimistic, nextConfirmed)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// epoch mismatch\n\n\t\t\t\tconst hasEnqueuedOptimisticUpdate = optimisticQueue.some(\n\t\t\t\t\t(update) => update.epoch === confirmedUpdate.epoch,\n\t\t\t\t)\n\t\t\t\tif (hasEnqueuedOptimisticUpdate) {\n\t\t\t\t\tAtomIO.setState(\n\t\t\t\t\t\tconfirmedUpdateQueueState,\n\t\t\t\t\t\t(queue) => {\n\t\t\t\t\t\t\tqueue.push(confirmedUpdate)\n\t\t\t\t\t\t\tqueue.sort((a, b) => a.epoch - b.epoch)\n\t\t\t\t\t\t\treturn queue\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstore,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (\n\t\t\t\tisRootStore(store) &&\n\t\t\t\tstore.transactionMeta.epoch === confirmedUpdate.epoch - 1\n\t\t\t) {\n\t\t\t\tInternal.ingestTransactionUpdate(`newValue`, confirmedUpdate, store)\n\t\t\t\tsocket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch)\n\t\t\t\tstore.transactionMeta.epoch = confirmedUpdate.epoch\n\t\t\t} else if (isRootStore(store)) {\n\t\t\t\tstore.logger.info(\n\t\t\t\t\t`❌`,\n\t\t\t\t\t`transaction`,\n\t\t\t\t\ttoken.key,\n\t\t\t\t\t`received out-of-order update from server`,\n\t\t\t\t\t{\n\t\t\t\t\t\tclientEpoch: store.transactionMeta.epoch,\n\t\t\t\t\t\tserverEpoch: confirmedUpdate.epoch,\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\tsocket.off(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate)\n\tsocket.on(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate)\n\tsocket.emit(`tx-sub:${token.key}`)\n\tconst unsubscribeFromIncomingUpdates = () => {\n\t\tsocket.off(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate)\n\t\tsocket.emit(`tx-unsub:${token.key}`)\n\t}\n\treturn () => {\n\t\tunsubscribeFromLocalUpdates()\n\t\tunsubscribeFromIncomingUpdates()\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport type { Store } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport type { Socket } from \"socket.io-client\"\n\nexport function syncState<J extends Json.Serializable>(\n\ttoken: AtomIO.WritableToken<J>,\n\tsocket: Socket,\n\tstore: Store,\n): () => void {\n\tconst setServedValue = (data: J) => {\n\t\tAtomIO.setState(token, data, store)\n\t}\n\tsocket.on(`value:${token.key}`, setServedValue)\n\tsocket.emit(`get:${token.key}`)\n\treturn () => {\n\t\tsocket.off(`value:${token.key}`, setServedValue)\n\t}\n}\n"]}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export * from "./realtime-client-store"
|
|
2
1
|
export * from "./pull-state"
|
|
3
2
|
export * from "./pull-family-member"
|
|
4
3
|
export * from "./pull-mutable"
|
|
5
4
|
export * from "./pull-mutable-family-member"
|
|
6
5
|
export * from "./push-state"
|
|
7
6
|
export * from "./server-action"
|
|
7
|
+
export * from "./realtime-client-stores"
|
|
8
8
|
export * from "./sync-server-action"
|
|
9
|
+
export * from "./sync-state"
|
|
@@ -8,12 +8,13 @@ export function pullState<J extends Json.Serializable>(
|
|
|
8
8
|
socket: Socket,
|
|
9
9
|
store: Store,
|
|
10
10
|
): () => void {
|
|
11
|
-
|
|
11
|
+
const setServedValue = (data: J) => {
|
|
12
12
|
AtomIO.setState(token, data, store)
|
|
13
|
-
}
|
|
13
|
+
}
|
|
14
|
+
socket.on(`serve:${token.key}`, setServedValue)
|
|
14
15
|
socket.emit(`sub:${token.key}`)
|
|
15
16
|
return () => {
|
|
16
|
-
socket.off(`serve:${token.key}
|
|
17
|
+
socket.off(`serve:${token.key}`, setServedValue)
|
|
17
18
|
socket.emit(`unsub:${token.key}`)
|
|
18
19
|
}
|
|
19
20
|
}
|
package/realtime-client/src/{realtime-client-store.ts → realtime-client-stores/client-main-store.ts}
RENAMED
|
@@ -8,11 +8,3 @@ export const myIdState = AtomIO.selector<string | undefined>({
|
|
|
8
8
|
key: `myId`,
|
|
9
9
|
get: ({ get }) => get(myIdState__INTERNAL),
|
|
10
10
|
})
|
|
11
|
-
|
|
12
|
-
export const updateQueueAtoms = AtomIO.atomFamily<
|
|
13
|
-
AtomIO.TransactionUpdate<any>[],
|
|
14
|
-
AtomIO.TransactionToken<any>
|
|
15
|
-
>({
|
|
16
|
-
key: `updateQueue`,
|
|
17
|
-
default: [],
|
|
18
|
-
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as AtomIO from "atom.io"
|
|
2
|
+
|
|
3
|
+
export const optimisticUpdateQueueState = AtomIO.atom<
|
|
4
|
+
AtomIO.TransactionUpdate<any>[]
|
|
5
|
+
>({
|
|
6
|
+
key: `updateQueue`,
|
|
7
|
+
default: [],
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export const confirmedUpdateQueueState = AtomIO.atom<
|
|
11
|
+
AtomIO.TransactionUpdate<any>[]
|
|
12
|
+
>({
|
|
13
|
+
key: `serverConfirmedUpdateQueue`,
|
|
14
|
+
default: [],
|
|
15
|
+
})
|
|
@@ -1,71 +1,163 @@
|
|
|
1
1
|
import * as AtomIO from "atom.io"
|
|
2
2
|
import * as Internal from "atom.io/internal"
|
|
3
3
|
import type { Socket } from "socket.io-client"
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import { isRootStore } from "../../internal/src/transaction/is-root-store"
|
|
6
|
+
import {
|
|
7
|
+
confirmedUpdateQueueState,
|
|
8
|
+
optimisticUpdateQueueState,
|
|
9
|
+
} from "./realtime-client-stores"
|
|
5
10
|
|
|
6
11
|
export function syncAction<ƒ extends AtomIO.ƒn>(
|
|
7
12
|
token: AtomIO.TransactionToken<ƒ>,
|
|
8
13
|
socket: Socket,
|
|
9
|
-
updateQueue: AtomIO.TransactionUpdate<any>[],
|
|
10
14
|
store: Internal.Store,
|
|
11
15
|
): () => void {
|
|
12
|
-
const
|
|
16
|
+
const optimisticQueue = AtomIO.getState(optimisticUpdateQueueState, store)
|
|
17
|
+
const confirmedQueue = AtomIO.getState(confirmedUpdateQueueState, store)
|
|
13
18
|
|
|
14
19
|
const unsubscribeFromLocalUpdates = Internal.subscribeToTransaction(
|
|
15
20
|
token,
|
|
16
21
|
(clientUpdate) => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
const optimisticUpdateQueueIndex = optimisticQueue.findIndex(
|
|
23
|
+
(update) => update.id === clientUpdate.id,
|
|
24
|
+
)
|
|
25
|
+
if (optimisticUpdateQueueIndex === -1) {
|
|
26
|
+
AtomIO.setState(
|
|
27
|
+
optimisticUpdateQueueState,
|
|
28
|
+
(queue) => {
|
|
29
|
+
queue.push(clientUpdate)
|
|
30
|
+
queue.sort((a, b) => a.epoch - b.epoch)
|
|
31
|
+
return queue
|
|
32
|
+
},
|
|
33
|
+
store,
|
|
34
|
+
)
|
|
35
|
+
socket.emit(`tx-run:${token.key}`, clientUpdate)
|
|
36
|
+
} else {
|
|
37
|
+
AtomIO.setState(
|
|
38
|
+
optimisticUpdateQueueState,
|
|
39
|
+
(queue) => {
|
|
40
|
+
queue[optimisticUpdateQueueIndex] = clientUpdate
|
|
41
|
+
return queue
|
|
42
|
+
},
|
|
43
|
+
store,
|
|
44
|
+
)
|
|
45
|
+
socket.emit(`tx-run:${token.key}`, clientUpdate)
|
|
46
|
+
}
|
|
22
47
|
},
|
|
23
|
-
`tx-run:${token.key}
|
|
48
|
+
`tx-run:${token.key}`,
|
|
24
49
|
store,
|
|
25
50
|
)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const clientResult = JSON.stringify(
|
|
40
|
-
const serverResult = JSON.stringify(
|
|
41
|
-
if (clientResult
|
|
42
|
-
store.logger.
|
|
43
|
-
|
|
51
|
+
const reconcileUpdates = (
|
|
52
|
+
optimisticUpdate: AtomIO.TransactionUpdate<ƒ>,
|
|
53
|
+
confirmedUpdate: AtomIO.TransactionUpdate<ƒ>,
|
|
54
|
+
) => {
|
|
55
|
+
AtomIO.setState(
|
|
56
|
+
optimisticUpdateQueueState,
|
|
57
|
+
(queue) => {
|
|
58
|
+
queue.shift()
|
|
59
|
+
return queue
|
|
60
|
+
},
|
|
61
|
+
store,
|
|
62
|
+
)
|
|
63
|
+
if (optimisticUpdate.id === confirmedUpdate.id) {
|
|
64
|
+
const clientResult = JSON.stringify(optimisticUpdate.updates)
|
|
65
|
+
const serverResult = JSON.stringify(confirmedUpdate.updates)
|
|
66
|
+
if (clientResult === serverResult) {
|
|
67
|
+
store.logger.info(
|
|
68
|
+
`✅`,
|
|
44
69
|
`transaction`,
|
|
45
70
|
token.key,
|
|
46
|
-
`results
|
|
47
|
-
{ clientResult, serverResult },
|
|
71
|
+
`results for ${optimisticUpdate.id} match between client and server`,
|
|
48
72
|
)
|
|
49
|
-
|
|
73
|
+
socket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
// id mismatch
|
|
78
|
+
store.logger.info(
|
|
79
|
+
`❌`,
|
|
80
|
+
`transaction`,
|
|
81
|
+
token.key,
|
|
82
|
+
`${store.config.name} thought update #${confirmedUpdate.epoch} was ${optimisticUpdate.key}:${optimisticUpdate.id}, but it was actually ${confirmedUpdate.key}:${confirmedUpdate.id}`,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
for (const subsequentOptimistic of optimisticQueue.toReversed()) {
|
|
86
|
+
Internal.ingestTransactionUpdate(`oldValue`, subsequentOptimistic, store)
|
|
87
|
+
}
|
|
88
|
+
Internal.ingestTransactionUpdate(`oldValue`, optimisticUpdate, store)
|
|
89
|
+
Internal.ingestTransactionUpdate(`newValue`, confirmedUpdate, store)
|
|
90
|
+
socket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch)
|
|
91
|
+
for (const subsequentOptimistic of optimisticQueue) {
|
|
92
|
+
const token = Object.assign(
|
|
93
|
+
{ type: `transaction` } as const,
|
|
94
|
+
subsequentOptimistic,
|
|
95
|
+
)
|
|
96
|
+
const { id, params } = subsequentOptimistic
|
|
97
|
+
AtomIO.runTransaction(token, id, store)(...params)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const registerAndAttemptConfirmedUpdate = (
|
|
102
|
+
confirmedUpdate: AtomIO.TransactionUpdate<ƒ>,
|
|
103
|
+
) => {
|
|
104
|
+
const zerothOptimisticUpdate = optimisticQueue[0]
|
|
105
|
+
if (zerothOptimisticUpdate) {
|
|
106
|
+
if (zerothOptimisticUpdate.epoch === confirmedUpdate.epoch) {
|
|
107
|
+
reconcileUpdates(zerothOptimisticUpdate, confirmedUpdate)
|
|
108
|
+
for (const nextConfirmed of confirmedQueue) {
|
|
109
|
+
const nextOptimistic = optimisticQueue[0]
|
|
110
|
+
if (nextConfirmed.epoch === nextOptimistic.epoch) {
|
|
111
|
+
reconcileUpdates(nextOptimistic, nextConfirmed)
|
|
112
|
+
} else {
|
|
113
|
+
break
|
|
114
|
+
}
|
|
115
|
+
}
|
|
50
116
|
} else {
|
|
117
|
+
// epoch mismatch
|
|
118
|
+
|
|
119
|
+
const hasEnqueuedOptimisticUpdate = optimisticQueue.some(
|
|
120
|
+
(update) => update.epoch === confirmedUpdate.epoch,
|
|
121
|
+
)
|
|
122
|
+
if (hasEnqueuedOptimisticUpdate) {
|
|
123
|
+
AtomIO.setState(
|
|
124
|
+
confirmedUpdateQueueState,
|
|
125
|
+
(queue) => {
|
|
126
|
+
queue.push(confirmedUpdate)
|
|
127
|
+
queue.sort((a, b) => a.epoch - b.epoch)
|
|
128
|
+
return queue
|
|
129
|
+
},
|
|
130
|
+
store,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
if (
|
|
136
|
+
isRootStore(store) &&
|
|
137
|
+
store.transactionMeta.epoch === confirmedUpdate.epoch - 1
|
|
138
|
+
) {
|
|
139
|
+
Internal.ingestTransactionUpdate(`newValue`, confirmedUpdate, store)
|
|
140
|
+
socket.emit(`tx-ack:${token.key}`, confirmedUpdate.epoch)
|
|
141
|
+
store.transactionMeta.epoch = confirmedUpdate.epoch
|
|
142
|
+
} else if (isRootStore(store)) {
|
|
51
143
|
store.logger.info(
|
|
52
|
-
|
|
144
|
+
`❌`,
|
|
53
145
|
`transaction`,
|
|
54
146
|
token.key,
|
|
55
|
-
`
|
|
147
|
+
`received out-of-order update from server`,
|
|
148
|
+
{
|
|
149
|
+
clientEpoch: store.transactionMeta.epoch,
|
|
150
|
+
serverEpoch: confirmedUpdate.epoch,
|
|
151
|
+
},
|
|
56
152
|
)
|
|
57
153
|
}
|
|
58
|
-
AtomIO.setState(updateQueueState, (queue) => {
|
|
59
|
-
queue.shift()
|
|
60
|
-
return queue
|
|
61
|
-
})
|
|
62
154
|
}
|
|
63
|
-
Internal.ingestTransactionUpdate(`newValue`, serverUpdate, store)
|
|
64
155
|
}
|
|
65
|
-
socket.
|
|
156
|
+
socket.off(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate)
|
|
157
|
+
socket.on(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate)
|
|
66
158
|
socket.emit(`tx-sub:${token.key}`)
|
|
67
159
|
const unsubscribeFromIncomingUpdates = () => {
|
|
68
|
-
socket.off(`tx-new:${token.key}`,
|
|
160
|
+
socket.off(`tx-new:${token.key}`, registerAndAttemptConfirmedUpdate)
|
|
69
161
|
socket.emit(`tx-unsub:${token.key}`)
|
|
70
162
|
}
|
|
71
163
|
return () => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as AtomIO from "atom.io"
|
|
2
|
+
import type { Store } from "atom.io/internal"
|
|
3
|
+
import type { Json } from "atom.io/json"
|
|
4
|
+
import type { Socket } from "socket.io-client"
|
|
5
|
+
|
|
6
|
+
export function syncState<J extends Json.Serializable>(
|
|
7
|
+
token: AtomIO.WritableToken<J>,
|
|
8
|
+
socket: Socket,
|
|
9
|
+
store: Store,
|
|
10
|
+
): () => void {
|
|
11
|
+
const setServedValue = (data: J) => {
|
|
12
|
+
AtomIO.setState(token, data, store)
|
|
13
|
+
}
|
|
14
|
+
socket.on(`value:${token.key}`, setServedValue)
|
|
15
|
+
socket.emit(`get:${token.key}`)
|
|
16
|
+
return () => {
|
|
17
|
+
socket.off(`value:${token.key}`, setServedValue)
|
|
18
|
+
}
|
|
19
|
+
}
|