atom.io 0.16.3 → 0.18.0

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 (162) hide show
  1. package/data/dist/index.cjs +62 -40
  2. package/data/dist/index.cjs.map +1 -1
  3. package/data/dist/index.d.ts +8 -2
  4. package/data/dist/index.js +64 -42
  5. package/data/dist/index.js.map +1 -1
  6. package/data/src/dict.ts +8 -4
  7. package/data/src/join.ts +74 -33
  8. package/data/src/struct-family.ts +18 -17
  9. package/dist/chunk-OEVFAUPE.js +289 -0
  10. package/dist/chunk-OEVFAUPE.js.map +1 -0
  11. package/dist/index.cjs +36 -57
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +64 -53
  14. package/dist/index.js +15 -36
  15. package/dist/index.js.map +1 -1
  16. package/internal/dist/index.cjs +211 -81
  17. package/internal/dist/index.cjs.map +1 -1
  18. package/internal/dist/index.d.ts +100 -72
  19. package/internal/dist/index.js +200 -75
  20. package/internal/dist/index.js.map +1 -1
  21. package/internal/src/arbitrary.ts +3 -0
  22. package/internal/src/atom/create-regular-atom.ts +2 -3
  23. package/internal/src/caching.ts +8 -6
  24. package/internal/src/families/find-in-store.ts +16 -0
  25. package/internal/src/get-environment-data.ts +4 -7
  26. package/internal/src/get-state/get-from-store.ts +14 -0
  27. package/internal/src/get-state/index.ts +2 -0
  28. package/internal/src/{read-or-compute-value.ts → get-state/read-or-compute-value.ts} +3 -3
  29. package/internal/src/index.ts +7 -6
  30. package/internal/src/ingest-updates/ingest-atom-update.ts +2 -2
  31. package/internal/src/ingest-updates/ingest-transaction-update.ts +0 -1
  32. package/internal/src/mutable/create-mutable-atom.ts +3 -4
  33. package/internal/src/mutable/tracker.ts +18 -13
  34. package/internal/src/selector/create-standalone-selector.ts +0 -2
  35. package/internal/src/selector/register-selector.ts +1 -1
  36. package/internal/src/set-state/index.ts +1 -0
  37. package/internal/src/set-state/set-atom.ts +15 -19
  38. package/internal/src/set-state/set-into-store.ts +24 -0
  39. package/internal/src/store/store.ts +14 -2
  40. package/internal/src/store/withdraw.ts +72 -2
  41. package/internal/src/subscribe/subscribe-to-root-atoms.ts +1 -1
  42. package/internal/src/subscribe/subscribe-to-timeline.ts +2 -2
  43. package/internal/src/subscribe/subscribe-to-transaction.ts +2 -2
  44. package/internal/src/timeline/create-timeline.ts +12 -1
  45. package/internal/src/transaction/act-upon-store.ts +19 -0
  46. package/internal/src/transaction/apply-transaction.ts +7 -1
  47. package/internal/src/transaction/assign-transaction-to-continuity.ts +18 -0
  48. package/internal/src/transaction/build-transaction.ts +11 -8
  49. package/internal/src/transaction/create-transaction.ts +1 -1
  50. package/internal/src/transaction/get-epoch-number.ts +40 -0
  51. package/internal/src/transaction/index.ts +10 -1
  52. package/internal/src/transaction/set-epoch-number.ts +31 -0
  53. package/introspection/dist/index.cjs.map +1 -1
  54. package/introspection/dist/index.d.ts +3 -3
  55. package/introspection/dist/index.js.map +1 -1
  56. package/introspection/src/attach-introspection-states.ts +6 -2
  57. package/introspection/src/attach-timeline-family.ts +5 -2
  58. package/introspection/src/attach-transaction-logs.ts +2 -2
  59. package/json/dist/index.d.ts +3 -1
  60. package/json/src/index.ts +6 -2
  61. package/package.json +24 -13
  62. package/react/dist/index.cjs +3 -3
  63. package/react/dist/index.cjs.map +1 -1
  64. package/react/dist/index.d.ts +1 -1
  65. package/react/dist/index.js +5 -5
  66. package/react/dist/index.js.map +1 -1
  67. package/react/src/use-i.ts +2 -3
  68. package/react/src/use-json.ts +1 -1
  69. package/react/src/use-o.ts +3 -4
  70. package/react-devtools/dist/index.cjs +131 -134
  71. package/react-devtools/dist/index.cjs.map +1 -1
  72. package/react-devtools/dist/index.css +2 -2
  73. package/react-devtools/dist/index.css.map +1 -1
  74. package/react-devtools/dist/index.d.ts +3 -3
  75. package/react-devtools/dist/index.js +103 -106
  76. package/react-devtools/dist/index.js.map +1 -1
  77. package/react-devtools/src/StateEditor.tsx +6 -6
  78. package/react-devtools/src/StateIndex.tsx +2 -5
  79. package/react-devtools/src/TimelineIndex.tsx +3 -3
  80. package/react-devtools/src/TransactionIndex.tsx +9 -8
  81. package/react-devtools/src/Updates.tsx +1 -1
  82. package/react-devtools/src/index.ts +4 -4
  83. package/realtime/dist/index.cjs +72 -0
  84. package/realtime/dist/index.cjs.map +1 -0
  85. package/realtime/dist/index.d.ts +39 -0
  86. package/realtime/dist/index.js +68 -0
  87. package/realtime/dist/index.js.map +1 -0
  88. package/realtime/package.json +16 -0
  89. package/realtime/src/index.ts +1 -0
  90. package/realtime/src/realtime-continuity.ts +152 -0
  91. package/realtime-client/dist/index.cjs +403 -59
  92. package/realtime-client/dist/index.cjs.map +1 -1
  93. package/realtime-client/dist/index.d.ts +16 -9
  94. package/realtime-client/dist/index.js +114 -48
  95. package/realtime-client/dist/index.js.map +1 -1
  96. package/realtime-client/src/index.ts +8 -5
  97. package/realtime-client/src/{pull-family-member.ts → pull-atom-family-member.ts} +5 -5
  98. package/realtime-client/src/{pull-state.ts → pull-atom.ts} +5 -5
  99. package/realtime-client/src/{pull-mutable-family-member.ts → pull-mutable-atom-family-member.ts} +5 -5
  100. package/realtime-client/src/{pull-mutable.ts → pull-mutable-atom.ts} +5 -5
  101. package/realtime-client/src/pull-selector-family-member.ts +42 -0
  102. package/realtime-client/src/pull-selector.ts +38 -0
  103. package/realtime-client/src/realtime-client-stores/client-main-store.ts +2 -2
  104. package/realtime-client/src/realtime-client-stores/client-sync-store.ts +7 -7
  105. package/realtime-client/src/sync-continuity.ts +321 -0
  106. package/realtime-client/src/sync-server-action.ts +22 -21
  107. package/realtime-client/src/sync-state.ts +3 -3
  108. package/realtime-react/dist/index.cjs +330 -15
  109. package/realtime-react/dist/index.cjs.map +1 -1
  110. package/realtime-react/dist/index.d.ts +26 -6
  111. package/realtime-react/dist/index.js +43 -12
  112. package/realtime-react/dist/index.js.map +1 -1
  113. package/realtime-react/src/index.ts +6 -3
  114. package/realtime-react/src/use-pull-atom-family-member.ts +21 -0
  115. package/realtime-react/src/{use-pull.ts → use-pull-atom.ts} +6 -5
  116. package/realtime-react/src/{use-pull-mutable.ts → use-pull-mutable-atom.ts} +4 -3
  117. package/realtime-react/src/use-pull-mutable-family-member.ts +9 -4
  118. package/realtime-react/src/use-pull-selector-family-member.ts +21 -0
  119. package/realtime-react/src/{use-pull-family-member.ts → use-pull-selector.ts} +7 -5
  120. package/realtime-react/src/use-push.ts +3 -2
  121. package/realtime-react/src/use-server-action.ts +3 -2
  122. package/realtime-react/src/use-sync-continuity.ts +12 -0
  123. package/realtime-react/src/use-sync-server-action.ts +3 -2
  124. package/realtime-server/dist/index.cjs +582 -256
  125. package/realtime-server/dist/index.cjs.map +1 -1
  126. package/realtime-server/dist/index.d.ts +124 -49
  127. package/realtime-server/dist/index.js +566 -249
  128. package/realtime-server/dist/index.js.map +1 -1
  129. package/realtime-server/src/index.ts +18 -2
  130. package/realtime-server/src/ipc-socket.ts +230 -0
  131. package/realtime-server/src/realtime-action-receiver.ts +8 -5
  132. package/realtime-server/src/realtime-action-synchronizer.ts +53 -35
  133. package/realtime-server/src/realtime-continuity-synchronizer.ts +247 -0
  134. package/realtime-server/src/realtime-family-provider.ts +37 -73
  135. package/realtime-server/src/realtime-mutable-family-provider.ts +26 -87
  136. package/realtime-server/src/realtime-mutable-provider.ts +3 -2
  137. package/realtime-server/src/realtime-server-stores/index.ts +3 -1
  138. package/realtime-server/src/realtime-server-stores/realtime-continuity-store.ts +90 -0
  139. package/realtime-server/src/realtime-server-stores/server-room-store.ts +97 -0
  140. package/realtime-server/src/realtime-server-stores/server-sync-store.ts +2 -72
  141. package/realtime-server/src/realtime-server-stores/server-user-store.ts +14 -29
  142. package/realtime-server/src/realtime-state-provider.ts +3 -3
  143. package/realtime-server/src/realtime-state-receiver.ts +2 -3
  144. package/realtime-server/src/realtime-state-synchronizer.ts +3 -3
  145. package/realtime-testing/dist/index.cjs +28 -28
  146. package/realtime-testing/dist/index.cjs.map +1 -1
  147. package/realtime-testing/dist/index.js +28 -27
  148. package/realtime-testing/dist/index.js.map +1 -1
  149. package/realtime-testing/src/setup-realtime-test.tsx +38 -28
  150. package/src/atom.ts +49 -31
  151. package/src/get-state.ts +2 -11
  152. package/src/logger.ts +10 -5
  153. package/src/selector.ts +44 -25
  154. package/src/set-state.ts +1 -13
  155. package/src/silo.ts +7 -3
  156. package/src/subscribe.ts +2 -1
  157. package/src/timeline.ts +4 -4
  158. package/src/transaction.ts +13 -17
  159. package/src/validators.ts +15 -9
  160. package/dist/chunk-H4Q5FTPZ.js +0 -11
  161. package/dist/chunk-H4Q5FTPZ.js.map +0 -1
  162. package/internal/src/set-state/copy-mutable-in-transaction.ts +0 -19
@@ -3,7 +3,7 @@ import { __spreadProps, __spreadValues } from '../../dist/chunk-PZLG2HP3.js';
3
3
  import * as http from 'http';
4
4
  import { render, prettyDOM } from '@testing-library/react';
5
5
  import * as AtomIO from 'atom.io';
6
- import * as Internal from 'atom.io/internal';
6
+ import { IMPLICIT, findInStore, setIntoStore, getFromStore, clearStore } from 'atom.io/internal';
7
7
  import * as AR from 'atom.io/react';
8
8
  import * as RTR from 'atom.io/realtime-react';
9
9
  import * as RTS from 'atom.io/realtime-server';
@@ -12,8 +12,10 @@ import * as SocketIO from 'socket.io';
12
12
  import { io } from 'socket.io-client';
13
13
  import { jsx } from 'react/jsx-runtime';
14
14
 
15
+ var testNumber = 0;
15
16
  var setupRealtimeTestServer = (options) => {
16
- const silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE);
17
+ ++testNumber;
18
+ const silo = new AtomIO.Silo(`SERVER-${testNumber}`, IMPLICIT.STORE);
17
19
  const httpServer = http.createServer((_, res) => res.end(`Hello World!`));
18
20
  const address = httpServer.listen().address();
19
21
  const port = typeof address === `string` ? 80 : address === null ? null : address.port;
@@ -22,26 +24,12 @@ var setupRealtimeTestServer = (options) => {
22
24
  const server = new SocketIO.Server(httpServer).use((socket, next) => {
23
25
  const { token, username } = socket.handshake.auth;
24
26
  if (token === `test` && socket.id) {
25
- const socketRelatedKeysState = Internal.findInStore(
26
- RTS.usersOfSockets.core.findRelatedKeysState,
27
- socket.id,
28
- silo.store
29
- );
30
- const clientRelatedKeysState = Internal.findInStore(
31
- RTS.usersOfSockets.core.findRelatedKeysState,
32
- username,
33
- silo.store
34
- );
35
- AtomIO.setState(
36
- socketRelatedKeysState,
37
- (keys) => (keys.clear(), keys.add(username)),
38
- silo.store
39
- );
40
- AtomIO.setState(
41
- clientRelatedKeysState,
42
- (keys) => (keys.clear(), keys.add(socket.id)),
43
- silo.store
44
- );
27
+ const socketState = findInStore(RTS.socketAtoms, socket.id, silo.store);
28
+ setIntoStore(socketState, socket, silo.store);
29
+ const usersOfSockets2 = RTS.usersOfSockets.in(silo.store);
30
+ usersOfSockets2.relations.set(socket.id, username);
31
+ setIntoStore(RTS.userIndex, (index) => index.add(username), silo.store);
32
+ setIntoStore(RTS.socketIndex, (index) => index.add(socket.id), silo.store);
45
33
  console.log(`${username} connected on ${socket.id}`);
46
34
  next();
47
35
  } else {
@@ -53,7 +41,15 @@ var setupRealtimeTestServer = (options) => {
53
41
  });
54
42
  const dispose = () => {
55
43
  server.close();
56
- Internal.clearStore(silo.store);
44
+ const roomKeys = getFromStore(RTS.roomIndex, silo.store);
45
+ for (const roomKey of roomKeys) {
46
+ const roomState = findInStore(RTS.roomSelectors, roomKey, silo.store);
47
+ const room = getFromStore(roomState, silo.store);
48
+ if (room && !(room instanceof Promise)) {
49
+ room.kill();
50
+ }
51
+ }
52
+ silo.store.valueMap.clear();
57
53
  };
58
54
  return {
59
55
  name: `SERVER`,
@@ -67,9 +63,9 @@ var setupRealtimeTestClient = (options, name, port) => {
67
63
  } };
68
64
  const init = () => {
69
65
  const socket = io(`http://localhost:${port}/`, {
70
- auth: { token: `test`, username: name }
66
+ auth: { token: `test`, username: `${name}-${testNumber}` }
71
67
  });
72
- const silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE);
68
+ const silo = new AtomIO.Silo(name, IMPLICIT.STORE);
73
69
  for (const [key, value] of silo.store.valueMap.entries()) {
74
70
  if (Array.isArray(value)) {
75
71
  silo.store.valueMap.set(key, [...value]);
@@ -85,8 +81,9 @@ var setupRealtimeTestClient = (options, name, port) => {
85
81
  );
86
82
  const prettyPrint = () => console.log(prettyDOM(renderResult.container));
87
83
  const dispose = () => {
84
+ renderResult.unmount();
88
85
  socket.disconnect();
89
- Internal.clearStore(silo.store);
86
+ clearStore(silo.store);
90
87
  };
91
88
  testClient.dispose = dispose;
92
89
  return {
@@ -101,7 +98,11 @@ var setupRealtimeTestClient = (options, name, port) => {
101
98
  };
102
99
  var singleClient = (options) => {
103
100
  const server = setupRealtimeTestServer(options);
104
- const client = setupRealtimeTestClient(options, `CLIENT`, server.port);
101
+ const client = setupRealtimeTestClient(
102
+ options,
103
+ `CLIENT-${testNumber}`,
104
+ server.port
105
+ );
105
106
  return {
106
107
  client,
107
108
  server,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/setup-realtime-test.tsx"],"names":["clients"],"mappings":"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","sourcesContent":["import * as http from \"http\"\n\nimport { type RenderResult, prettyDOM, render } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport * as AR from \"atom.io/react\"\nimport * as RTR from \"atom.io/realtime-react\"\nimport * as RTS from \"atom.io/realtime-server\"\nimport * as Happy from \"happy-dom\"\nimport * as React from \"react\"\nimport * as SocketIO from \"socket.io\"\nimport type { Socket as ClientSocket } from \"socket.io-client\"\nimport { io } from \"socket.io-client\"\n\nimport { recordToEntries } from \"~/packages/anvl/src/object\"\n\nexport type TestSetupOptions = {\n\tserver: (tools: { socket: SocketIO.Socket; silo: AtomIO.Silo }) => void\n}\nexport type TestSetupOptions__SingleClient = TestSetupOptions & {\n\tclient: React.FC\n}\nexport type TestSetupOptions__MultiClient<ClientNames extends string> =\n\tTestSetupOptions & {\n\t\tclients: {\n\t\t\t[K in ClientNames]: React.FC\n\t\t}\n\t}\n\nexport type RealtimeTestTools = {\n\tname: string\n\tsilo: AtomIO.Silo\n}\nexport type RealtimeTestClient = RealtimeTestTools & {\n\trenderResult: RenderResult\n\tprettyPrint: () => void\n\tsocket: ClientSocket\n}\nexport type RealtimeTestClientBuilder = {\n\tdispose: () => void\n\tinit: () => RealtimeTestClient\n}\n\nexport type RealtimeTestServer = RealtimeTestTools & {\n\tdispose: () => void\n\tport: number\n}\n\nexport type RealtimeTestAPI = {\n\tserver: RealtimeTestServer\n\tteardown: () => void\n}\nexport type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {\n\tclient: RealtimeTestClientBuilder\n}\nexport type RealtimeTestAPI__MultiClient<ClientNames extends string> =\n\tRealtimeTestAPI & {\n\t\tclients: Record<ClientNames, RealtimeTestClientBuilder>\n\t}\n\nexport const setupRealtimeTestServer = (\n\toptions: TestSetupOptions,\n): RealtimeTestServer => {\n\tconst silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE)\n\n\tconst httpServer = http.createServer((_, res) => res.end(`Hello World!`))\n\tconst address = httpServer.listen().address()\n\tconst port =\n\t\ttypeof address === `string` ? 80 : address === null ? null : address.port\n\tif (port === null) throw new Error(`Could not determine port for test server`)\n\tconst server = new SocketIO.Server(httpServer).use((socket, next) => {\n\t\tconst { token, username } = socket.handshake.auth\n\t\tif (token === `test` && socket.id) {\n\t\t\tconst socketRelatedKeysState = Internal.findInStore(\n\t\t\t\tRTS.usersOfSockets.core.findRelatedKeysState,\n\t\t\t\tsocket.id,\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tconst clientRelatedKeysState = Internal.findInStore(\n\t\t\t\tRTS.usersOfSockets.core.findRelatedKeysState,\n\t\t\t\tusername,\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tAtomIO.setState(\n\t\t\t\tsocketRelatedKeysState,\n\t\t\t\t(keys) => (keys.clear(), keys.add(username)),\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tAtomIO.setState(\n\t\t\t\tclientRelatedKeysState,\n\t\t\t\t(keys) => (keys.clear(), keys.add(socket.id)),\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tconsole.log(`${username} connected on ${socket.id}`)\n\t\t\tnext()\n\t\t} else {\n\t\t\tnext(new Error(`Authentication error`))\n\t\t}\n\t})\n\n\tserver.on(`connection`, (socket: SocketIO.Socket) => {\n\t\toptions.server({ socket, silo })\n\t})\n\n\tconst dispose = () => {\n\t\tserver.close()\n\t\tInternal.clearStore(silo.store)\n\t}\n\n\treturn {\n\t\tname: `SERVER`,\n\t\tsilo,\n\t\tdispose,\n\t\tport,\n\t}\n}\nexport const setupRealtimeTestClient = (\n\toptions: TestSetupOptions__SingleClient,\n\tname: string,\n\tport: number,\n): RealtimeTestClientBuilder => {\n\tconst testClient = { dispose: () => {} }\n\tconst init = () => {\n\t\tconst socket: ClientSocket = io(`http://localhost:${port}/`, {\n\t\t\tauth: { token: `test`, username: name },\n\t\t})\n\t\tconst silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE)\n\t\tfor (const [key, value] of silo.store.valueMap.entries()) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tsilo.store.valueMap.set(key, [...value])\n\t\t\t}\n\t\t}\n\n\t\tconst { document } = new Happy.Window()\n\t\tdocument.body.innerHTML = `<div id=\"app\"></div>`\n\t\tconst renderResult = render(\n\t\t\t<AR.StoreProvider store={silo.store}>\n\t\t\t\t<RTR.RealtimeProvider socket={socket}>\n\t\t\t\t\t<options.client />\n\t\t\t\t</RTR.RealtimeProvider>\n\t\t\t</AR.StoreProvider>,\n\t\t\t{\n\t\t\t\tcontainer: document.querySelector(`#app`) as unknown as HTMLElement,\n\t\t\t},\n\t\t)\n\n\t\tconst prettyPrint = () => console.log(prettyDOM(renderResult.container))\n\n\t\tconst dispose = () => {\n\t\t\tsocket.disconnect()\n\t\t\tInternal.clearStore(silo.store)\n\t\t}\n\t\ttestClient.dispose = dispose\n\n\t\treturn {\n\t\t\tname,\n\t\t\tsilo,\n\t\t\tsocket,\n\t\t\trenderResult,\n\t\t\tprettyPrint,\n\t\t}\n\t}\n\treturn Object.assign(testClient, { init })\n}\n\nexport const singleClient = (\n\toptions: TestSetupOptions__SingleClient,\n): RealtimeTestAPI__SingleClient => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst client = setupRealtimeTestClient(options, `CLIENT`, server.port)\n\n\treturn {\n\t\tclient,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tclient.dispose()\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n\nexport const multiClient = <ClientNames extends string>(\n\toptions: TestSetupOptions__MultiClient<ClientNames>,\n): RealtimeTestAPI__MultiClient<ClientNames> => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst clients = recordToEntries(options.clients).reduce(\n\t\t(clients, [name, client]) => {\n\t\t\tclients[name] = setupRealtimeTestClient(\n\t\t\t\t{ ...options, client },\n\t\t\t\tname,\n\t\t\t\tserver.port,\n\t\t\t)\n\t\t\treturn clients\n\t\t},\n\t\t{} as Record<ClientNames, RealtimeTestClientBuilder>,\n\t)\n\n\treturn {\n\t\tclients,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tfor (const [, client] of recordToEntries(clients)) {\n\t\t\t\tclient.dispose()\n\t\t\t}\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n"]}
1
+ {"version":3,"sources":["../src/setup-realtime-test.tsx"],"names":["usersOfSockets","clients"],"mappings":";;;;;;;;;AAAA,YAAY,UAAU;AAEtB,SAAS,WAAW,cAAc;AAElC,YAAY,YAAY;AACxB;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,YAAY,QAAQ;AACpB,YAAY,SAAS;AACrB,YAAY,SAAS;AACrB,YAAY,WAAW;AAEvB,YAAY,cAAc;AAE1B,SAAS,UAAU;AA4Hd;AAxHL,IAAI,aAAa;AA8CV,IAAM,0BAA0B,CACtC,YACwB;AACxB,IAAE;AACF,QAAM,OAAO,IAAW,YAAK,UAAU,UAAU,IAAI,SAAS,KAAK;AAEnE,QAAM,aAAkB,kBAAa,CAAC,GAAG,QAAQ,IAAI,IAAI,cAAc,CAAC;AACxE,QAAM,UAAU,WAAW,OAAO,EAAE,QAAQ;AAC5C,QAAM,OACL,OAAO,YAAY,WAAW,KAAK,YAAY,OAAO,OAAO,QAAQ;AACtE,MAAI,SAAS;AAAM,UAAM,IAAI,MAAM,0CAA0C;AAE7E,QAAM,SAAS,IAAa,gBAAO,UAAU,EAAE,IAAI,CAAC,QAAQ,SAAS;AACpE,UAAM,EAAE,OAAO,SAAS,IAAI,OAAO,UAAU;AAC7C,QAAI,UAAU,UAAU,OAAO,IAAI;AAClC,YAAM,cAAc,YAAgB,iBAAa,OAAO,IAAI,KAAK,KAAK;AACtE,mBAAa,aAAa,QAAQ,KAAK,KAAK;AAC5C,YAAMA,kBAAqB,mBAAe,GAAG,KAAK,KAAK;AACvD,MAAAA,gBAAe,UAAU,IAAI,OAAO,IAAI,QAAQ;AAChD,mBAAiB,eAAW,CAAC,UAAU,MAAM,IAAI,QAAQ,GAAG,KAAK,KAAK;AACtE,mBAAiB,iBAAa,CAAC,UAAU,MAAM,IAAI,OAAO,EAAE,GAAG,KAAK,KAAK;AACzE,cAAQ,IAAI,GAAG,QAAQ,iBAAiB,OAAO,EAAE,EAAE;AACnD,WAAK;AAAA,IACN,OAAO;AACN,WAAK,IAAI,MAAM,sBAAsB,CAAC;AAAA,IACvC;AAAA,EACD,CAAC;AAED,SAAO,GAAG,cAAc,CAAC,WAA4B;AACpD,YAAQ,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,EAChC,CAAC;AAED,QAAM,UAAU,MAAM;AACrB,WAAO,MAAM;AACb,UAAM,WAAW,aAAiB,eAAW,KAAK,KAAK;AACvD,eAAW,WAAW,UAAU;AAC/B,YAAM,YAAY,YAAgB,mBAAe,SAAS,KAAK,KAAK;AACpE,YAAM,OAAO,aAAa,WAAW,KAAK,KAAK;AAC/C,UAAI,QAAQ,EAAE,gBAAgB,UAAU;AACvC,aAAK,KAAK;AAAA,MACX;AAAA,IACD;AACA,SAAK,MAAM,SAAS,MAAM;AAAA,EAC3B;AAEA,SAAO;AAAA,IACN,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AACO,IAAM,0BAA0B,CACtC,SACA,MACA,SAC+B;AAC/B,QAAM,aAAa,EAAE,SAAS,MAAM;AAAA,EAAC,EAAE;AACvC,QAAM,OAAO,MAAM;AAClB,UAAM,SAAuB,GAAG,oBAAoB,IAAI,KAAK;AAAA,MAC5D,MAAM,EAAE,OAAO,QAAQ,UAAU,GAAG,IAAI,IAAI,UAAU,GAAG;AAAA,IAC1D,CAAC;AACD,UAAM,OAAO,IAAW,YAAK,MAAM,SAAS,KAAK;AACjD,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,SAAS,QAAQ,GAAG;AACzD,UAAI,MAAM,QAAQ,KAAK,GAAG;AACzB,aAAK,MAAM,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC;AAAA,MACxC;AAAA,IACD;AAEA,UAAM,EAAE,SAAS,IAAI,IAAU,aAAO;AACtC,aAAS,KAAK,YAAY;AAC1B,UAAM,eAAe;AAAA,MACpB,oBAAI,kBAAH,EAAiB,OAAO,KAAK,OAC7B,8BAAK,sBAAJ,EAAqB,QACrB,8BAAC,QAAQ,QAAR,EAAe,GACjB,GACD;AAAA,MACA;AAAA,QACC,WAAW,SAAS,cAAc,MAAM;AAAA,MACzC;AAAA,IACD;AAEA,UAAM,cAAc,MAAM,QAAQ,IAAI,UAAU,aAAa,SAAS,CAAC;AAEvE,UAAM,UAAU,MAAM;AACrB,mBAAa,QAAQ;AACrB,aAAO,WAAW;AAClB,iBAAW,KAAK,KAAK;AAAA,IACtB;AACA,eAAW,UAAU;AAErB,WAAO;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD;AAAA,EACD;AACA,SAAO,OAAO,OAAO,YAAY,EAAE,KAAK,CAAC;AAC1C;AAEO,IAAM,eAAe,CAC3B,YACmC;AACnC,QAAM,SAAS,wBAAwB,OAAO;AAC9C,QAAM,SAAS;AAAA,IACd;AAAA,IACA,UAAU,UAAU;AAAA,IACpB,OAAO;AAAA,EACR;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,UAAU,MAAM;AACf,aAAO,QAAQ;AACf,aAAO,QAAQ;AAAA,IAChB;AAAA,EACD;AACD;AAEO,IAAM,cAAc,CAC1B,YAC+C;AAC/C,QAAM,SAAS,wBAAwB,OAAO;AAC9C,QAAM,UAAU,gBAAgB,QAAQ,OAAO,EAAE;AAAA,IAChD,CAACC,UAAS,CAAC,MAAM,MAAM,MAAM;AAC5B,MAAAA,SAAQ,IAAI,IAAI;AAAA,QACf,iCAAK,UAAL,EAAc,OAAO;AAAA,QACrB;AAAA,QACA,OAAO;AAAA,MACR;AACA,aAAOA;AAAA,IACR;AAAA,IACA,CAAC;AAAA,EACF;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,UAAU,MAAM;AACf,iBAAW,CAAC,EAAE,MAAM,KAAK,gBAAgB,OAAO,GAAG;AAClD,eAAO,QAAQ;AAAA,MAChB;AACA,aAAO,QAAQ;AAAA,IAChB;AAAA,EACD;AACD","sourcesContent":["import * as http from \"http\"\n\nimport { prettyDOM, render } from \"@testing-library/react\"\nimport type { RenderResult } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport {\n\tIMPLICIT,\n\tclearStore,\n\tfindInStore,\n\tgetFromStore,\n\tsetIntoStore,\n} from \"atom.io/internal\"\nimport * as AR from \"atom.io/react\"\nimport * as RTR from \"atom.io/realtime-react\"\nimport * as RTS from \"atom.io/realtime-server\"\nimport * as Happy from \"happy-dom\"\nimport * as React from \"react\"\nimport * as SocketIO from \"socket.io\"\nimport type { Socket as ClientSocket } from \"socket.io-client\"\nimport { io } from \"socket.io-client\"\n\nimport { recordToEntries } from \"~/packages/anvl/src/object\"\n\nlet testNumber = 0\n\nexport type TestSetupOptions = {\n\tserver: (tools: { socket: SocketIO.Socket; silo: AtomIO.Silo }) => void\n}\nexport type TestSetupOptions__SingleClient = TestSetupOptions & {\n\tclient: React.FC\n}\nexport type TestSetupOptions__MultiClient<ClientNames extends string> =\n\tTestSetupOptions & {\n\t\tclients: {\n\t\t\t[K in ClientNames]: React.FC\n\t\t}\n\t}\n\nexport type RealtimeTestTools = {\n\tname: string\n\tsilo: AtomIO.Silo\n}\nexport type RealtimeTestClient = RealtimeTestTools & {\n\trenderResult: RenderResult\n\tprettyPrint: () => void\n\tsocket: ClientSocket\n}\nexport type RealtimeTestClientBuilder = {\n\tdispose: () => void\n\tinit: () => RealtimeTestClient\n}\n\nexport type RealtimeTestServer = RealtimeTestTools & {\n\tdispose: () => void\n\tport: number\n}\n\nexport type RealtimeTestAPI = {\n\tserver: RealtimeTestServer\n\tteardown: () => void\n}\nexport type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {\n\tclient: RealtimeTestClientBuilder\n}\nexport type RealtimeTestAPI__MultiClient<ClientNames extends string> =\n\tRealtimeTestAPI & {\n\t\tclients: Record<ClientNames, RealtimeTestClientBuilder>\n\t}\n\nexport const setupRealtimeTestServer = (\n\toptions: TestSetupOptions,\n): RealtimeTestServer => {\n\t++testNumber\n\tconst silo = new AtomIO.Silo(`SERVER-${testNumber}`, IMPLICIT.STORE)\n\n\tconst httpServer = http.createServer((_, res) => res.end(`Hello World!`))\n\tconst address = httpServer.listen().address()\n\tconst port =\n\t\ttypeof address === `string` ? 80 : address === null ? null : address.port\n\tif (port === null) throw new Error(`Could not determine port for test server`)\n\n\tconst server = new SocketIO.Server(httpServer).use((socket, next) => {\n\t\tconst { token, username } = socket.handshake.auth\n\t\tif (token === `test` && socket.id) {\n\t\t\tconst socketState = findInStore(RTS.socketAtoms, socket.id, silo.store)\n\t\t\tsetIntoStore(socketState, socket, silo.store)\n\t\t\tconst usersOfSockets = RTS.usersOfSockets.in(silo.store)\n\t\t\tusersOfSockets.relations.set(socket.id, username)\n\t\t\tsetIntoStore(RTS.userIndex, (index) => index.add(username), silo.store)\n\t\t\tsetIntoStore(RTS.socketIndex, (index) => index.add(socket.id), silo.store)\n\t\t\tconsole.log(`${username} connected on ${socket.id}`)\n\t\t\tnext()\n\t\t} else {\n\t\t\tnext(new Error(`Authentication error`))\n\t\t}\n\t})\n\n\tserver.on(`connection`, (socket: SocketIO.Socket) => {\n\t\toptions.server({ socket, silo })\n\t})\n\n\tconst dispose = () => {\n\t\tserver.close()\n\t\tconst roomKeys = getFromStore(RTS.roomIndex, silo.store)\n\t\tfor (const roomKey of roomKeys) {\n\t\t\tconst roomState = findInStore(RTS.roomSelectors, roomKey, silo.store)\n\t\t\tconst room = getFromStore(roomState, silo.store)\n\t\t\tif (room && !(room instanceof Promise)) {\n\t\t\t\troom.kill()\n\t\t\t}\n\t\t}\n\t\tsilo.store.valueMap.clear()\n\t}\n\n\treturn {\n\t\tname: `SERVER`,\n\t\tsilo,\n\t\tdispose,\n\t\tport,\n\t}\n}\nexport const setupRealtimeTestClient = (\n\toptions: TestSetupOptions__SingleClient,\n\tname: string,\n\tport: number,\n): RealtimeTestClientBuilder => {\n\tconst testClient = { dispose: () => {} }\n\tconst init = () => {\n\t\tconst socket: ClientSocket = io(`http://localhost:${port}/`, {\n\t\t\tauth: { token: `test`, username: `${name}-${testNumber}` },\n\t\t})\n\t\tconst silo = new AtomIO.Silo(name, IMPLICIT.STORE)\n\t\tfor (const [key, value] of silo.store.valueMap.entries()) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tsilo.store.valueMap.set(key, [...value])\n\t\t\t}\n\t\t}\n\n\t\tconst { document } = new Happy.Window()\n\t\tdocument.body.innerHTML = `<div id=\"app\"></div>`\n\t\tconst renderResult = render(\n\t\t\t<AR.StoreProvider store={silo.store}>\n\t\t\t\t<RTR.RealtimeProvider socket={socket}>\n\t\t\t\t\t<options.client />\n\t\t\t\t</RTR.RealtimeProvider>\n\t\t\t</AR.StoreProvider>,\n\t\t\t{\n\t\t\t\tcontainer: document.querySelector(`#app`) as unknown as HTMLElement,\n\t\t\t},\n\t\t)\n\n\t\tconst prettyPrint = () => console.log(prettyDOM(renderResult.container))\n\n\t\tconst dispose = () => {\n\t\t\trenderResult.unmount()\n\t\t\tsocket.disconnect()\n\t\t\tclearStore(silo.store)\n\t\t}\n\t\ttestClient.dispose = dispose\n\n\t\treturn {\n\t\t\tname,\n\t\t\tsilo,\n\t\t\tsocket,\n\t\t\trenderResult,\n\t\t\tprettyPrint,\n\t\t}\n\t}\n\treturn Object.assign(testClient, { init })\n}\n\nexport const singleClient = (\n\toptions: TestSetupOptions__SingleClient,\n): RealtimeTestAPI__SingleClient => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst client = setupRealtimeTestClient(\n\t\toptions,\n\t\t`CLIENT-${testNumber}`,\n\t\tserver.port,\n\t)\n\n\treturn {\n\t\tclient,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tclient.dispose()\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n\nexport const multiClient = <ClientNames extends string>(\n\toptions: TestSetupOptions__MultiClient<ClientNames>,\n): RealtimeTestAPI__MultiClient<ClientNames> => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst clients = recordToEntries(options.clients).reduce(\n\t\t(clients, [name, client]) => {\n\t\t\tclients[name] = setupRealtimeTestClient(\n\t\t\t\t{ ...options, client },\n\t\t\t\tname,\n\t\t\t\tserver.port,\n\t\t\t)\n\t\t\treturn clients\n\t\t},\n\t\t{} as Record<ClientNames, RealtimeTestClientBuilder>,\n\t)\n\n\treturn {\n\t\tclients,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tfor (const [, client] of recordToEntries(clients)) {\n\t\t\t\tclient.dispose()\n\t\t\t}\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n"]}
@@ -1,8 +1,15 @@
1
1
  import * as http from "http"
2
2
 
3
- import { type RenderResult, prettyDOM, render } from "@testing-library/react"
3
+ import { prettyDOM, render } from "@testing-library/react"
4
+ import type { RenderResult } from "@testing-library/react"
4
5
  import * as AtomIO from "atom.io"
5
- import * as Internal from "atom.io/internal"
6
+ import {
7
+ IMPLICIT,
8
+ clearStore,
9
+ findInStore,
10
+ getFromStore,
11
+ setIntoStore,
12
+ } from "atom.io/internal"
6
13
  import * as AR from "atom.io/react"
7
14
  import * as RTR from "atom.io/realtime-react"
8
15
  import * as RTS from "atom.io/realtime-server"
@@ -14,6 +21,8 @@ import { io } from "socket.io-client"
14
21
 
15
22
  import { recordToEntries } from "~/packages/anvl/src/object"
16
23
 
24
+ let testNumber = 0
25
+
17
26
  export type TestSetupOptions = {
18
27
  server: (tools: { socket: SocketIO.Socket; silo: AtomIO.Silo }) => void
19
28
  }
@@ -61,36 +70,24 @@ export type RealtimeTestAPI__MultiClient<ClientNames extends string> =
61
70
  export const setupRealtimeTestServer = (
62
71
  options: TestSetupOptions,
63
72
  ): RealtimeTestServer => {
64
- const silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE)
73
+ ++testNumber
74
+ const silo = new AtomIO.Silo(`SERVER-${testNumber}`, IMPLICIT.STORE)
65
75
 
66
76
  const httpServer = http.createServer((_, res) => res.end(`Hello World!`))
67
77
  const address = httpServer.listen().address()
68
78
  const port =
69
79
  typeof address === `string` ? 80 : address === null ? null : address.port
70
80
  if (port === null) throw new Error(`Could not determine port for test server`)
81
+
71
82
  const server = new SocketIO.Server(httpServer).use((socket, next) => {
72
83
  const { token, username } = socket.handshake.auth
73
84
  if (token === `test` && socket.id) {
74
- const socketRelatedKeysState = Internal.findInStore(
75
- RTS.usersOfSockets.core.findRelatedKeysState,
76
- socket.id,
77
- silo.store,
78
- )
79
- const clientRelatedKeysState = Internal.findInStore(
80
- RTS.usersOfSockets.core.findRelatedKeysState,
81
- username,
82
- silo.store,
83
- )
84
- AtomIO.setState(
85
- socketRelatedKeysState,
86
- (keys) => (keys.clear(), keys.add(username)),
87
- silo.store,
88
- )
89
- AtomIO.setState(
90
- clientRelatedKeysState,
91
- (keys) => (keys.clear(), keys.add(socket.id)),
92
- silo.store,
93
- )
85
+ const socketState = findInStore(RTS.socketAtoms, socket.id, silo.store)
86
+ setIntoStore(socketState, socket, silo.store)
87
+ const usersOfSockets = RTS.usersOfSockets.in(silo.store)
88
+ usersOfSockets.relations.set(socket.id, username)
89
+ setIntoStore(RTS.userIndex, (index) => index.add(username), silo.store)
90
+ setIntoStore(RTS.socketIndex, (index) => index.add(socket.id), silo.store)
94
91
  console.log(`${username} connected on ${socket.id}`)
95
92
  next()
96
93
  } else {
@@ -104,7 +101,15 @@ export const setupRealtimeTestServer = (
104
101
 
105
102
  const dispose = () => {
106
103
  server.close()
107
- Internal.clearStore(silo.store)
104
+ const roomKeys = getFromStore(RTS.roomIndex, silo.store)
105
+ for (const roomKey of roomKeys) {
106
+ const roomState = findInStore(RTS.roomSelectors, roomKey, silo.store)
107
+ const room = getFromStore(roomState, silo.store)
108
+ if (room && !(room instanceof Promise)) {
109
+ room.kill()
110
+ }
111
+ }
112
+ silo.store.valueMap.clear()
108
113
  }
109
114
 
110
115
  return {
@@ -122,9 +127,9 @@ export const setupRealtimeTestClient = (
122
127
  const testClient = { dispose: () => {} }
123
128
  const init = () => {
124
129
  const socket: ClientSocket = io(`http://localhost:${port}/`, {
125
- auth: { token: `test`, username: name },
130
+ auth: { token: `test`, username: `${name}-${testNumber}` },
126
131
  })
127
- const silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE)
132
+ const silo = new AtomIO.Silo(name, IMPLICIT.STORE)
128
133
  for (const [key, value] of silo.store.valueMap.entries()) {
129
134
  if (Array.isArray(value)) {
130
135
  silo.store.valueMap.set(key, [...value])
@@ -147,8 +152,9 @@ export const setupRealtimeTestClient = (
147
152
  const prettyPrint = () => console.log(prettyDOM(renderResult.container))
148
153
 
149
154
  const dispose = () => {
155
+ renderResult.unmount()
150
156
  socket.disconnect()
151
- Internal.clearStore(silo.store)
157
+ clearStore(silo.store)
152
158
  }
153
159
  testClient.dispose = dispose
154
160
 
@@ -167,7 +173,11 @@ export const singleClient = (
167
173
  options: TestSetupOptions__SingleClient,
168
174
  ): RealtimeTestAPI__SingleClient => {
169
175
  const server = setupRealtimeTestServer(options)
170
- const client = setupRealtimeTestClient(options, `CLIENT`, server.port)
176
+ const client = setupRealtimeTestClient(
177
+ options,
178
+ `CLIENT-${testNumber}`,
179
+ server.port,
180
+ )
171
181
 
172
182
  return {
173
183
  client,
package/src/atom.ts CHANGED
@@ -45,23 +45,30 @@ export type RegularAtomFamilyOptions<T, K extends Json.Serializable> = {
45
45
  effects?: (key: K) => AtomEffect<T>[]
46
46
  }
47
47
 
48
- export type RegularAtomFamily<
49
- T,
50
- K extends Json.Serializable = Json.Serializable,
51
- > = ((key: K) => RegularAtomToken<T>) & {
52
- key: string
53
- type: `atom_family`
54
- subject: Subject<RegularAtomToken<T>>
55
- install: (store: Store) => void
56
- __T?: T
57
- __K?: K
58
- }
59
48
  export type RegularAtomFamilyToken<T, K extends Json.Serializable> = {
60
49
  key: string
61
50
  type: `atom_family`
62
51
  __T?: T
63
52
  __K?: K
64
53
  }
54
+ // biome-ignore format: intersection
55
+ export type RegularAtomFamilyTokenWithCall<
56
+ T,
57
+ K extends Json.Serializable,
58
+ > =
59
+ & RegularAtomFamilyToken<T, K>
60
+ & {
61
+ /** @deprecated Prefer the `findState`, `findInStore`, or `find` functions. */
62
+ (key: K): RegularAtomToken<T>
63
+ }
64
+ // biome-ignore format: intersection
65
+ export type RegularAtomFamily<T, K extends Json.Serializable> =
66
+ & RegularAtomFamilyToken<T, K>
67
+ & {
68
+ (key: K): RegularAtomToken<T>
69
+ subject: Subject<RegularAtomToken<T>>
70
+ install: (store: Store) => void
71
+ }
65
72
 
66
73
  // biome-ignore format: intersection
67
74
  export type MutableAtomFamilyOptions<
@@ -77,23 +84,6 @@ export type MutableAtomFamilyOptions<
77
84
  mutable: true,
78
85
  }
79
86
 
80
- // biome-ignore format: intersection
81
- export type MutableAtomFamily<
82
- T extends Transceiver<any>,
83
- J extends Json.Serializable,
84
- K extends Json.Serializable,
85
- > =
86
- & JsonInterface<T, J>
87
- & ((key: K) => MutableAtomToken<T, J>)
88
- & {
89
- key: string
90
- type: `mutable_atom_family`
91
- subject: Subject<MutableAtomToken<T, J>>
92
- install: (store: Store) => void
93
- __T?: T
94
- __J?: J
95
- __K?: K
96
- }
97
87
  export type MutableAtomFamilyToken<
98
88
  T extends Transceiver<any>,
99
89
  J extends Json.Serializable,
@@ -105,6 +95,30 @@ export type MutableAtomFamilyToken<
105
95
  __J?: J
106
96
  __K?: K
107
97
  }
98
+ // biome-ignore format: intersection
99
+ export type MutableAtomFamilyTokenWithCall<
100
+ T extends Transceiver<any>,
101
+ J extends Json.Serializable,
102
+ K extends Json.Serializable,
103
+ > =
104
+ & MutableAtomFamilyToken<T, J, K>
105
+ & {
106
+ /** @deprecated Prefer the `findState`, `findInStore`, or `find` functions. */
107
+ (key: K): MutableAtomToken<T, J>
108
+ }
109
+ // biome-ignore format: intersection
110
+ export type MutableAtomFamily<
111
+ T extends Transceiver<any>,
112
+ J extends Json.Serializable,
113
+ K extends Json.Serializable,
114
+ > =
115
+ & JsonInterface<T, J>
116
+ & MutableAtomFamilyToken<T, J, K>
117
+ & {
118
+ (key: K): MutableAtomToken<T, J>
119
+ subject: Subject<MutableAtomToken<T, J>>
120
+ install: (store: Store) => void
121
+ }
108
122
 
109
123
  export type AtomFamily<T, K extends Json.Serializable = Json.Serializable> =
110
124
  | MutableAtomFamily<T extends Transceiver<any> ? T : never, any, K>
@@ -117,14 +131,18 @@ export function atomFamily<
117
131
  T extends Transceiver<any>,
118
132
  J extends Json.Serializable,
119
133
  K extends Json.Serializable,
120
- >(options: MutableAtomFamilyOptions<T, J, K>): MutableAtomFamily<T, J, K>
134
+ >(
135
+ options: MutableAtomFamilyOptions<T, J, K>,
136
+ ): MutableAtomFamilyTokenWithCall<T, J, K>
121
137
  export function atomFamily<T, K extends Json.Serializable>(
122
138
  options: RegularAtomFamilyOptions<T, K>,
123
- ): RegularAtomFamily<T, K>
139
+ ): RegularAtomFamilyTokenWithCall<T, K>
124
140
  export function atomFamily<T, K extends Json.Serializable>(
125
141
  options:
126
142
  | MutableAtomFamilyOptions<any, any, any>
127
143
  | RegularAtomFamilyOptions<T, K>,
128
- ): MutableAtomFamily<any, any, any> | RegularAtomFamily<T, K> {
144
+ ):
145
+ | MutableAtomFamilyTokenWithCall<any, any, any>
146
+ | RegularAtomFamilyTokenWithCall<T, K> {
129
147
  return createAtomFamily(options, IMPLICIT.STORE)
130
148
  }
package/src/get-state.ts CHANGED
@@ -2,15 +2,6 @@ import * as Internal from "atom.io/internal"
2
2
 
3
3
  import type { ReadableToken } from "."
4
4
 
5
- export function getState<T>(
6
- token: ReadableToken<T>,
7
- store: Internal.Store = Internal.IMPLICIT.STORE,
8
- ): T {
9
- const state =
10
- Internal.withdraw(token, store) ??
11
- Internal.withdrawNewFamilyMember(token, store)
12
- if (state === undefined) {
13
- throw new Internal.NotFoundError(token, store)
14
- }
15
- return Internal.readOrComputeValue(state, store)
5
+ export function getState<T>(token: ReadableToken<T>): T {
6
+ return Internal.getFromStore(token, Internal.IMPLICIT.STORE)
16
7
  }
package/src/logger.ts CHANGED
@@ -6,20 +6,23 @@ const LoggerIconDictionary = {
6
6
  "⏮️": `Transaction undo`,
7
7
  "⏳": `Timeline event partially captured`,
8
8
  "⏹️": `Time-travel complete`,
9
- "💁": `Notice`,
10
- "🔄": `Realtime transaction synchronized`,
11
9
  "✅": `Realtime transaction success`,
12
10
  "✨": `Computation complete`,
13
11
  "❌": `Conflict prevents attempted action`,
14
12
  "⭕": `Operation start`,
15
13
  "🐞": `Possible bug in AtomIO`,
16
14
  "👀": `Subscription added`,
15
+ "👋": `Greeting`,
16
+ "👍": `Realtime acknowledgment`,
17
17
  "👪": `Family member added`,
18
+ "💁": `Notice`,
19
+ "💥": `Caught`,
18
20
  "📁": `Stow update`,
19
21
  "📃": `Copy mutable`,
20
22
  "📖": `Read state`,
21
23
  "📝": `Write state`,
22
24
  "📢": `Notify subscribers`,
25
+ "🔄": `Realtime transaction synchronized`,
23
26
  "🔌": `Register dependency`,
24
27
  "🔍": `Discover root`,
25
28
  "🔥": `Delete state`,
@@ -27,8 +30,8 @@ const LoggerIconDictionary = {
27
30
  "🔨": `Create immutable atom`,
28
31
  "🔴": `Operation complete`,
29
32
  "🗑": `Evict cached value`,
30
- "💥": `Caught`,
31
33
  "🙈": `Subscription canceled`,
34
+ "🚀": `Performance measure`,
32
35
  "🛄": `Apply transaction`,
33
36
  "🛠️": `Install atom into store`,
34
37
  "🛫": `Begin transaction`,
@@ -36,14 +39,16 @@ const LoggerIconDictionary = {
36
39
  "🧮": `Computing selector`,
37
40
  "🧹": `Prepare to evict`,
38
41
  "🪂": `Abort transaction`,
39
- "🚀": `Performance measure`,
42
+ "🤞": `Realtime optimistic update enqueued`,
43
+ "👈": `Realtime confirmed update enqueued`,
44
+ "⚖️": `Realtime update beginning reconciliation`,
40
45
  } as const
41
46
  export type LoggerIcon = keyof typeof LoggerIconDictionary
42
47
  export type TokenDenomination =
43
48
  | `atom`
49
+ | `continuity`
44
50
  | `mutable_atom`
45
51
  | `readonly_selector`
46
- | `realtime_sync_group`
47
52
  | `selector`
48
53
  | `state`
49
54
  | `timeline`
package/src/selector.ts CHANGED
@@ -41,41 +41,58 @@ export type ReadonlySelectorFamilyOptions<T, K extends Json.Serializable> = {
41
41
  get: (key: K) => Read<() => T>
42
42
  }
43
43
 
44
- export type WritableSelectorFamily<
45
- T,
46
- K extends Json.Serializable = Json.Serializable,
47
- > = ((key: K) => WritableSelectorToken<T>) & {
48
- key: string
49
- type: `selector_family`
50
- subject: Subject<WritableSelectorToken<T>>
51
- install: (store: Store) => void
52
- __T?: T
53
- __K?: K
54
- }
55
44
  export type WritableSelectorFamilyToken<T, K extends Json.Serializable> = {
56
45
  key: string
57
46
  type: `selector_family`
58
47
  __T?: T
59
48
  __K?: K
60
49
  }
61
-
62
- export type ReadonlySelectorFamily<
50
+ // biome-ignore format: intersection
51
+ export type WritableSelectorFamilyTokenWithCall<
63
52
  T,
64
- K extends Json.Serializable = Json.Serializable,
65
- > = ((key: K) => ReadonlySelectorToken<T>) & {
66
- key: string
67
- type: `readonly_selector_family`
68
- subject: Subject<ReadonlySelectorToken<T>>
69
- install: (store: Store) => void
70
- __T?: T
71
- __K?: K
72
- }
53
+ K extends Json.Serializable,
54
+ > =
55
+ & WritableSelectorFamilyToken<T, K>
56
+ & {
57
+ /** @deprecated Prefer the `findState`, `findInStore`, or `find` functions. */
58
+ (key: K): WritableSelectorToken<T>
59
+ }
60
+ // biome-ignore format: intersection
61
+ export type WritableSelectorFamily<T, K extends Json.Serializable> =
62
+ & WritableSelectorFamilyToken<T, K>
63
+ & {
64
+ (key: K): WritableSelectorToken<T>
65
+ subject: Subject<WritableSelectorToken<T>>
66
+ install: (store: Store) => void
67
+ }
68
+
73
69
  export type ReadonlySelectorFamilyToken<T, K extends Json.Serializable> = {
74
70
  key: string
75
71
  type: `readonly_selector_family`
76
72
  __T?: T
77
73
  __K?: K
78
74
  }
75
+ // biome-ignore format: intersection
76
+ export type ReadonlySelectorFamilyTokenWithCall<
77
+ T,
78
+ K extends Json.Serializable,
79
+ > =
80
+ & ReadonlySelectorFamilyToken<T, K>
81
+ & {
82
+ /** @deprecated Prefer the `findState`, `findInStore`, or `find` functions. */
83
+ (key: K): ReadonlySelectorToken<T>
84
+ }
85
+ // biome-ignore format: intersection
86
+ export type ReadonlySelectorFamily<T, K extends Json.Serializable> =
87
+ & ((key: K) => ReadonlySelectorToken<T>)
88
+ & {
89
+ key: string
90
+ type: `readonly_selector_family`
91
+ subject: Subject<ReadonlySelectorToken<T>>
92
+ install: (store: Store) => void
93
+ __T?: T
94
+ __K?: K
95
+ }
79
96
 
80
97
  export type SelectorFamily<T, K extends Json.Serializable> =
81
98
  | ReadonlySelectorFamily<T, K>
@@ -86,14 +103,16 @@ export type SelectorFamilyToken<T, K extends Json.Serializable> =
86
103
 
87
104
  export function selectorFamily<T, K extends Json.Serializable>(
88
105
  options: WritableSelectorFamilyOptions<T, K>,
89
- ): WritableSelectorFamily<T, K>
106
+ ): WritableSelectorFamilyTokenWithCall<T, K>
90
107
  export function selectorFamily<T, K extends Json.Serializable>(
91
108
  options: ReadonlySelectorFamilyOptions<T, K>,
92
- ): ReadonlySelectorFamily<T, K>
109
+ ): ReadonlySelectorFamilyTokenWithCall<T, K>
93
110
  export function selectorFamily<T, K extends Json.Serializable>(
94
111
  options:
95
112
  | ReadonlySelectorFamilyOptions<T, K>
96
113
  | WritableSelectorFamilyOptions<T, K>,
97
- ): ReadonlySelectorFamily<T, K> | WritableSelectorFamily<T, K> {
114
+ ):
115
+ | ReadonlySelectorFamilyTokenWithCall<T, K>
116
+ | WritableSelectorFamilyTokenWithCall<T, K> {
98
117
  return createSelectorFamily(options, IMPLICIT.STORE)
99
118
  }
package/src/set-state.ts CHANGED
@@ -5,18 +5,6 @@ import type { WritableToken } from "."
5
5
  export function setState<T, New extends T>(
6
6
  token: WritableToken<T>,
7
7
  value: New | ((oldValue: T) => New),
8
- store: Internal.Store = Internal.IMPLICIT.STORE,
9
8
  ): void {
10
- const rejection = Internal.openOperation(token, store)
11
- if (rejection) {
12
- return
13
- }
14
- const state =
15
- Internal.withdraw(token, store) ??
16
- Internal.withdrawNewFamilyMember(token, store)
17
- if (state === undefined) {
18
- throw new Internal.NotFoundError(token, store)
19
- }
20
- Internal.setAtomOrSelector(state, value, store)
21
- Internal.closeOperation(store)
9
+ Internal.setIntoStore(token, value, Internal.IMPLICIT.STORE)
22
10
  }
package/src/silo.ts CHANGED
@@ -7,6 +7,8 @@ import {
7
7
  createStandaloneSelector,
8
8
  createTimeline,
9
9
  createTransaction,
10
+ getFromStore,
11
+ setIntoStore,
10
12
  timeTravel,
11
13
  } from "atom.io/internal"
12
14
  import type { Json } from "atom.io/json"
@@ -21,11 +23,13 @@ import type {
21
23
  RegularAtomFamilyOptions,
22
24
  RegularAtomOptions,
23
25
  RegularAtomToken,
26
+ getState,
24
27
  redo,
28
+ setState,
25
29
  timeline,
26
30
  undo,
27
31
  } from "."
28
- import { getState, setState, subscribe } from "."
32
+ import { subscribe } from "."
29
33
  import type { atom, atomFamily } from "./atom"
30
34
  import type { selector, selectorFamily } from "./selector"
31
35
  import type { transaction } from "./transaction"
@@ -76,8 +80,8 @@ export class Silo {
76
80
  this.selectorFamily = (options) => createSelectorFamily(options, s) as any
77
81
  this.transaction = (options) => createTransaction(options, s)
78
82
  this.timeline = (options) => createTimeline(options, s)
79
- this.getState = (token) => getState(token, s)
80
- this.setState = (token, newValue) => setState(token, newValue, s)
83
+ this.getState = (token) => getFromStore(token, s)
84
+ this.setState = (token, newValue) => setIntoStore(token, newValue, s)
81
85
  this.subscribe = (token, handler, key) => subscribe(token, handler, key, s)
82
86
  this.undo = (token) => timeTravel(`undo`, token, s)
83
87
  this.redo = (token) => timeTravel(`redo`, token, s)
package/src/subscribe.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Store } from "atom.io/internal"
2
2
  import {
3
3
  IMPLICIT,
4
+ arbitrary,
4
5
  subscribeToState,
5
6
  subscribeToTimeline,
6
7
  subscribeToTransaction,
@@ -49,7 +50,7 @@ export function subscribe<M extends TimelineManageable>(
49
50
  export function subscribe(
50
51
  token: ReadableToken<any> | TimelineToken<any> | TransactionToken<any>,
51
52
  handleUpdate: (update: any) => void,
52
- key: string = Math.random().toString(36).slice(2),
53
+ key: string = arbitrary(),
53
54
  store = IMPLICIT.STORE,
54
55
  ): () => void {
55
56
  switch (token.type) {
package/src/timeline.ts CHANGED
@@ -6,14 +6,14 @@ import type {
6
6
  } from "atom.io/internal"
7
7
  import { IMPLICIT, createTimeline, timeTravel } from "atom.io/internal"
8
8
 
9
- import type { AtomFamily, AtomToken } from "."
9
+ import type { AtomFamilyToken, AtomToken } from "."
10
10
 
11
- export type TimelineManageable = AtomFamily<any, any> | AtomToken<any>
11
+ export type TimelineManageable = AtomFamilyToken<any, any> | AtomToken<any>
12
12
 
13
- export type TimelineToken<_> = {
13
+ export type TimelineToken<M> = {
14
14
  key: string
15
15
  type: `timeline`
16
- __brand?: _
16
+ __M?: M
17
17
  }
18
18
 
19
19
  export type TimelineOptions<ManagedAtom extends TimelineManageable> = {