atom.io 0.18.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/{chunk-OEVFAUPE.js → chunk-IZHOMSXA.js} +53 -11
  2. package/dist/chunk-IZHOMSXA.js.map +1 -0
  3. package/dist/chunk-JDUNWJFB.js +18 -0
  4. package/dist/chunk-JDUNWJFB.js.map +1 -0
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.ts +5 -1
  7. package/dist/index.js.map +1 -1
  8. package/internal/dist/index.cjs +64 -34
  9. package/internal/dist/index.cjs.map +1 -1
  10. package/internal/dist/index.d.ts +1 -1
  11. package/internal/dist/index.js +64 -34
  12. package/internal/dist/index.js.map +1 -1
  13. package/internal/src/atom/delete-atom.ts +7 -6
  14. package/internal/src/caching.ts +6 -6
  15. package/internal/src/ingest-updates/ingest-atom-update.ts +6 -2
  16. package/internal/src/set-state/copy-mutable-if-needed.ts +5 -0
  17. package/internal/src/set-state/emit-update.ts +25 -11
  18. package/internal/src/set-state/set-atom.ts +4 -3
  19. package/internal/src/transaction/apply-transaction.ts +0 -1
  20. package/internal/src/transaction/set-epoch-number.ts +0 -1
  21. package/json/src/index.ts +3 -3
  22. package/package.json +241 -241
  23. package/react-devtools/dist/index.cjs.map +1 -1
  24. package/react-devtools/dist/index.js +1 -15
  25. package/react-devtools/dist/index.js.map +1 -1
  26. package/react-devtools/src/StateEditor.tsx +6 -6
  27. package/react-devtools/src/StateIndex.tsx +2 -2
  28. package/react-devtools/src/Updates.tsx +1 -1
  29. package/react-devtools/src/index.ts +3 -3
  30. package/realtime/dist/index.cjs +50 -2
  31. package/realtime/dist/index.cjs.map +1 -1
  32. package/realtime/dist/index.d.ts +110 -3
  33. package/realtime/dist/index.js +47 -4
  34. package/realtime/dist/index.js.map +1 -1
  35. package/realtime/src/index.ts +1 -0
  36. package/realtime/src/realtime-continuity.ts +14 -4
  37. package/realtime/src/shared-room-store.ts +48 -0
  38. package/realtime-client/dist/index.cjs +113 -200
  39. package/realtime-client/dist/index.cjs.map +1 -1
  40. package/realtime-client/dist/index.d.ts +2 -5
  41. package/realtime-client/dist/index.js +17 -161
  42. package/realtime-client/dist/index.js.map +1 -1
  43. package/realtime-client/src/index.ts +0 -2
  44. package/realtime-client/src/pull-mutable-atom-family-member.ts +5 -5
  45. package/realtime-client/src/realtime-client-stores/client-main-store.ts +10 -0
  46. package/realtime-client/src/sync-continuity.ts +56 -9
  47. package/realtime-react/dist/index.cjs +51 -26
  48. package/realtime-react/dist/index.cjs.map +1 -1
  49. package/realtime-react/dist/index.d.ts +2 -6
  50. package/realtime-react/dist/index.js +2 -17
  51. package/realtime-react/dist/index.js.map +1 -1
  52. package/realtime-react/src/index.ts +0 -2
  53. package/realtime-server/dist/index.cjs +399 -327
  54. package/realtime-server/dist/index.cjs.map +1 -1
  55. package/realtime-server/dist/index.d.ts +55 -60
  56. package/realtime-server/dist/index.js +394 -319
  57. package/realtime-server/dist/index.js.map +1 -1
  58. package/realtime-server/src/index.ts +2 -4
  59. package/realtime-server/src/ipc-sockets/child-socket.ts +135 -0
  60. package/realtime-server/src/ipc-sockets/custom-socket.ts +90 -0
  61. package/realtime-server/src/ipc-sockets/index.ts +3 -0
  62. package/realtime-server/src/ipc-sockets/parent-socket.ts +185 -0
  63. package/realtime-server/src/realtime-continuity-synchronizer.ts +225 -96
  64. package/realtime-server/src/realtime-server-stores/index.ts +2 -1
  65. package/realtime-server/src/realtime-server-stores/realtime-continuity-store.ts +50 -31
  66. package/realtime-server/src/realtime-server-stores/server-room-external-actions.ts +64 -0
  67. package/realtime-server/src/realtime-server-stores/server-room-external-store.ts +42 -0
  68. package/realtime-server/src/realtime-server-stores/server-sync-store.ts +49 -26
  69. package/realtime-testing/dist/index.cjs +8 -6
  70. package/realtime-testing/dist/index.cjs.map +1 -1
  71. package/realtime-testing/dist/index.d.ts +1 -0
  72. package/realtime-testing/dist/index.js +7 -6
  73. package/realtime-testing/dist/index.js.map +1 -1
  74. package/realtime-testing/src/setup-realtime-test.tsx +8 -6
  75. package/src/logger.ts +5 -1
  76. package/dist/chunk-OEVFAUPE.js.map +0 -1
  77. package/realtime-client/src/sync-server-action.ts +0 -168
  78. package/realtime-client/src/sync-state.ts +0 -19
  79. package/realtime-react/src/use-sync-server-action.ts +0 -17
  80. package/realtime-react/src/use-sync.ts +0 -17
  81. package/realtime-server/src/ipc-socket.ts +0 -230
  82. package/realtime-server/src/realtime-action-synchronizer.ts +0 -164
  83. package/realtime-server/src/realtime-server-stores/server-room-store.ts +0 -97
@@ -1,14 +1,14 @@
1
1
  import { __spreadProps, __spreadValues } from '../../dist/chunk-PZLG2HP3.js';
2
- import { Subject, findInStore, getFromStore, subscribeToState, IMPLICIT, getJsonToken, getUpdateToken, assignTransactionToContinuity, setIntoStore, isRootStore, subscribeToTransaction, actUponStore } from 'atom.io/internal';
3
2
  import { parseJson, stringifyJson } from 'atom.io/json';
3
+ import { getUpdateToken, IMPLICIT, Subject, findInStore, getFromStore, subscribeToState, getJsonToken, isRootStore, subscribeToTransaction, actUponStore, setIntoStore } from 'atom.io/internal';
4
+ import { SetRTX } from 'atom.io/transceivers/set-rtx';
4
5
  import * as AtomIO from 'atom.io';
5
6
  import { selectorFamily, atomFamily, atom } from 'atom.io';
6
- import { SyncGroup } from 'atom.io/realtime';
7
- import { completeUpdateAtoms } from 'atom.io/realtime-server';
7
+ import { SyncGroup, roomIndex, usersInRooms } from 'atom.io/realtime';
8
8
  import { spawn } from 'child_process';
9
9
  import { join } from 'atom.io/data';
10
- import { SetRTX } from 'atom.io/transceivers/set-rtx';
11
10
 
11
+ // realtime-server/src/ipc-sockets/custom-socket.ts
12
12
  var CustomSocket = class {
13
13
  constructor(emit) {
14
14
  this.emit = emit;
@@ -43,7 +43,11 @@ var CustomSocket = class {
43
43
  off(event, listener) {
44
44
  const listeners = this.listeners.get(event);
45
45
  if (listeners) {
46
- listeners.delete(listener);
46
+ if (listener) {
47
+ listeners.delete(listener);
48
+ } else {
49
+ this.listeners.delete(event);
50
+ }
47
51
  }
48
52
  return this;
49
53
  }
@@ -52,28 +56,109 @@ var CustomSocket = class {
52
56
  return this;
53
57
  }
54
58
  };
59
+
60
+ // realtime-server/src/ipc-sockets/child-socket.ts
55
61
  var ChildSocket = class extends CustomSocket {
56
- constructor(process2) {
62
+ constructor(process2, key, logger = console) {
57
63
  super((event, ...args) => {
58
- const stringifiedEvent = JSON.stringify([event, ...args]) + `
59
- `;
64
+ const stringifiedEvent = JSON.stringify([event, ...args]) + ``;
65
+ const errorHandler = (err) => {
66
+ if (err.code === `EPIPE`) {
67
+ console.error(`EPIPE error during write`, this.process.stdin);
68
+ }
69
+ this.process.stdin.removeListener(`error`, errorHandler);
70
+ };
71
+ this.process.stdin.once(`error`, errorHandler);
60
72
  this.process.stdin.write(stringifiedEvent);
61
73
  return this;
62
74
  });
63
- this.id = `no_id_retrieved`;
75
+ this.process = process2;
76
+ this.key = key;
77
+ this.logger = logger;
78
+ this.incompleteData = ``;
79
+ this.unprocessedEvents = [];
80
+ this.incompleteLog = ``;
81
+ this.unprocessedLogs = [];
82
+ this.id = `#####`;
64
83
  this.process = process2;
65
84
  this.process.stdout.on(
66
85
  `data`,
67
86
  (buffer) => {
68
- const stringifiedEvent = buffer.toString();
69
- const parsedEvent = parseJson(stringifiedEvent);
70
- this.handleEvent(...parsedEvent);
87
+ const chunk = buffer.toString();
88
+ if (chunk === `\u2728`) {
89
+ return;
90
+ }
91
+ this.unprocessedEvents.push(...chunk.split(``));
92
+ const newInput = this.unprocessedEvents.shift();
93
+ this.incompleteData += newInput || ``;
94
+ try {
95
+ if (this.incompleteData.startsWith(`error`)) {
96
+ console.log(`\u2757`, this.incompleteData);
97
+ }
98
+ const parsedEvent = parseJson(this.incompleteData);
99
+ this.handleEvent(...parsedEvent);
100
+ while (this.unprocessedEvents.length > 0) {
101
+ const event = this.unprocessedEvents.shift();
102
+ if (event) {
103
+ if (this.unprocessedEvents.length === 0) {
104
+ this.incompleteData = event;
105
+ }
106
+ const parsedEvent2 = parseJson(event);
107
+ this.handleEvent(...parsedEvent2);
108
+ }
109
+ }
110
+ this.incompleteData = ``;
111
+ } catch (error) {
112
+ console.warn(`\u26A0\uFE0F----------------\u26A0\uFE0F`);
113
+ console.warn(this.incompleteData);
114
+ console.warn(`\u26A0\uFE0F----------------\u26A0\uFE0F`);
115
+ console.error(error);
116
+ }
71
117
  }
72
118
  );
119
+ this.process.stderr.on(`data`, (buf) => {
120
+ var _a;
121
+ const chunk = buf.toString();
122
+ this.unprocessedLogs.push(...chunk.split(``));
123
+ const newInput = this.unprocessedLogs.shift();
124
+ this.incompleteLog += newInput || ``;
125
+ try {
126
+ const parsedLog = parseJson(this.incompleteLog);
127
+ this.handleLog(parsedLog);
128
+ while (this.unprocessedLogs.length > 0) {
129
+ this.incompleteLog = (_a = this.unprocessedLogs.shift()) != null ? _a : ``;
130
+ if (this.incompleteLog) {
131
+ const parsedLog2 = parseJson(this.incompleteLog);
132
+ this.handleLog(parsedLog2);
133
+ }
134
+ }
135
+ } catch (error) {
136
+ console.error(`\u274C\u274C\u274C`);
137
+ console.error(this.incompleteLog);
138
+ console.error(error);
139
+ console.error(`\u274C\u274C\u274C\uFE0F`);
140
+ }
141
+ });
73
142
  if (process2.pid) {
74
143
  this.id = process2.pid.toString();
75
144
  }
76
145
  }
146
+ handleLog(arg) {
147
+ if (Array.isArray(arg)) {
148
+ const [level, ...rest] = arg;
149
+ switch (level) {
150
+ case `i`:
151
+ this.logger.info(this.id, this.key, ...rest);
152
+ break;
153
+ case `w`:
154
+ this.logger.warn(this.id, this.key, ...rest);
155
+ break;
156
+ case `e`:
157
+ this.logger.error(this.id, this.key, ...rest);
158
+ break;
159
+ }
160
+ }
161
+ }
77
162
  };
78
163
  var SubjectSocket = class extends CustomSocket {
79
164
  constructor(id) {
@@ -82,6 +167,7 @@ var SubjectSocket = class extends CustomSocket {
82
167
  return this;
83
168
  });
84
169
  this.id = `no_id_retrieved`;
170
+ this.disposalFunctions = [];
85
171
  this.id = id;
86
172
  this.in = new Subject();
87
173
  this.out = new Subject();
@@ -89,69 +175,130 @@ var SubjectSocket = class extends CustomSocket {
89
175
  this.handleEvent(...event);
90
176
  });
91
177
  }
178
+ dispose() {
179
+ for (const dispose of this.disposalFunctions) {
180
+ dispose();
181
+ }
182
+ }
92
183
  };
93
184
  var ParentSocket = class extends CustomSocket {
94
185
  constructor() {
95
186
  var _a;
96
187
  super((event, ...args) => {
97
188
  const stringifiedEvent = JSON.stringify([event, ...args]);
98
- this.process.stdout.write(stringifiedEvent);
189
+ this.process.stdout.write(stringifiedEvent + ``);
99
190
  return this;
100
191
  });
101
- this.id = `no_id_retrieved`;
192
+ this.incompleteData = ``;
193
+ this.unprocessedEvents = [];
194
+ this.id = `#####`;
195
+ this.logger = {
196
+ info: (...args) => this.log(`i`, ...args),
197
+ warn: (...args) => this.log(`w`, ...args),
198
+ error: (...args) => this.log(`e`, ...args)
199
+ };
102
200
  this.process = process;
103
201
  this.process.stdin.resume();
104
- this.queue = [];
105
202
  this.relays = /* @__PURE__ */ new Map();
106
203
  this.relayServices = [];
107
204
  this.process.stdin.on(
108
205
  `data`,
109
- (chunk) => {
110
- const buffer = chunk.toString();
111
- this.queue.push(...buffer.split(`
112
- `));
113
- while (this.queue.length > 0) {
114
- try {
115
- const event = this.queue.shift();
116
- if (event === ``)
117
- continue;
118
- const parsedEvent = parseJson(event);
119
- this.handleEvent(...parsedEvent);
120
- } catch (error) {
121
- this.process.stderr.write(`\u274C ${error}
122
- `);
123
- break;
206
+ (buffer) => {
207
+ const chunk = buffer.toString();
208
+ this.unprocessedEvents.push(...chunk.split(``));
209
+ const newInput = this.unprocessedEvents.shift();
210
+ this.incompleteData += newInput || ``;
211
+ try {
212
+ const parsedEvent = parseJson(this.incompleteData);
213
+ this.logger.info(`\u{1F3B0}`, `received`, parsedEvent);
214
+ this.handleEvent(...parsedEvent);
215
+ while (this.unprocessedEvents.length > 0) {
216
+ const event = this.unprocessedEvents.shift();
217
+ if (event) {
218
+ if (this.unprocessedEvents.length === 0) {
219
+ this.incompleteData = event;
220
+ }
221
+ const parsedEvent2 = parseJson(event);
222
+ this.handleEvent(...parsedEvent2);
223
+ }
224
+ }
225
+ this.incompleteData = ``;
226
+ } catch (thrown) {
227
+ if (thrown instanceof Error) {
228
+ this.logger.error(`\u2757`, thrown.message, thrown.cause, thrown.stack);
124
229
  }
125
230
  }
126
231
  }
127
232
  );
128
- process.on(`SIGINT`, () => process.exit(0));
233
+ this.on(`exit`, () => {
234
+ process.exit(0);
235
+ });
236
+ process.on(`exit`, () => {
237
+ this.logger.info(`\u{1F525}`, this.id, `exited`);
238
+ process.exit(0);
239
+ });
240
+ process.on(`end`, () => {
241
+ this.logger.info(`\u{1F525}`, this.id, `ended`);
242
+ process.exit(0);
243
+ });
244
+ process.on(`SIGTERM`, () => {
245
+ this.logger.error(`\u{1F525}`, this.id, `terminated`);
246
+ process.exit(0);
247
+ });
248
+ process.on(`SIGINT`, () => {
249
+ this.logger.error(`\u{1F525}`, this.id, `interrupted`);
250
+ process.exit(0);
251
+ });
129
252
  if (process.pid) {
130
253
  this.id = (_a = process.pid) == null ? void 0 : _a.toString();
131
254
  }
132
- this.on(`setup-relay`, (id) => {
133
- const relay = new SubjectSocket(`relay:${id}`);
134
- this.relays.set(id, relay);
255
+ this.on(`user-joins`, (username) => {
256
+ this.logger.info(`\u{1F464}`, `user`, username, `joined`);
257
+ const relay = new SubjectSocket(`user:${username}`);
258
+ this.relays.set(username, relay);
259
+ this.logger.info(
260
+ `\u{1F517}`,
261
+ `attaching services:`,
262
+ `[${[...this.relayServices.keys()].join(`, `)}]`
263
+ );
135
264
  for (const attachServices of this.relayServices) {
136
- attachServices(relay);
265
+ const cleanup = attachServices(relay);
266
+ if (cleanup) {
267
+ relay.disposalFunctions.push(cleanup);
268
+ }
137
269
  }
138
- this.on(`relay:${id}`, (...data) => {
270
+ this.on(`user:${username}`, (...data) => {
139
271
  relay.in.next(data);
140
272
  });
141
273
  relay.out.subscribe(`socket`, (data) => {
142
274
  this.emit(...data);
143
275
  });
144
276
  });
277
+ this.on(`user-leaves`, (username) => {
278
+ const relay = this.relays.get(username);
279
+ this.off(`relay:${username}`);
280
+ if (relay) {
281
+ relay.dispose();
282
+ this.relays.delete(username);
283
+ }
284
+ });
285
+ process.stdout.write(`\u2728`);
286
+ }
287
+ log(...args) {
288
+ this.process.stderr.write(
289
+ stringifyJson(
290
+ args.map(
291
+ (arg) => arg instanceof SetRTX ? `{ ${arg.toJSON().members.join(` | `)} }` : arg
292
+ )
293
+ ) + ``
294
+ );
145
295
  }
146
296
  relay(attachServices) {
297
+ this.logger.info(`\u{1F517}`, `running relay method`);
147
298
  this.relayServices.push(attachServices);
148
- const relays = this.relays.values();
149
- for (const relay of relays) {
150
- attachServices(relay);
151
- }
152
299
  }
153
300
  };
154
- var redactorAtoms = selectorFamily({
301
+ selectorFamily({
155
302
  key: `perspectiveRedactor`,
156
303
  get: ({ userId, syncGroupKey }) => ({ get, find }) => {
157
304
  const syncGroup = SyncGroup.existing.get(syncGroupKey);
@@ -161,17 +308,31 @@ var redactorAtoms = selectorFamily({
161
308
  );
162
309
  }
163
310
  const userPerspectiveTokens = syncGroup.perspectives.flatMap(
164
- ({ perspectiveAtoms, resourceAtoms }) => {
165
- const userPerspectiveToken = find(perspectiveAtoms, userId);
311
+ ({ viewAtoms }) => {
312
+ const userPerspectiveToken = find(viewAtoms, userId);
166
313
  const userPerspective = get(userPerspectiveToken);
167
- const visibleTokens = [...userPerspective].map((subKey) => {
168
- const resourceToken = find(resourceAtoms, subKey);
169
- return resourceToken.key;
314
+ const visibleTokens = [...userPerspective].map((token) => {
315
+ return token.type === `mutable_atom` ? getUpdateToken(token).key : token.key;
170
316
  });
317
+ IMPLICIT.STORE.logger.info(
318
+ `\u{1F52D}`,
319
+ `continuity`,
320
+ syncGroupKey,
321
+ `${userId} can see ${visibleTokens.length} tokens in ${viewAtoms.key}`,
322
+ visibleTokens
323
+ );
171
324
  return visibleTokens;
172
325
  }
173
326
  );
174
327
  const filterTransactionUpdate = (visible, transactionUpdate) => {
328
+ IMPLICIT.STORE.logger.info(
329
+ `\u{1F58C}`,
330
+ `continuity`,
331
+ syncGroupKey,
332
+ `redacting updates from ${transactionUpdate.epoch}:${transactionUpdate.key}:${transactionUpdate.id}`,
333
+ visible,
334
+ transactionUpdate.updates
335
+ );
175
336
  const updates = transactionUpdate.updates.filter((update) => {
176
337
  if (`newValue` in update) {
177
338
  return visible.includes(update.key);
@@ -190,7 +351,7 @@ var redactorAtoms = selectorFamily({
190
351
  };
191
352
  const filter = (update) => {
192
353
  const visibleKeys = syncGroup.globals.map(
193
- (atomToken) => atomToken.key
354
+ (atomToken) => atomToken.type === `mutable_atom` ? getUpdateToken(atomToken).key : atomToken.key
194
355
  );
195
356
  visibleKeys.push(...userPerspectiveTokens);
196
357
  return filterTransactionUpdate(visibleKeys, update);
@@ -198,60 +359,33 @@ var redactorAtoms = selectorFamily({
198
359
  return filter;
199
360
  }
200
361
  });
201
- var redactedPerspectiveUpdateSelectors = selectorFamily({
202
- key: `redactedPerspectiveUpdate`,
203
- get: ({ userId, syncGroupKey, updateId }) => ({ get, find }) => {
204
- const updateState = find(completeUpdateAtoms, updateId);
205
- const update = get(updateState);
206
- const redactorKey = { userId, syncGroupKey };
207
- const redactorState = find(redactorAtoms, redactorKey);
208
- const redact = get(redactorState);
209
- if (update) {
210
- return redact(update);
211
- }
212
- return null;
213
- }
214
- });
215
- var roomIndex = AtomIO.atom({
216
- key: `roomIndex`,
217
- default: () => new SetRTX(),
218
- mutable: true,
219
- toJson: (set) => set.toJSON(),
220
- fromJson: (json) => SetRTX.fromJSON(json)
221
- });
222
- var DEFAULT_USER_IN_ROOM_META = {
223
- enteredAtEpoch: 0
224
- };
225
- var usersInRooms = join(
226
- {
227
- key: `usersInRooms`,
228
- between: [`room`, `user`],
229
- cardinality: `1:n`
230
- },
231
- DEFAULT_USER_IN_ROOM_META
232
- );
233
- var roomArgumentsAtoms = AtomIO.atomFamily({
362
+ var roomArgumentsAtoms = atomFamily({
234
363
  key: `roomArguments`,
235
- default: [`echo Hello World!`]
364
+ default: [`echo`, [`Hello World!`]]
236
365
  });
237
- var roomSelectors = AtomIO.selectorFamily({
366
+ var roomSelectors = selectorFamily({
238
367
  key: `room`,
239
- get: (roomId) => ({ get, find }) => {
368
+ get: (roomId) => async ({ get, find }) => {
240
369
  const argumentsState = find(roomArgumentsAtoms, roomId);
241
370
  const args = get(argumentsState);
242
371
  const [script, options] = args;
243
- return new Promise((resolve) => {
244
- const room = spawn(script, options, { env: process.env });
245
- const resolver = (data) => {
246
- if (data.toString() === `\u2728`) {
247
- room.stdout.off(`data`, resolver);
248
- resolve(room);
249
- }
250
- };
251
- room.stdout.on(`data`, resolver);
252
- });
372
+ const child = await new Promise(
373
+ (resolve) => {
374
+ const room = spawn(script, options, { env: process.env });
375
+ const resolver = (data) => {
376
+ if (data.toString() === `\u2728`) {
377
+ room.stdout.off(`data`, resolver);
378
+ resolve(room);
379
+ }
380
+ };
381
+ room.stdout.on(`data`, resolver);
382
+ }
383
+ );
384
+ return new ChildSocket(child, roomId);
253
385
  }
254
386
  });
387
+
388
+ // realtime-server/src/realtime-server-stores/server-room-external-actions.ts
255
389
  var createRoomTX = AtomIO.transaction({
256
390
  key: `createRoom`,
257
391
  do: ({ get, set, find }, roomId, script, options) => {
@@ -274,24 +408,43 @@ var joinRoomTX = AtomIO.transaction({
274
408
  return meta;
275
409
  }
276
410
  });
277
- var completeUpdateAtoms2 = atomFamily({
278
- key: `completeUpdate`,
279
- default: null
411
+ var leaveRoomTX = AtomIO.transaction({
412
+ key: `leaveRoom`,
413
+ do: (transactors, roomId, userId) => {
414
+ usersInRooms.transact(transactors, ({ relations }) => {
415
+ relations.delete({ room: roomId, user: userId });
416
+ });
417
+ }
280
418
  });
281
- var transactionRedactorAtoms = atomFamily({
282
- key: `transactionRedactor`,
283
- default: { filter: (updates) => updates }
419
+ var destroyRoomTX = AtomIO.transaction({
420
+ key: `destroyRoom`,
421
+ do: (transactors, roomId) => {
422
+ usersInRooms.transact(transactors, ({ relations }) => {
423
+ relations.delete({ room: roomId });
424
+ });
425
+ transactors.set(roomIndex, (s) => (s.delete(roomId), s));
426
+ }
284
427
  });
285
- var redactedUpdateSelectors = selectorFamily({
286
- key: `redactedUpdate`,
287
- get: ([transactionKey, updateId]) => ({ get, find }) => {
288
- const update = get(find(completeUpdateAtoms2, updateId));
289
- const { filter } = get(find(transactionRedactorAtoms, transactionKey));
290
- if (update && filter) {
291
- return __spreadProps(__spreadValues({}, update), { updates: filter(update.updates) });
428
+ function redactTransactionUpdateContent(visibleStateKeys, updates) {
429
+ return updates.map((update) => {
430
+ if (`newValue` in update) {
431
+ return update;
292
432
  }
293
- return null;
294
- }
433
+ const redacted = redactTransactionUpdateContent(
434
+ visibleStateKeys,
435
+ update.updates
436
+ );
437
+ return __spreadProps(__spreadValues({}, update), { updates: redacted });
438
+ }).filter((update) => {
439
+ if (`newValue` in update) {
440
+ return visibleStateKeys.includes(update.key);
441
+ }
442
+ return true;
443
+ });
444
+ }
445
+ var actionOcclusionAtoms = atomFamily({
446
+ key: `transactionRedactor`,
447
+ default: { occlude: (updates) => updates }
295
448
  });
296
449
  var userUnacknowledgedQueues = atomFamily({
297
450
  key: `unacknowledgedUpdates`,
@@ -380,27 +533,74 @@ function realtimeContinuitySynchronizer({
380
533
  userKey,
381
534
  store
382
535
  );
383
- const userUnacknowledgedUpdates = getFromStore(
536
+ getFromStore(
384
537
  userUnacknowledgedQueue,
385
538
  store
386
539
  );
387
540
  const unsubscribeFunctions = [];
541
+ const revealPerspectives = () => {
542
+ const unsubscribeFunctions2 = [];
543
+ for (const perspective of continuity.perspectives) {
544
+ const { viewAtoms } = perspective;
545
+ const userViewState = findInStore(viewAtoms, userKey, store);
546
+ const unsubscribe = subscribeToState(
547
+ userViewState,
548
+ ({ oldValue, newValue }) => {
549
+ const oldKeys = oldValue.map((token) => token.key);
550
+ const newKeys = newValue.map((token) => token.key);
551
+ const concealed = oldValue.filter(
552
+ (token) => !newKeys.includes(token.key)
553
+ );
554
+ const revealed = newValue.filter((token) => !oldKeys.includes(token.key)).flatMap((token) => {
555
+ const resourceToken = token.type === `mutable_atom` ? getJsonToken(token) : token;
556
+ const resource = getFromStore(resourceToken, store);
557
+ return [resourceToken, resource];
558
+ });
559
+ store.logger.info(
560
+ `\u{1F441}`,
561
+ `atom`,
562
+ perspective.resourceAtoms.key,
563
+ `${userKey} has a new perspective`,
564
+ { oldKeys, newKeys, revealed, concealed }
565
+ );
566
+ if (revealed.length > 0) {
567
+ socket == null ? void 0 : socket.emit(`reveal:${continuityKey}`, revealed);
568
+ }
569
+ if (concealed.length > 0) {
570
+ socket == null ? void 0 : socket.emit(`conceal:${continuityKey}`, concealed);
571
+ }
572
+ },
573
+ `sync-continuity:${continuityKey}:${userKey}:perspective:${perspective.resourceAtoms.key}`,
574
+ store
575
+ );
576
+ unsubscribeFunctions2.push(unsubscribe);
577
+ }
578
+ return () => {
579
+ for (const unsubscribe of unsubscribeFunctions2)
580
+ unsubscribe();
581
+ };
582
+ };
583
+ const unsubscribeFromPerspectives = revealPerspectives();
388
584
  const sendInitialPayload = () => {
389
585
  var _a;
390
586
  const initialPayload = [];
391
- for (const atom3 of continuity.globals) {
392
- initialPayload.push(atom3, getFromStore(atom3, store));
587
+ for (const atom2 of continuity.globals) {
588
+ const resourceToken = atom2.type === `mutable_atom` ? getJsonToken(atom2) : atom2;
589
+ initialPayload.push(resourceToken, getFromStore(atom2, store));
393
590
  }
394
- for (const { perspectiveAtoms } of continuity.perspectives) {
395
- const perspectiveTokensState = findInStore(
396
- perspectiveAtoms,
397
- userKey,
398
- store
399
- );
400
- const perspectiveTokens = getFromStore(perspectiveTokensState, store);
401
- for (const perspectiveToken of perspectiveTokens) {
402
- const resource = getFromStore(perspectiveToken, store);
403
- initialPayload.push(perspectiveToken, resource);
591
+ for (const perspective of continuity.perspectives) {
592
+ const { viewAtoms, resourceAtoms } = perspective;
593
+ const userViewState = findInStore(viewAtoms, userKey, store);
594
+ const userView = getFromStore(userViewState, store);
595
+ store.logger.info(`\u{1F441}`, `atom`, resourceAtoms.key, `${userKey} can see`, {
596
+ viewAtoms,
597
+ resourceAtoms,
598
+ userView
599
+ });
600
+ for (const visibleToken of userView) {
601
+ const resourceToken = visibleToken.type === `mutable_atom` ? getJsonToken(visibleToken) : visibleToken;
602
+ const resource = getFromStore(resourceToken, store);
603
+ initialPayload.push(resourceToken, resource);
404
604
  }
405
605
  }
406
606
  const epoch = isRootStore(store) ? (_a = store.transactionMeta.epoch.get(continuityKey)) != null ? _a : null : null;
@@ -409,38 +609,47 @@ function realtimeContinuitySynchronizer({
409
609
  const unsubscribeFromTransaction = subscribeToTransaction(
410
610
  transaction2,
411
611
  (update) => {
412
- const updateState = findInStore(
413
- completeUpdateAtoms2,
414
- update.id,
415
- store
416
- );
417
- setIntoStore(updateState, update, store);
418
- const redactedUpdateKey = {
419
- userId: userKey,
420
- syncGroupKey: continuityKey,
421
- updateId: update.id
422
- };
423
- const redactedUpdateState = findInStore(
424
- redactedPerspectiveUpdateSelectors,
425
- redactedUpdateKey,
426
- store
427
- );
428
- const redactedUpdate = getFromStore(redactedUpdateState, store);
429
- setIntoStore(
430
- userUnacknowledgedQueue,
431
- (updates) => {
432
- if (redactedUpdate) {
433
- updates.push(redactedUpdate);
434
- updates.sort((a, b) => a.epoch - b.epoch);
435
- }
436
- return updates;
437
- },
438
- store
439
- );
440
- socket == null ? void 0 : socket.emit(
441
- `tx-new:${continuityKey}`,
442
- redactedUpdate
443
- );
612
+ try {
613
+ const visibleKeys = continuity.globals.map((atom2) => atom2.key).concat(
614
+ continuity.perspectives.flatMap((perspective) => {
615
+ const { viewAtoms } = perspective;
616
+ const userPerspectiveTokenState = findInStore(
617
+ viewAtoms,
618
+ userKey,
619
+ store
620
+ );
621
+ const visibleTokens = getFromStore(
622
+ userPerspectiveTokenState,
623
+ store
624
+ );
625
+ return visibleTokens.map((token) => {
626
+ const key = token.type === `mutable_atom` ? `*` + token.key : token.key;
627
+ return key;
628
+ });
629
+ })
630
+ );
631
+ const redactedUpdates = redactTransactionUpdateContent(
632
+ visibleKeys,
633
+ update.updates
634
+ );
635
+ const redactedUpdate = __spreadProps(__spreadValues({}, update), {
636
+ updates: redactedUpdates
637
+ });
638
+ socket == null ? void 0 : socket.emit(
639
+ `tx-new:${continuityKey}`,
640
+ redactedUpdate
641
+ );
642
+ } catch (thrown) {
643
+ if (thrown instanceof Error) {
644
+ store.logger.error(
645
+ `\u274C`,
646
+ `continuity`,
647
+ continuityKey,
648
+ `failed to send update from transaction ${transaction2.key} to ${userKey}`,
649
+ thrown.message
650
+ );
651
+ }
652
+ }
444
653
  },
445
654
  `sync-continuity:${continuityKey}:${userKey}`,
446
655
  store
@@ -451,17 +660,30 @@ function realtimeContinuitySynchronizer({
451
660
  socket.off(`get:${continuityKey}`, sendInitialPayload);
452
661
  socket.on(`get:${continuityKey}`, sendInitialPayload);
453
662
  const fillTransactionRequest = (update) => {
663
+ store.logger.info(`\u{1F6CE}\uFE0F`, `continuity`, continuityKey, `received`, update);
454
664
  const transactionKey = update.key;
455
665
  const updateId = update.id;
456
666
  const performanceKey = `tx-run:${transactionKey}:${updateId}`;
457
667
  const performanceKeyStart = `${performanceKey}:start`;
458
668
  const performanceKeyEnd = `${performanceKey}:end`;
459
669
  performance.mark(performanceKeyStart);
460
- actUponStore(
461
- { type: `transaction`, key: transactionKey },
462
- updateId,
463
- store
464
- )(...update.params);
670
+ try {
671
+ actUponStore(
672
+ { type: `transaction`, key: transactionKey },
673
+ updateId,
674
+ store
675
+ )(...update.params);
676
+ } catch (thrown) {
677
+ if (thrown instanceof Error) {
678
+ store.logger.error(
679
+ `\u274C`,
680
+ `continuity`,
681
+ continuityKey,
682
+ `failed to run transaction ${transactionKey} with update ${updateId}`,
683
+ thrown.message
684
+ );
685
+ }
686
+ }
465
687
  performance.mark(performanceKeyEnd);
466
688
  const metric = performance.measure(
467
689
  performanceKey,
@@ -475,56 +697,27 @@ function realtimeContinuitySynchronizer({
475
697
  updateId,
476
698
  metric.duration
477
699
  );
478
- };
479
- socket.off(`tx-run:${continuityKey}`, fillTransactionRequest);
480
- socket.on(`tx-run:${continuityKey}`, fillTransactionRequest);
481
- let i = 1;
482
- let next = 1;
483
- const retry = setInterval(() => {
484
- const toEmit = userUnacknowledgedUpdates[0];
700
+ const valuesOfCardsViewKey = `valuesOfCardsView("${userKey}")`;
701
+ const rootsOfCardValueView = store.selectorAtoms.getRelatedKeys(valuesOfCardsViewKey);
702
+ const myCardValueView = store.valueMap.get(valuesOfCardsViewKey);
485
703
  store.logger.info(
486
- `\u{1F504}`,
704
+ `\u{1F441}`,
487
705
  `continuity`,
488
706
  continuityKey,
489
- `${store.config.name} retrying ${userKey} (${i}/${next})`,
490
- socket == null ? void 0 : socket.id,
491
- userUnacknowledgedUpdates
492
- );
493
- if (toEmit && i === next) {
494
- socket == null ? void 0 : socket.emit(`tx-new:${continuityKey}`, toEmit);
495
- next *= 2;
496
- }
497
- i++;
498
- }, 250);
499
- const trackClientAcknowledgement = (epoch) => {
500
- var _a;
501
- store.logger.info(
502
- `\u{1F44D}`,
503
- `continuity`,
504
- continuityKey,
505
- `${userKey} acknowledged epoch ${epoch}`
707
+ `seeing ${userKey} card values`,
708
+ {
709
+ valuesOfCardsViewKey,
710
+ rootsOfCardValueView,
711
+ myCardValueView
712
+ }
506
713
  );
507
- i = 1;
508
- next = 1;
509
- const isUnacknowledged = ((_a = userUnacknowledgedUpdates[0]) == null ? void 0 : _a.epoch) === epoch;
510
- if (isUnacknowledged) {
511
- setIntoStore(
512
- userUnacknowledgedQueue,
513
- (updates) => {
514
- updates.shift();
515
- return updates;
516
- },
517
- store
518
- );
519
- }
520
714
  };
521
- socket.off(`ack:${continuityKey}`, trackClientAcknowledgement);
522
- socket.on(`ack:${continuityKey}`, trackClientAcknowledgement);
715
+ socket.off(`tx-run:${continuityKey}`, fillTransactionRequest);
716
+ socket.on(`tx-run:${continuityKey}`, fillTransactionRequest);
523
717
  return () => {
524
- clearInterval(retry);
525
718
  for (const unsubscribe of unsubscribeFunctions)
526
719
  unsubscribe();
527
- socket == null ? void 0 : socket.off(`ack:${continuityKey}`, trackClientAcknowledgement);
720
+ unsubscribeFromPerspectives();
528
721
  socket == null ? void 0 : socket.off(`get:${continuityKey}`, sendInitialPayload);
529
722
  socket == null ? void 0 : socket.off(`tx-run:${continuityKey}`, fillTransactionRequest);
530
723
  };
@@ -749,125 +942,7 @@ function realtimeActionReceiver({
749
942
  return () => socket.off(`tx-run:${tx.key}`, fillTransactionRequest);
750
943
  };
751
944
  }
752
- function realtimeActionSynchronizer({
753
- socket,
754
- store = IMPLICIT.STORE
755
- }) {
756
- return function actionSynchronizer(tx, filter) {
757
- assignTransactionToContinuity(`default`, tx.key, store);
758
- const userKeyState = findInStore(
759
- usersOfSockets.states.userKeyOfSocket,
760
- socket.id,
761
- store
762
- );
763
- const userKey = getFromStore(userKeyState, store);
764
- if (!userKey) {
765
- store.logger.error(
766
- `\u274C`,
767
- `transaction`,
768
- tx.key,
769
- `Tried to create a synchronizer for a socket (${socket.id}) that is not connected to a user.`
770
- );
771
- return () => {
772
- };
773
- }
774
- const userUnacknowledgedQueue = findInStore(
775
- userUnacknowledgedQueues,
776
- userKey,
777
- store
778
- );
779
- const userUnacknowledgedUpdates = getFromStore(
780
- userUnacknowledgedQueue,
781
- store
782
- );
783
- if (filter) {
784
- const redactorState = findInStore(transactionRedactorAtoms, tx.key, store);
785
- setIntoStore(redactorState, { filter }, store);
786
- }
787
- const fillTransactionRequest = (update) => {
788
- const performanceKey = `tx-run:${tx.key}:${update.id}`;
789
- const performanceKeyStart = `${performanceKey}:start`;
790
- const performanceKeyEnd = `${performanceKey}:end`;
791
- performance.mark(performanceKeyStart);
792
- actUponStore(tx, update.id, store)(...update.params);
793
- performance.mark(performanceKeyEnd);
794
- const metric = performance.measure(
795
- performanceKey,
796
- performanceKeyStart,
797
- performanceKeyEnd
798
- );
799
- store == null ? void 0 : store.logger.info(`\u{1F680}`, `transaction`, tx.key, update.id, metric.duration);
800
- };
801
- socket.off(`tx-run:${tx.key}`, fillTransactionRequest);
802
- socket.on(`tx-run:${tx.key}`, fillTransactionRequest);
803
- let unsubscribeFromTransaction;
804
- const fillTransactionSubscriptionRequest = () => {
805
- unsubscribeFromTransaction = subscribeToTransaction(
806
- tx,
807
- (update) => {
808
- const updateState = findInStore(completeUpdateAtoms2, update.id, store);
809
- setIntoStore(updateState, update, store);
810
- const toEmit = filter ? getFromStore(
811
- findInStore(redactedUpdateSelectors, [tx.key, update.id], store),
812
- store
813
- ) : update;
814
- setIntoStore(
815
- userUnacknowledgedQueue,
816
- (updates) => {
817
- if (toEmit) {
818
- updates.push(toEmit);
819
- updates.sort((a, b) => a.epoch - b.epoch);
820
- }
821
- return updates;
822
- },
823
- store
824
- );
825
- socket.emit(`tx-new:${tx.key}`, toEmit);
826
- },
827
- `tx-sub:${tx.key}:${socket.id}`,
828
- store
829
- );
830
- socket.on(`tx-unsub:${tx.key}`, unsubscribeFromTransaction);
831
- };
832
- socket.on(`tx-sub:${tx.key}`, fillTransactionSubscriptionRequest);
833
- let i = 1;
834
- let next = 1;
835
- const retry = setInterval(() => {
836
- const toEmit = userUnacknowledgedUpdates[0];
837
- if (toEmit && i === next) {
838
- socket.emit(`tx-new:${tx.key}`, toEmit);
839
- next *= 2;
840
- }
841
- i++;
842
- }, 250);
843
- const trackClientAcknowledgement = (epoch) => {
844
- var _a;
845
- i = 1;
846
- next = 1;
847
- if (((_a = userUnacknowledgedUpdates[0]) == null ? void 0 : _a.epoch) === epoch) {
848
- setIntoStore(
849
- userUnacknowledgedQueue,
850
- (updates) => {
851
- updates.shift();
852
- return updates;
853
- },
854
- store
855
- );
856
- }
857
- };
858
- socket.on(`tx-ack:${tx.key}`, trackClientAcknowledgement);
859
- return () => {
860
- if (unsubscribeFromTransaction) {
861
- unsubscribeFromTransaction();
862
- unsubscribeFromTransaction = void 0;
863
- }
864
- clearInterval(retry);
865
- socket.off(`tx-run:${tx.key}`, fillTransactionRequest);
866
- socket.off(`tx-sub:${tx.key}`, fillTransactionSubscriptionRequest);
867
- };
868
- };
869
- }
870
945
 
871
- export { ChildSocket, CustomSocket, DEFAULT_USER_IN_ROOM_META, ParentSocket, SubjectSocket, completeUpdateAtoms2 as completeUpdateAtoms, createRoomTX, joinRoomTX, realtimeActionReceiver, realtimeActionSynchronizer, realtimeAtomFamilyProvider, realtimeContinuitySynchronizer, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, realtimeStateSynchronizer, redactedPerspectiveUpdateSelectors, redactedUpdateSelectors, roomArgumentsAtoms, roomIndex, roomSelectors, socketAtoms, socketIndex, transactionRedactorAtoms, userIndex, userUnacknowledgedQueues, usersInRooms, usersOfSockets };
946
+ export { ChildSocket, CustomSocket, ParentSocket, SubjectSocket, actionOcclusionAtoms, createRoomTX, destroyRoomTX, joinRoomTX, leaveRoomTX, realtimeActionReceiver, realtimeAtomFamilyProvider, realtimeContinuitySynchronizer, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, realtimeStateSynchronizer, redactTransactionUpdateContent, roomArgumentsAtoms, roomSelectors, socketAtoms, socketIndex, userIndex, userUnacknowledgedQueues, usersOfSockets };
872
947
  //# sourceMappingURL=out.js.map
873
948
  //# sourceMappingURL=index.js.map