atom.io 0.39.0 → 0.40.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 (59) hide show
  1. package/dist/data/index.js.map +1 -1
  2. package/dist/eslint-plugin/index.js +2 -1
  3. package/dist/eslint-plugin/index.js.map +1 -1
  4. package/dist/internal/index.d.ts +14 -8
  5. package/dist/internal/index.d.ts.map +1 -1
  6. package/dist/internal/index.js +129 -87
  7. package/dist/internal/index.js.map +1 -1
  8. package/dist/introspection/index.d.ts.map +1 -1
  9. package/dist/introspection/index.js.map +1 -1
  10. package/dist/json/index.d.ts.map +1 -1
  11. package/dist/json/index.js.map +1 -1
  12. package/dist/main/index.d.ts +8 -7
  13. package/dist/main/index.d.ts.map +1 -1
  14. package/dist/main/index.js.map +1 -1
  15. package/dist/react/index.d.ts.map +1 -1
  16. package/dist/react/index.js +43 -2
  17. package/dist/react/index.js.map +1 -1
  18. package/dist/react-devtools/index.d.ts.map +1 -1
  19. package/dist/react-devtools/index.js +12 -10
  20. package/dist/react-devtools/index.js.map +1 -1
  21. package/dist/realtime/index.d.ts.map +1 -1
  22. package/dist/realtime/index.js.map +1 -1
  23. package/dist/realtime-client/index.js.map +1 -1
  24. package/dist/realtime-react/index.js.map +1 -1
  25. package/dist/realtime-server/index.d.ts.map +1 -1
  26. package/dist/realtime-server/index.js.map +1 -1
  27. package/dist/realtime-testing/index.d.ts.map +1 -1
  28. package/dist/realtime-testing/index.js.map +1 -1
  29. package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
  30. package/dist/transceivers/set-rtx/index.js.map +1 -1
  31. package/dist/web/index.js.map +1 -1
  32. package/package.json +8 -8
  33. package/src/internal/events/ingest-selector-update.ts +13 -4
  34. package/src/internal/families/create-regular-atom-family.ts +3 -2
  35. package/src/internal/get-state/read-or-compute-value.ts +2 -1
  36. package/src/internal/get-state/reduce-reference.ts +15 -2
  37. package/src/internal/is-fn.ts +9 -0
  38. package/src/internal/operation.ts +3 -1
  39. package/src/internal/set-state/become.ts +11 -6
  40. package/src/internal/set-state/dispatch-state-update.ts +15 -12
  41. package/src/internal/set-state/operate-on-store.ts +3 -1
  42. package/src/internal/set-state/reset-atom-or-selector.ts +7 -7
  43. package/src/internal/set-state/set-atom-or-selector.ts +3 -2
  44. package/src/internal/set-state/set-atom.ts +4 -3
  45. package/src/internal/set-state/set-selector.ts +8 -7
  46. package/src/internal/timeline/create-timeline.ts +133 -98
  47. package/src/internal/timeline/time-travel.ts +42 -31
  48. package/src/internal/transaction/apply-transaction.ts +2 -2
  49. package/src/internal/transaction/build-transaction.ts +1 -1
  50. package/src/main/events.ts +8 -2
  51. package/src/main/logger.ts +1 -1
  52. package/src/main/timeline.ts +1 -7
  53. package/src/react-devtools/Updates.tsx +14 -9
  54. package/src/react-devtools/json-editor/editors-by-type/array-editor.tsx +1 -1
  55. package/src/react-devtools/json-editor/editors-by-type/object-editor.tsx +2 -3
  56. package/src/react-devtools/json-editor/editors-by-type/utilities/array-elements.ts +1 -1
  57. package/src/react-devtools/json-editor/editors-by-type/utilities/object-properties.ts +1 -1
  58. package/dist/use-o-DXPncKmZ.js +0 -47
  59. package/dist/use-o-DXPncKmZ.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["userKey: string | null","socket: ClientSocket"],"sources":["../../src/realtime-testing/setup-realtime-test.tsx"],"sourcesContent":["import * as http from \"node:http\"\n\nimport type { RenderResult } from \"@testing-library/react\"\nimport { prettyDOM, render } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport type { Store } from \"atom.io/internal\"\nimport {\n\tclearStore,\n\teditRelationsInStore,\n\tfindInStore,\n\tfindRelationsInStore,\n\tgetFromStore,\n\tIMPLICIT,\n\tsetIntoStore,\n} from \"atom.io/internal\"\nimport { toEntries } from \"atom.io/json\"\nimport * as AR from \"atom.io/react\"\nimport * as RT from \"atom.io/realtime\"\nimport * as RTC from \"atom.io/realtime-client\"\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\nlet testNumber = 0\n\n/* eslint-disable no-console */\n\nfunction prefixLogger(store: Store, prefix: string) {\n\tstore.loggers[0] = new AtomIO.AtomIOLogger(`info`, undefined, {\n\t\tinfo: (...args) => {\n\t\t\tconsole.info(prefix, ...args)\n\t\t},\n\t\twarn: (...args) => {\n\t\t\tconsole.warn(prefix, ...args)\n\t\t},\n\t\terror: (...args) => {\n\t\t\tconsole.error(prefix, ...args)\n\t\t},\n\t})\n}\n\nexport type TestSetupOptions = {\n\tport: number\n\timmortal?: { server?: boolean }\n\tserver: (tools: {\n\t\tsocket: SocketIO.Socket\n\t\tsilo: AtomIO.Silo\n\t\tenableLogging: () => void\n\t}) => 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\tenableLogging: () => void\n\tsocket: ClientSocket\n}\nexport type RealtimeTestClientBuilder = {\n\tdispose: () => void\n\tinit: () => RealtimeTestClient\n}\n\nexport type RealtimeTestServer = RealtimeTestTools & {\n\tdispose: () => Promise<void>\n\tport: number\n}\n\nexport type RealtimeTestAPI = {\n\tserver: RealtimeTestServer\n\tteardown: () => Promise<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(\n\t\t{\n\t\t\tname: `SERVER-${testNumber}`,\n\t\t\tlifespan: options.immortal?.server ? `immortal` : `ephemeral`,\n\t\t},\n\t\tIMPLICIT.STORE,\n\t)\n\tconst socketRealm = new AtomIO.Realm<RTS.SocketSystemHierarchy>(silo.store)\n\n\tconst httpServer = http.createServer((_, res) => res.end(`Hello World!`))\n\tconst address = httpServer.listen(options.port).address()\n\tconst port =\n\t\ttypeof address === `string` ? null : 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 userClaim = socketRealm.allocate(`root`, `user::${username}`)\n\t\t\tconst socketClaim = socketRealm.allocate(`root`, `socket::${socket.id}`)\n\t\t\tconst socketState = findInStore(silo.store, RTS.socketAtoms, socketClaim)\n\t\t\tsetIntoStore(silo.store, socketState, socket)\n\t\t\teditRelationsInStore(\n\t\t\t\tRTS.usersOfSockets,\n\t\t\t\t(relations) => {\n\t\t\t\t\trelations.set(userClaim, socketClaim)\n\t\t\t\t},\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tsetIntoStore(silo.store, RTS.userIndex, (index) => index.add(userClaim))\n\t\t\tsetIntoStore(silo.store, RTS.socketIndex, (index) =>\n\t\t\t\tindex.add(socketClaim),\n\t\t\t)\n\t\t\t// console.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\tlet userKey: string | null = null\n\t\tfunction enableLogging() {\n\t\t\tconst userKeyState = findRelationsInStore(\n\t\t\t\tRTS.usersOfSockets,\n\t\t\t\t`socket::${socket.id}`,\n\t\t\t\tsilo.store,\n\t\t\t).userKeyOfSocket\n\t\t\tuserKey = getFromStore(silo.store, userKeyState)\n\t\t\tprefixLogger(silo.store, `server`)\n\t\t\tsocket.onAny((event, ...args) => {\n\t\t\t\tconsole.log(`🛰 `, userKey, event, ...args)\n\t\t\t})\n\t\t\tsocket.onAnyOutgoing((event, ...args) => {\n\t\t\t\tconsole.log(`🛰 >>`, userKey, event, ...args)\n\t\t\t})\n\t\t\tsocket.on(`disconnect`, () => {\n\t\t\t\tconsole.log(`${userKey} disconnected`)\n\t\t\t})\n\t\t}\n\t\toptions.server({ socket, enableLogging, silo })\n\t})\n\n\tconst dispose = async () => {\n\t\tawait server.close()\n\t\tconst roomKeys = getFromStore(silo.store, RT.roomIndex)\n\t\tfor (const roomKey of roomKeys) {\n\t\t\tconst roomState = findInStore(silo.store, RTS.roomSelectors, roomKey)\n\t\t\tconst room = getFromStore(silo.store, roomState)\n\t\t\tif (room && !(room instanceof Promise)) {\n\t\t\t\troom.process.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, lifespan: `ephemeral` }, IMPLICIT.STORE)\n\t\tsilo.setState(RTC.myUsernameState, `${name}-${testNumber}`)\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 = () => {\n\t\t\tconsole.log(prettyDOM(renderResult.container))\n\t\t}\n\n\t\tconst enableLogging = () => {\n\t\t\tprefixLogger(silo.store, name)\n\t\t\tsocket.onAny((event, ...args) => {\n\t\t\t\tconsole.log(`📡 `, name, event, ...args)\n\t\t\t})\n\t\t\tsocket.onAnyOutgoing((event, ...args) => {\n\t\t\t\tconsole.log(`📡 >>`, name, event, ...args)\n\t\t\t})\n\t\t}\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\tenableLogging,\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: async () => {\n\t\t\tawait server.dispose()\n\t\t\tclient.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 = toEntries(options.clients).reduce(\n\t\t(clientRecord, [name, client]) => {\n\t\t\tclientRecord[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 clientRecord\n\t\t},\n\t\t{} as Record<ClientNames, RealtimeTestClientBuilder>,\n\t)\n\n\treturn {\n\t\tclients,\n\t\tserver,\n\t\tteardown: async () => {\n\t\t\tawait server.dispose()\n\t\t\tfor (const [, client] of toEntries(clients)) {\n\t\t\t\tclient.dispose()\n\t\t\t}\n\t\t},\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["userKey: string | null","socket: ClientSocket"],"sources":["../../src/realtime-testing/setup-realtime-test.tsx"],"sourcesContent":["import * as http from \"node:http\"\n\nimport type { RenderResult } from \"@testing-library/react\"\nimport { prettyDOM, render } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport type { Store } from \"atom.io/internal\"\nimport {\n\tclearStore,\n\teditRelationsInStore,\n\tfindInStore,\n\tfindRelationsInStore,\n\tgetFromStore,\n\tIMPLICIT,\n\tsetIntoStore,\n} from \"atom.io/internal\"\nimport { toEntries } from \"atom.io/json\"\nimport * as AR from \"atom.io/react\"\nimport * as RT from \"atom.io/realtime\"\nimport * as RTC from \"atom.io/realtime-client\"\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\nlet testNumber = 0\n\n/* eslint-disable no-console */\n\nfunction prefixLogger(store: Store, prefix: string) {\n\tstore.loggers[0] = new AtomIO.AtomIOLogger(`info`, undefined, {\n\t\tinfo: (...args) => {\n\t\t\tconsole.info(prefix, ...args)\n\t\t},\n\t\twarn: (...args) => {\n\t\t\tconsole.warn(prefix, ...args)\n\t\t},\n\t\terror: (...args) => {\n\t\t\tconsole.error(prefix, ...args)\n\t\t},\n\t})\n}\n\nexport type TestSetupOptions = {\n\tport: number\n\timmortal?: { server?: boolean }\n\tserver: (tools: {\n\t\tsocket: SocketIO.Socket\n\t\tsilo: AtomIO.Silo\n\t\tenableLogging: () => void\n\t}) => 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\tenableLogging: () => void\n\tsocket: ClientSocket\n}\nexport type RealtimeTestClientBuilder = {\n\tdispose: () => void\n\tinit: () => RealtimeTestClient\n}\n\nexport type RealtimeTestServer = RealtimeTestTools & {\n\tdispose: () => Promise<void>\n\tport: number\n}\n\nexport type RealtimeTestAPI = {\n\tserver: RealtimeTestServer\n\tteardown: () => Promise<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(\n\t\t{\n\t\t\tname: `SERVER-${testNumber}`,\n\t\t\tlifespan: options.immortal?.server ? `immortal` : `ephemeral`,\n\t\t},\n\t\tIMPLICIT.STORE,\n\t)\n\tconst socketRealm = new AtomIO.Realm<RTS.SocketSystemHierarchy>(silo.store)\n\n\tconst httpServer = http.createServer((_, res) => res.end(`Hello World!`))\n\tconst address = httpServer.listen(options.port).address()\n\tconst port =\n\t\ttypeof address === `string` ? null : 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 userClaim = socketRealm.allocate(`root`, `user::${username}`)\n\t\t\tconst socketClaim = socketRealm.allocate(`root`, `socket::${socket.id}`)\n\t\t\tconst socketState = findInStore(silo.store, RTS.socketAtoms, socketClaim)\n\t\t\tsetIntoStore(silo.store, socketState, socket)\n\t\t\teditRelationsInStore(\n\t\t\t\tRTS.usersOfSockets,\n\t\t\t\t(relations) => {\n\t\t\t\t\trelations.set(userClaim, socketClaim)\n\t\t\t\t},\n\t\t\t\tsilo.store,\n\t\t\t)\n\t\t\tsetIntoStore(silo.store, RTS.userIndex, (index) => index.add(userClaim))\n\t\t\tsetIntoStore(silo.store, RTS.socketIndex, (index) =>\n\t\t\t\tindex.add(socketClaim),\n\t\t\t)\n\t\t\t// console.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\tlet userKey: string | null = null\n\t\tfunction enableLogging() {\n\t\t\tconst userKeyState = findRelationsInStore(\n\t\t\t\tRTS.usersOfSockets,\n\t\t\t\t`socket::${socket.id}`,\n\t\t\t\tsilo.store,\n\t\t\t).userKeyOfSocket\n\t\t\tuserKey = getFromStore(silo.store, userKeyState)\n\t\t\tprefixLogger(silo.store, `server`)\n\t\t\tsocket.onAny((event, ...args) => {\n\t\t\t\tconsole.log(`🛰 `, userKey, event, ...args)\n\t\t\t})\n\t\t\tsocket.onAnyOutgoing((event, ...args) => {\n\t\t\t\tconsole.log(`🛰 >>`, userKey, event, ...args)\n\t\t\t})\n\t\t\tsocket.on(`disconnect`, () => {\n\t\t\t\tconsole.log(`${userKey} disconnected`)\n\t\t\t})\n\t\t}\n\t\toptions.server({ socket, enableLogging, silo })\n\t})\n\n\tconst dispose = async () => {\n\t\tawait server.close()\n\t\tconst roomKeys = getFromStore(silo.store, RT.roomIndex)\n\t\tfor (const roomKey of roomKeys) {\n\t\t\tconst roomState = findInStore(silo.store, RTS.roomSelectors, roomKey)\n\t\t\tconst room = getFromStore(silo.store, roomState)\n\t\t\tif (room && !(room instanceof Promise)) {\n\t\t\t\troom.process.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, lifespan: `ephemeral` }, IMPLICIT.STORE)\n\t\tsilo.setState(RTC.myUsernameState, `${name}-${testNumber}`)\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 = () => {\n\t\t\tconsole.log(prettyDOM(renderResult.container))\n\t\t}\n\n\t\tconst enableLogging = () => {\n\t\t\tprefixLogger(silo.store, name)\n\t\t\tsocket.onAny((event, ...args) => {\n\t\t\t\tconsole.log(`📡 `, name, event, ...args)\n\t\t\t})\n\t\t\tsocket.onAnyOutgoing((event, ...args) => {\n\t\t\t\tconsole.log(`📡 >>`, name, event, ...args)\n\t\t\t})\n\t\t}\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\tenableLogging,\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: async () => {\n\t\t\tawait server.dispose()\n\t\t\tclient.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 = toEntries(options.clients).reduce(\n\t\t(clientRecord, [name, client]) => {\n\t\t\tclientRecord[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 clientRecord\n\t\t},\n\t\t{} as Record<ClientNames, RealtimeTestClientBuilder>,\n\t)\n\n\treturn {\n\t\tclients,\n\t\tserver,\n\t\tteardown: async () => {\n\t\t\tawait server.dispose()\n\t\t\tfor (const [, client] of toEntries(clients)) {\n\t\t\t\tclient.dispose()\n\t\t\t}\n\t\t},\n\t}\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/transceivers/set-rtx/set-rtx.ts"],"sourcesContent":[],"mappings":";;;;KAKY,aAAA;AAAA,KACA,SAAA,GADA,GACe,aADf,IAAA,MAAA,EAAA;AACA,KACA,iBAAA,GADe,GAAA,MAAA,IACkB,SADlB,EAAA;AACf,UAEK,UAFL,CAAA,UAE0B,SAFO,CAAA,SAEY,WAFZ,CAEwB,CAFxB,CAAA,CAAA;EAE7C,SAAiB,KAAA,EACA,aADA,CACc,iBADd,GAAA,IAAA,CAAA;EAAA,SAAA,UAAA,EAAA,MAAA;WAAqB,QAAA,EAAA,MAAA;WAA+B,iBAAA,EAAA,MAAA;;AACpD,UAMA,UANA,CAAA,UAMqB,SANrB,CAAA,SAMwC,IAAA,CAAK,MAN7C,CAAA;SADwC,EAQ/C,CAR+C,EAAA;EAAA,KAAA,EAAA,CAShD,iBATgD,GAAA,IAAA,CAAA,EAAA;EAOzD,UAAiB,EAAA,MAAA;EAAA,QAAA,EAAA,MAAA;mBAAqB,EAAA,MAAA;;AAE7B,cAKI,MALJ,CAAA,UAKqB,SALrB,CAAA,SAMA,GANA,CAMI,CANJ,CAAA,YAQP,WARO,CAQK,UARL,CAQgB,CARhB,CAAA,EAQoB,iBARpB,EAQuC,UARvC,CAQkD,CARlD,CAAA,CAAA,EASP,OATO,CAAA;QAWK,eAbgD;EAAA,SAAA,OAAA,EAcpC,OAdoC,CAc5B,SAd4B,CAAA;EAO9D,UAAa,EAAA,MAAA;EAAA,KAAA,EAAA,CASG,iBATH,GAAA,IAAA,CAAA,EAAA;UAAiB,EAAA,MAAA;mBACjB,EAAA,MAAA;aAEY,CAAA,MAAA,CAAA,EAUI,QAVJ,CAUa,CAVb,CAAA,EAAA,UAAA,CAAA,EAAA,MAAA;WAAX,aAAA,EA2BkB,UA3BlB,CA2B6B,CA3B7B,CAAA;QAAe,CAAA,CAAA,EA6BX,UA7BW,CA6BA,CA7BA,CAAA;SAA8B,QAAA,CAAA,UAuCzB,SAvCyB,CAAA,CAAA,IAAA,EAuCR,UAvCQ,CAuCG,CAvCH,CAAA,CAAA,EAuCQ,MAvCR,CAuCe,CAvCf,CAAA;WAAX,EA+C7B,CA/C6B,CAAA,EAAA,IAAA;SAGlC,EAAA,IAAA;QACoB,CAAA,KAAA,EA6DZ,CA7DY,CAAA,EAAA,OAAA;WAAR,MAAA,EAsED,MAtEC,CAsEM,CAtEN,CAAA,GAAA,IAAA;SAuEX,MArEC,CAqEM,CArEN,CAAA,GAAA,IAAA;oBAIsB,EAkEV,SAlEU,EAAA,GAAA,IAAA;aAAT,CAAA,GAAA,EAAA,CAAA,KAAA,EAmEI,MAnEJ,CAmEW,CAnEX,CAAA,EAAA,GAAA,OAAA,CAAA,EAAA,IAAA;YAiBc,UAAA,CAAA,GAAA,EAAA,MAAA,EAAA,EAAA,EAAA,CAAA,MAAA,EAmF5B,SAnF4B,EAAA,GAAA,IAAA,CAAA,EAAA,GAAA,GAAA,IAAA;WAAX,CAAA,GAAA,EAAA,MAAA,EAAA,EAAA,EAAA,CAAA,MAAA,EAyFjB,iBAzFiB,EAAA,GAAA,IAAA,CAAA,EAAA,GAAA,GAAA,IAAA;aAEH,EA8FR,SA9FQ,CAAA,EAAA,IAAA;UAAX,MAAA;iBAUgB,CAAA,MAAA,EA6GF,iBA7GE,CAAA,EAAA,MAAA;WAA4B,EAkH3C,iBAlH2C,CAAA,EAAA,MAAA,GAAA,cAAA,GAAA,IAAA;UAAX,CAAA,MAAA,EA6J1B,SA7J0B,CAAA,EAAA,IAAA;aAAuB,EAsLrD,iBAtLqD,CAAA,EAAA,MAAA,GAAA,IAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/transceivers/set-rtx/set-rtx.ts"],"sourcesContent":[],"mappings":";;;;KAKY,aAAA;AAAA,KACA,SAAA,GADA,GACe,aADf,IAAA,MAAA,EAAA;AACA,KACA,iBAAA,GADe,GAAA,MAAA,IACkB,SADlB,EAAA;AACf,UAEK,UAFL,CAAA,UAE0B,SAFO,CAAA,SAEY,WAFZ,CAEwB,CAFxB,CAAA,CAAA;EAE7C,SAAiB,KAAA,EACA,aADA,CACc,iBADd,GAAA,IAAA,CAAA;EAAA,SAAA,UAAA,EAAA,MAAA;WAAqB,QAAA,EAAA,MAAA;WAA+B,iBAAA,EAAA,MAAA;;AACpD,UAMA,UANA,CAAA,UAMqB,SANrB,CAAA,SAMwC,IAAA,CAAK,MAN7C,CAAA;SADwC,EAQ/C,CAR+C,EAAA;UAShD;EAFT,UAAiB,EAAA,MAAA;EAAA,QAAA,EAAA,MAAA;mBAAqB,EAAA,MAAA;;AAE7B,cAKI,MALJ,CAAA,UAKqB,SALrB,CAAA,SAMA,GANA,CAMI,CANJ,CAAA,YAQP,WARO,CAQK,UARL,CAQgB,CARhB,CAAA,EAQoB,iBARpB,EAQuC,UARvC,CAQkD,CARlD,CAAA,CAAA,EASP,OATO,CAAA;QAWK,eAbgD;oBAcpC,QAAQ;EAPlC,UAAa,EAAA,MAAA;EAAA,KAAA,EAAA,CASG,iBATH,GAAA,IAAA,CAAA,EAAA;UAAiB,EAAA,MAAA;mBACjB,EAAA,MAAA;aAEY,CAAA,MAAA,CAAA,EAUI,QAVJ,CAUa,CAVb,CAAA,EAAA,UAAA,CAAA,EAAA,MAAA;WAAX,aAAA,EA2BkB,UA3BlB,CA2B6B,CA3B7B,CAAA;QAAe,CAAA,CAAA,EA6BX,UA7BW,CA6BA,CA7BA,CAAA;SAA8B,QAAA,CAAA,UAuCzB,SAvCyB,CAAA,CAAA,IAAA,EAuCR,UAvCQ,CAuCG,CAvCH,CAAA,CAAA,EAuCQ,MAvCR,CAuCe,CAvCf,CAAA;WAAX,EA+C7B,CA/C6B,CAAA,EAAA,IAAA;SAGlC,EAAA,IAAA;QACoB,CAAA,KAAA,EA6DZ,CA7DY,CAAA,EAAA,OAAA;WAAR,MAAA,EAsED,MAtEC,CAsEM,CAtEN,CAAA,GAAA,IAAA;SAuEX,MArEC,CAqEM,CArEN,CAAA,GAAA,IAAA;oBAIsB,EAkEV,SAlEU,EAAA,GAAA,IAAA;aAAT,CAAA,GAAA,EAAA,CAAA,KAAA,EAmEI,MAnEJ,CAmEW,CAnEX,CAAA,EAAA,GAAA,OAAA,CAAA,EAAA,IAAA;YAiBc,UAAA,CAAA,GAAA,EAAA,MAAA,EAAA,EAAA,EAAA,CAAA,MAAA,EAmF5B,SAnF4B,EAAA,GAAA,IAAA,CAAA,EAAA,GAAA,GAAA,IAAA;WAAX,CAAA,GAAA,EAAA,MAAA,EAAA,EAAA,EAAA,CAAA,MAAA,EAyFjB,iBAzFiB,EAAA,GAAA,IAAA,CAAA,EAAA,GAAA,GAAA,IAAA;aAEH,EA8FR,SA9FQ,CAAA,EAAA,IAAA;UAAX,MAAA;iBAUgB,CAAA,MAAA,EA6GF,iBA7GE,CAAA,EAAA,MAAA;WAA4B,EAkH3C,iBAlH2C,CAAA,EAAA,MAAA,GAAA,cAAA,GAAA,IAAA;UAAX,CAAA,MAAA,EA6J1B,SA7J0B,CAAA,EAAA,IAAA;aAAuB,EAsLrD,iBAtLqD,CAAA,EAAA,MAAA,GAAA,IAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/transceivers/set-rtx/set-rtx.ts"],"sourcesContent":["import type { Lineage, Transceiver, TransceiverMode } from \"atom.io/internal\"\nimport { Subject } from \"atom.io/internal\"\nimport type { Json, primitive } from \"atom.io/json\"\nimport { stringifyJson } from \"atom.io/json\"\n\nexport type SetUpdateType = `add` | `clear` | `del` | `tx`\nexport type SetUpdate = `${SetUpdateType}:${string}`\nexport type NumberedSetUpdate = `${number}=${SetUpdate}`\n\nexport interface SetRTXView<P extends primitive> extends ReadonlySet<P> {\n\treadonly cache: ReadonlyArray<NumberedSetUpdate | null>\n\treadonly cacheLimit: number\n\treadonly cacheIdx: number\n\treadonly cacheUpdateNumber: number\n}\n\nexport interface SetRTXJson<P extends primitive> extends Json.Object {\n\tmembers: P[]\n\tcache: (NumberedSetUpdate | null)[]\n\tcacheLimit: number\n\tcacheIdx: number\n\tcacheUpdateNumber: number\n}\nexport class SetRTX<P extends primitive>\n\textends Set<P>\n\timplements\n\t\tTransceiver<SetRTXView<P>, NumberedSetUpdate, SetRTXJson<P>>,\n\t\tLineage\n{\n\tpublic mode: TransceiverMode = `record`\n\tpublic readonly subject: Subject<SetUpdate> = new Subject<SetUpdate>()\n\tpublic cacheLimit = 0\n\tpublic cache: (NumberedSetUpdate | null)[] = []\n\tpublic cacheIdx = -1\n\tpublic cacheUpdateNumber = -1\n\n\tpublic constructor(values?: Iterable<P>, cacheLimit = 0) {\n\t\tsuper(values)\n\t\tif (values instanceof SetRTX) {\n\t\t\tthis.parent = values\n\t\t\tthis.cacheUpdateNumber = values.cacheUpdateNumber\n\t\t}\n\t\tif (cacheLimit) {\n\t\t\tthis.cacheLimit = cacheLimit\n\t\t\tthis.cache = new Array(cacheLimit)\n\t\t\tthis.subscribe(`auto cache`, (update) => {\n\t\t\t\tthis.cacheIdx++\n\t\t\t\tthis.cacheIdx %= this.cacheLimit\n\t\t\t\tthis.cache[this.cacheIdx] = update\n\t\t\t})\n\t\t}\n\t}\n\n\tpublic readonly READONLY_VIEW: SetRTXView<P> = this\n\n\tpublic toJSON(): SetRTXJson<P> {\n\t\treturn {\n\t\t\tmembers: [...this],\n\t\t\tcache: this.cache,\n\t\t\tcacheLimit: this.cacheLimit,\n\t\t\tcacheIdx: this.cacheIdx,\n\t\t\tcacheUpdateNumber: this.cacheUpdateNumber,\n\t\t}\n\t}\n\n\tpublic static fromJSON<P extends primitive>(json: SetRTXJson<P>): SetRTX<P> {\n\t\tconst set = new SetRTX<P>(json.members, json.cacheLimit)\n\t\tset.cache = json.cache\n\t\tset.cacheIdx = json.cacheIdx\n\t\tset.cacheUpdateNumber = json.cacheUpdateNumber\n\t\treturn set\n\t}\n\n\tpublic add(value: P): this {\n\t\tconst result = super.add(value)\n\t\tif (this.mode === `record`) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`add:${stringifyJson<P>(value)}`)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic clear(): void {\n\t\tconst capturedContents = this.mode === `record` ? [...this] : null\n\t\tsuper.clear()\n\t\tif (capturedContents) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`clear:${JSON.stringify(capturedContents)}`)\n\t\t}\n\t}\n\n\tpublic delete(value: P): boolean {\n\t\tconst result = super.delete(value)\n\t\tif (this.mode === `record`) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`del:${stringifyJson<P>(value)}`)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic readonly parent: SetRTX<P> | null = null\n\tpublic child: SetRTX<P> | null = null\n\tpublic transactionUpdates: SetUpdate[] | null = null\n\tpublic transaction(run: (child: SetRTX<P>) => boolean): void {\n\t\tthis.mode = `transaction`\n\t\tthis.transactionUpdates = []\n\t\tthis.child = new SetRTX(this)\n\t\tconst unsubscribe = this.child._subscribe(`transaction`, (update) => {\n\t\t\tthis.transactionUpdates?.push(update)\n\t\t})\n\t\ttry {\n\t\t\tconst shouldCommit = run(this.child)\n\t\t\tif (shouldCommit) {\n\t\t\t\tfor (const update of this.transactionUpdates) {\n\t\t\t\t\tthis.doStep(update)\n\t\t\t\t}\n\t\t\t\tthis.cacheUpdateNumber++\n\t\t\t\tthis.emit(`tx:${this.transactionUpdates.join(`;`)}`)\n\t\t\t}\n\t\t} catch (thrown) {\n\t\t\t/* eslint-disable-next-line no-console */\n\t\t\tconsole.warn(\n\t\t\t\t`Did not apply transaction to SetRTX; this error was thrown:`,\n\t\t\t\tthrown,\n\t\t\t)\n\t\t\tthrow thrown\n\t\t} finally {\n\t\t\tunsubscribe()\n\t\t\tthis.child = null\n\t\t\tthis.transactionUpdates = null\n\t\t\tthis.mode = `record`\n\t\t}\n\t}\n\n\tprotected _subscribe(\n\t\tkey: string,\n\t\tfn: (update: SetUpdate) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, fn)\n\t}\n\tpublic subscribe(\n\t\tkey: string,\n\t\tfn: (update: NumberedSetUpdate) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, (update) => {\n\t\t\tfn(`${this.cacheUpdateNumber}=${update}`)\n\t\t})\n\t}\n\n\tpublic emit(update: SetUpdate): void {\n\t\tthis.subject.next(update)\n\t}\n\n\tprivate doStep(update: SetUpdate): void {\n\t\tconst typeValueBreak = update.indexOf(`:`)\n\t\tconst type = update.substring(0, typeValueBreak) as SetUpdateType\n\t\tconst value = update.substring(typeValueBreak + 1)\n\t\tswitch (type) {\n\t\t\tcase `add`:\n\t\t\t\tthis.add(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `clear`:\n\t\t\t\tthis.clear()\n\t\t\t\tbreak\n\t\t\tcase `del`:\n\t\t\t\tthis.delete(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `tx`:\n\t\t\t\tfor (const subUpdate of value.split(`;`)) {\n\t\t\t\t\tthis.doStep(subUpdate as SetUpdate)\n\t\t\t\t}\n\t\t}\n\t}\n\n\tpublic getUpdateNumber(update: NumberedSetUpdate): number {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\treturn Number(update.substring(0, breakpoint))\n\t}\n\n\tpublic do(update: NumberedSetUpdate): number | `OUT_OF_RANGE` | null {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\tconst updateNumber = Number(update.substring(0, breakpoint))\n\t\tconst eventOffset = updateNumber - this.cacheUpdateNumber\n\t\tconst isFuture = eventOffset > 0\n\t\tif (isFuture) {\n\t\t\tif (eventOffset === 1) {\n\t\t\t\tthis.mode = `playback`\n\t\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\t\tthis.doStep(innerUpdate)\n\t\t\t\tthis.mode = `record`\n\t\t\t\tthis.cacheUpdateNumber = updateNumber\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.cacheUpdateNumber + 1\n\t\t}\n\t\tif (Math.abs(eventOffset) < this.cacheLimit) {\n\t\t\tconst eventIdx = this.cacheIdx + eventOffset\n\t\t\tconst cachedUpdate = this.cache[eventIdx]\n\t\t\tif (cachedUpdate === update) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tthis.mode = `playback`\n\t\t\tlet done = false\n\t\t\twhile (!done) {\n\t\t\t\tthis.cacheIdx %= this.cacheLimit\n\t\t\t\tconst u = this.cache[this.cacheIdx]\n\t\t\t\tthis.cacheIdx--\n\t\t\t\tif (!u) {\n\t\t\t\t\treturn `OUT_OF_RANGE`\n\t\t\t\t}\n\t\t\t\tthis.undo(u)\n\t\t\t\tdone = this.cacheIdx === eventIdx - 1\n\t\t\t}\n\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\tthis.doStep(innerUpdate)\n\t\t\tthis.mode = `record`\n\t\t\tthis.cacheUpdateNumber = updateNumber\n\t\t\treturn null\n\t\t}\n\t\treturn `OUT_OF_RANGE`\n\t}\n\n\tpublic undoStep(update: SetUpdate): void {\n\t\tconst breakpoint = update.indexOf(`:`)\n\t\tconst type = update.substring(0, breakpoint) as SetUpdateType\n\t\tconst value = update.substring(breakpoint + 1)\n\t\tswitch (type) {\n\t\t\tcase `add`:\n\t\t\t\tthis.delete(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `del`:\n\t\t\t\tthis.add(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `clear`: {\n\t\t\t\tconst values = JSON.parse(value) as P[]\n\t\t\t\tfor (const v of values) this.add(v)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase `tx`: {\n\t\t\t\tconst updates = value.split(`;`) as SetUpdate[]\n\t\t\t\tfor (let i = updates.length - 1; i >= 0; i--) {\n\t\t\t\t\tthis.undoStep(updates[i])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic undo(update: NumberedSetUpdate): number | null {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\tconst updateNumber = Number(update.substring(0, breakpoint))\n\t\tif (updateNumber === this.cacheUpdateNumber) {\n\t\t\tthis.mode = `playback`\n\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\tthis.undoStep(innerUpdate)\n\t\t\tthis.mode = `record`\n\t\t\tthis.cacheUpdateNumber--\n\t\t\treturn null\n\t\t}\n\t\treturn this.cacheUpdateNumber\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/transceivers/set-rtx/set-rtx.ts"],"sourcesContent":["import type { Lineage, Transceiver, TransceiverMode } from \"atom.io/internal\"\nimport { Subject } from \"atom.io/internal\"\nimport type { Json, primitive } from \"atom.io/json\"\nimport { stringifyJson } from \"atom.io/json\"\n\nexport type SetUpdateType = `add` | `clear` | `del` | `tx`\nexport type SetUpdate = `${SetUpdateType}:${string}`\nexport type NumberedSetUpdate = `${number}=${SetUpdate}`\n\nexport interface SetRTXView<P extends primitive> extends ReadonlySet<P> {\n\treadonly cache: ReadonlyArray<NumberedSetUpdate | null>\n\treadonly cacheLimit: number\n\treadonly cacheIdx: number\n\treadonly cacheUpdateNumber: number\n}\n\nexport interface SetRTXJson<P extends primitive> extends Json.Object {\n\tmembers: P[]\n\tcache: (NumberedSetUpdate | null)[]\n\tcacheLimit: number\n\tcacheIdx: number\n\tcacheUpdateNumber: number\n}\nexport class SetRTX<P extends primitive>\n\textends Set<P>\n\timplements\n\t\tTransceiver<SetRTXView<P>, NumberedSetUpdate, SetRTXJson<P>>,\n\t\tLineage\n{\n\tpublic mode: TransceiverMode = `record`\n\tpublic readonly subject: Subject<SetUpdate> = new Subject<SetUpdate>()\n\tpublic cacheLimit = 0\n\tpublic cache: (NumberedSetUpdate | null)[] = []\n\tpublic cacheIdx = -1\n\tpublic cacheUpdateNumber = -1\n\n\tpublic constructor(values?: Iterable<P>, cacheLimit = 0) {\n\t\tsuper(values)\n\t\tif (values instanceof SetRTX) {\n\t\t\tthis.parent = values\n\t\t\tthis.cacheUpdateNumber = values.cacheUpdateNumber\n\t\t}\n\t\tif (cacheLimit) {\n\t\t\tthis.cacheLimit = cacheLimit\n\t\t\tthis.cache = new Array(cacheLimit)\n\t\t\tthis.subscribe(`auto cache`, (update) => {\n\t\t\t\tthis.cacheIdx++\n\t\t\t\tthis.cacheIdx %= this.cacheLimit\n\t\t\t\tthis.cache[this.cacheIdx] = update\n\t\t\t})\n\t\t}\n\t}\n\n\tpublic readonly READONLY_VIEW: SetRTXView<P> = this\n\n\tpublic toJSON(): SetRTXJson<P> {\n\t\treturn {\n\t\t\tmembers: [...this],\n\t\t\tcache: this.cache,\n\t\t\tcacheLimit: this.cacheLimit,\n\t\t\tcacheIdx: this.cacheIdx,\n\t\t\tcacheUpdateNumber: this.cacheUpdateNumber,\n\t\t}\n\t}\n\n\tpublic static fromJSON<P extends primitive>(json: SetRTXJson<P>): SetRTX<P> {\n\t\tconst set = new SetRTX<P>(json.members, json.cacheLimit)\n\t\tset.cache = json.cache\n\t\tset.cacheIdx = json.cacheIdx\n\t\tset.cacheUpdateNumber = json.cacheUpdateNumber\n\t\treturn set\n\t}\n\n\tpublic add(value: P): this {\n\t\tconst result = super.add(value)\n\t\tif (this.mode === `record`) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`add:${stringifyJson<P>(value)}`)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic clear(): void {\n\t\tconst capturedContents = this.mode === `record` ? [...this] : null\n\t\tsuper.clear()\n\t\tif (capturedContents) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`clear:${JSON.stringify(capturedContents)}`)\n\t\t}\n\t}\n\n\tpublic delete(value: P): boolean {\n\t\tconst result = super.delete(value)\n\t\tif (this.mode === `record`) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`del:${stringifyJson<P>(value)}`)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic readonly parent: SetRTX<P> | null = null\n\tpublic child: SetRTX<P> | null = null\n\tpublic transactionUpdates: SetUpdate[] | null = null\n\tpublic transaction(run: (child: SetRTX<P>) => boolean): void {\n\t\tthis.mode = `transaction`\n\t\tthis.transactionUpdates = []\n\t\tthis.child = new SetRTX(this)\n\t\tconst unsubscribe = this.child._subscribe(`transaction`, (update) => {\n\t\t\tthis.transactionUpdates?.push(update)\n\t\t})\n\t\ttry {\n\t\t\tconst shouldCommit = run(this.child)\n\t\t\tif (shouldCommit) {\n\t\t\t\tfor (const update of this.transactionUpdates) {\n\t\t\t\t\tthis.doStep(update)\n\t\t\t\t}\n\t\t\t\tthis.cacheUpdateNumber++\n\t\t\t\tthis.emit(`tx:${this.transactionUpdates.join(`;`)}`)\n\t\t\t}\n\t\t} catch (thrown) {\n\t\t\t/* eslint-disable-next-line no-console */\n\t\t\tconsole.warn(\n\t\t\t\t`Did not apply transaction to SetRTX; this error was thrown:`,\n\t\t\t\tthrown,\n\t\t\t)\n\t\t\tthrow thrown\n\t\t} finally {\n\t\t\tunsubscribe()\n\t\t\tthis.child = null\n\t\t\tthis.transactionUpdates = null\n\t\t\tthis.mode = `record`\n\t\t}\n\t}\n\n\tprotected _subscribe(\n\t\tkey: string,\n\t\tfn: (update: SetUpdate) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, fn)\n\t}\n\tpublic subscribe(\n\t\tkey: string,\n\t\tfn: (update: NumberedSetUpdate) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, (update) => {\n\t\t\tfn(`${this.cacheUpdateNumber}=${update}`)\n\t\t})\n\t}\n\n\tpublic emit(update: SetUpdate): void {\n\t\tthis.subject.next(update)\n\t}\n\n\tprivate doStep(update: SetUpdate): void {\n\t\tconst typeValueBreak = update.indexOf(`:`)\n\t\tconst type = update.substring(0, typeValueBreak) as SetUpdateType\n\t\tconst value = update.substring(typeValueBreak + 1)\n\t\tswitch (type) {\n\t\t\tcase `add`:\n\t\t\t\tthis.add(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `clear`:\n\t\t\t\tthis.clear()\n\t\t\t\tbreak\n\t\t\tcase `del`:\n\t\t\t\tthis.delete(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `tx`:\n\t\t\t\tfor (const subUpdate of value.split(`;`)) {\n\t\t\t\t\tthis.doStep(subUpdate as SetUpdate)\n\t\t\t\t}\n\t\t}\n\t}\n\n\tpublic getUpdateNumber(update: NumberedSetUpdate): number {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\treturn Number(update.substring(0, breakpoint))\n\t}\n\n\tpublic do(update: NumberedSetUpdate): number | `OUT_OF_RANGE` | null {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\tconst updateNumber = Number(update.substring(0, breakpoint))\n\t\tconst eventOffset = updateNumber - this.cacheUpdateNumber\n\t\tconst isFuture = eventOffset > 0\n\t\tif (isFuture) {\n\t\t\tif (eventOffset === 1) {\n\t\t\t\tthis.mode = `playback`\n\t\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\t\tthis.doStep(innerUpdate)\n\t\t\t\tthis.mode = `record`\n\t\t\t\tthis.cacheUpdateNumber = updateNumber\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.cacheUpdateNumber + 1\n\t\t}\n\t\tif (Math.abs(eventOffset) < this.cacheLimit) {\n\t\t\tconst eventIdx = this.cacheIdx + eventOffset\n\t\t\tconst cachedUpdate = this.cache[eventIdx]\n\t\t\tif (cachedUpdate === update) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tthis.mode = `playback`\n\t\t\tlet done = false\n\t\t\twhile (!done) {\n\t\t\t\tthis.cacheIdx %= this.cacheLimit\n\t\t\t\tconst u = this.cache[this.cacheIdx]\n\t\t\t\tthis.cacheIdx--\n\t\t\t\tif (!u) {\n\t\t\t\t\treturn `OUT_OF_RANGE`\n\t\t\t\t}\n\t\t\t\tthis.undo(u)\n\t\t\t\tdone = this.cacheIdx === eventIdx - 1\n\t\t\t}\n\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\tthis.doStep(innerUpdate)\n\t\t\tthis.mode = `record`\n\t\t\tthis.cacheUpdateNumber = updateNumber\n\t\t\treturn null\n\t\t}\n\t\treturn `OUT_OF_RANGE`\n\t}\n\n\tpublic undoStep(update: SetUpdate): void {\n\t\tconst breakpoint = update.indexOf(`:`)\n\t\tconst type = update.substring(0, breakpoint) as SetUpdateType\n\t\tconst value = update.substring(breakpoint + 1)\n\t\tswitch (type) {\n\t\t\tcase `add`:\n\t\t\t\tthis.delete(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `del`:\n\t\t\t\tthis.add(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `clear`: {\n\t\t\t\tconst values = JSON.parse(value) as P[]\n\t\t\t\tfor (const v of values) this.add(v)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase `tx`: {\n\t\t\t\tconst updates = value.split(`;`) as SetUpdate[]\n\t\t\t\tfor (let i = updates.length - 1; i >= 0; i--) {\n\t\t\t\t\tthis.undoStep(updates[i])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic undo(update: NumberedSetUpdate): number | null {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\tconst updateNumber = Number(update.substring(0, breakpoint))\n\t\tif (updateNumber === this.cacheUpdateNumber) {\n\t\t\tthis.mode = `playback`\n\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\tthis.undoStep(innerUpdate)\n\t\t\tthis.mode = `record`\n\t\t\tthis.cacheUpdateNumber--\n\t\t\treturn null\n\t\t}\n\t\treturn this.cacheUpdateNumber\n\t}\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/web/persist-sync.ts"],"sourcesContent":["import type { AtomEffect, ViewOf } from \"atom.io\"\n\nexport type StringInterface<T> = {\n\tstringify: (t: ViewOf<T>) => string\n\tparse: (s: string) => T\n}\n\nexport const persistSync =\n\t<T>(\n\t\tstorage: Storage,\n\t\t{ stringify, parse }: StringInterface<T>,\n\t\tkey: string,\n\t): AtomEffect<T> =>\n\t({ setSelf, onSet }) => {\n\t\tconst savedValue = storage.getItem(key)\n\t\tif (savedValue != null) setSelf(parse(savedValue))\n\n\t\tonSet(({ newValue }) => {\n\t\t\tif (newValue == null) {\n\t\t\t\tstorage.removeItem(key)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstorage.setItem(key, stringify(newValue))\n\t\t})\n\t}\n"],"mappings":";AAOA,MAAa,eAEX,SACA,EAAE,WAAW,OAA2B,EACxC,SAEA,EAAE,SAAS,OAAO,KAAK;CACvB,MAAM,aAAa,QAAQ,QAAQ;AACnC,KAAI,cAAc,KAAM,SAAQ,MAAM;AAEtC,QAAO,EAAE,UAAU,KAAK;AACvB,MAAI,YAAY,MAAM;AACrB,WAAQ,WAAW;AACnB;EACA;AACD,UAAQ,QAAQ,KAAK,UAAU;CAC/B;AACD"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/web/persist-sync.ts"],"sourcesContent":["import type { AtomEffect, ViewOf } from \"atom.io\"\n\nexport type StringInterface<T> = {\n\tstringify: (t: ViewOf<T>) => string\n\tparse: (s: string) => T\n}\n\nexport const persistSync =\n\t<T>(\n\t\tstorage: Storage,\n\t\t{ stringify, parse }: StringInterface<T>,\n\t\tkey: string,\n\t): AtomEffect<T> =>\n\t({ setSelf, onSet }) => {\n\t\tconst savedValue = storage.getItem(key)\n\t\tif (savedValue != null) setSelf(parse(savedValue))\n\n\t\tonSet(({ newValue }) => {\n\t\t\tif (newValue == null) {\n\t\t\t\tstorage.removeItem(key)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tstorage.setItem(key, stringify(newValue))\n\t\t})\n\t}\n"],"mappings":";AAOA,MAAa,eAEX,SACA,EAAE,WAAW,SACb,SAEA,EAAE,SAAS,YAAY;CACvB,MAAM,aAAa,QAAQ,QAAQ;AACnC,KAAI,cAAc,KAAM,SAAQ,MAAM;AAEtC,QAAO,EAAE,eAAe;AACvB,MAAI,YAAY,MAAM;AACrB,WAAQ,WAAW;AACnB;;AAED,UAAQ,QAAQ,KAAK,UAAU"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atom.io",
3
- "version": "0.39.0",
3
+ "version": "0.40.0",
4
4
  "description": "Composable and testable reactive data library.",
5
5
  "homepage": "https://atom.io.fyi",
6
6
  "sideEffects": false,
@@ -65,21 +65,21 @@
65
65
  "@storybook/addon-onboarding": "9.1.3",
66
66
  "@storybook/react-vite": "9.1.3",
67
67
  "@testing-library/react": "16.3.0",
68
- "@types/bun": "npm:bun-types@1.2.20",
68
+ "@types/bun": "npm:bun-types@1.2.21",
69
69
  "@types/eslint": "9.6.1",
70
70
  "@types/estree": "1.0.8",
71
71
  "@types/http-proxy": "1.17.16",
72
72
  "@types/npmlog": "7.0.0",
73
73
  "@types/react": "19.1.11",
74
74
  "@types/tmp": "0.2.6",
75
- "@typescript-eslint/parser": "8.40.0",
76
- "@typescript-eslint/rule-tester": "8.40.0",
77
- "@typescript-eslint/utils": "8.40.0",
75
+ "@typescript-eslint/parser": "8.41.0",
76
+ "@typescript-eslint/rule-tester": "8.41.0",
77
+ "@typescript-eslint/utils": "8.41.0",
78
78
  "@vitest/coverage-v8": "3.2.4",
79
79
  "@vitest/ui": "3.2.4",
80
- "concurrently": "9.2.0",
80
+ "concurrently": "9.2.1",
81
81
  "drizzle-kit": "0.31.4",
82
- "drizzle-orm": "0.44.4",
82
+ "drizzle-orm": "0.44.5",
83
83
  "eslint": "9.34.0",
84
84
  "happy-dom": "18.0.1",
85
85
  "http-proxy": "1.18.1",
@@ -96,7 +96,7 @@
96
96
  "socket.io-client": "4.8.1",
97
97
  "storybook": "9.1.3",
98
98
  "tmp": "0.2.5",
99
- "tsdown": "0.14.1",
99
+ "tsdown": "0.14.2",
100
100
  "typescript": "5.9.2",
101
101
  "vite": "7.1.3",
102
102
  "vite-tsconfig-paths": "5.1.4",
@@ -1,25 +1,34 @@
1
1
  import type {
2
2
  AtomOnly,
3
3
  AtomUpdateEvent,
4
+ StateCreationEvent,
4
5
  TimelineManageable,
5
6
  TimelineSelectorUpdateEvent,
6
7
  } from "atom.io"
7
8
 
8
9
  import type { Store } from "../store"
9
10
  import { ingestAtomUpdateEvent } from "./ingest-atom-update"
11
+ import { ingestCreationEvent } from "./ingest-creation-disposal"
10
12
 
11
13
  export function ingestSelectorUpdateEvent(
12
14
  store: Store,
13
15
  selectorUpdate: TimelineSelectorUpdateEvent<any>,
14
16
  applying: `newValue` | `oldValue`,
15
17
  ): void {
16
- let updates: AtomUpdateEvent<AtomOnly<TimelineManageable>>[]
18
+ let updates: (
19
+ | AtomUpdateEvent<AtomOnly<TimelineManageable>>
20
+ | StateCreationEvent<any>
21
+ )[]
17
22
  if (applying === `newValue`) {
18
- updates = selectorUpdate.atomUpdates
23
+ updates = selectorUpdate.subEvents
19
24
  } else {
20
- updates = selectorUpdate.atomUpdates.toReversed()
25
+ updates = selectorUpdate.subEvents.toReversed()
21
26
  }
22
27
  for (const atomUpdate of updates) {
23
- ingestAtomUpdateEvent(store, atomUpdate, applying)
28
+ if (atomUpdate.type === `state_creation`) {
29
+ ingestCreationEvent(store, atomUpdate, applying)
30
+ } else {
31
+ ingestAtomUpdateEvent(store, atomUpdate, applying)
32
+ }
24
33
  }
25
34
  }
@@ -12,6 +12,7 @@ import { stringifyJson } from "atom.io/json"
12
12
 
13
13
  import type { RegularAtomFamily } from ".."
14
14
  import { createRegularAtom } from "../atom"
15
+ import { isFn } from "../is-fn"
15
16
  import { newest } from "../lineage"
16
17
  import type { Store } from "../store"
17
18
  import { Subject } from "../subject"
@@ -47,7 +48,7 @@ export function createRegularAtomFamily<T, K extends Canonical>(
47
48
  const def = options.default
48
49
  const individualOptions: RegularAtomOptions<T> = {
49
50
  key: fullKey,
50
- default: def instanceof Function ? () => def(key) : def,
51
+ default: isFn(def) ? () => def(key) : def,
51
52
  }
52
53
  if (options.effects) {
53
54
  individualOptions.effects = options.effects(key)
@@ -67,7 +68,7 @@ export function createRegularAtomFamily<T, K extends Canonical>(
67
68
  }) satisfies RegularAtomFamily<T, K>
68
69
 
69
70
  store.families.set(options.key, atomFamily)
70
- if (options.default instanceof Function === false) {
71
+ if (isFn(options.default) === false) {
71
72
  store.defaults.set(options.key, options.default)
72
73
  }
73
74
  return familyToken
@@ -2,6 +2,7 @@ import type { ViewOf } from "atom.io"
2
2
 
3
3
  import type { ReadableState } from ".."
4
4
  import { readFromCache, writeToCache } from "../caching"
5
+ import { isFn } from "../is-fn"
5
6
  import type { Store } from "../store"
6
7
 
7
8
  export function readOrComputeValue<T>(
@@ -33,7 +34,7 @@ export function readOrComputeValue<T>(
33
34
  return state.getFrom(target)
34
35
  case `atom`: {
35
36
  let def: T
36
- if (state.default instanceof Function) {
37
+ if (isFn(state.default)) {
37
38
  def = state.default()
38
39
  target.logger.info(`✨`, state.type, key, `computed default`, def)
39
40
  } else {
@@ -66,9 +66,22 @@ export function reduceReference<T, K extends Canonical>(
66
66
  const isCounterfeit = `counterfeit` in token
67
67
  const isNewlyCreated = Boolean(brandNewToken) && isCounterfeit === false
68
68
  if (isNewlyCreated && family) {
69
- const stateCreationEvent: StateCreationEvent<ReadableToken<T>> = {
69
+ let subType: `readable` | `writable`
70
+ switch (token.type) {
71
+ case `readonly_pure_selector`:
72
+ case `readonly_held_selector`:
73
+ subType = `readable`
74
+ break
75
+ case `atom`:
76
+ case `mutable_atom`:
77
+ case `writable_pure_selector`:
78
+ case `writable_held_selector`:
79
+ subType = `writable`
80
+ break
81
+ }
82
+ const stateCreationEvent: StateCreationEvent<any> = {
70
83
  type: `state_creation`,
71
- subType: `readable`,
84
+ subType,
72
85
  token,
73
86
  timestamp: Date.now(),
74
87
  }
@@ -0,0 +1,9 @@
1
+ import type { Fn } from "./utility-types"
2
+
3
+ const NON_CTOR_FN_REGEX =
4
+ /^\[object (?:Async|Generator|AsyncGenerator)?Function\]$/
5
+
6
+ export function isFn(input: unknown): input is Fn {
7
+ const protoString = Object.prototype.toString.call(input)
8
+ return NON_CTOR_FN_REGEX.test(protoString)
9
+ }
@@ -1,4 +1,4 @@
1
- import type { ReadableToken } from "atom.io"
1
+ import type { AtomUpdateEvent, ReadableToken, StateCreationEvent } from "atom.io"
2
2
 
3
3
  import type { Store } from "./store"
4
4
  import { isChildStore } from "./transaction/is-root-store"
@@ -14,6 +14,7 @@ export type OpenOperation<R extends ReadableToken<any> = ReadableToken<any>> = {
14
14
  done: Set<string>
15
15
  prev: Map<string, any>
16
16
  timestamp: number
17
+ subEvents: (AtomUpdateEvent<any> | StateCreationEvent<any>)[]
17
18
  }
18
19
 
19
20
  export function openOperation(
@@ -36,6 +37,7 @@ export function openOperation(
36
37
  prev: new Map(),
37
38
  timestamp: Date.now(),
38
39
  token,
40
+ subEvents: [],
39
41
  }
40
42
  store.logger.info(
41
43
  `⭕`,
@@ -1,8 +1,13 @@
1
+ import { isFn } from "../is-fn"
2
+
1
3
  export type Modify<T> = (thing: T) => T
2
4
 
3
- export const become =
4
- <T>(nextVersionOfThing: Modify<T> | T) =>
5
- (originalThing: T): T =>
6
- nextVersionOfThing instanceof Function
7
- ? nextVersionOfThing(originalThing)
8
- : nextVersionOfThing
5
+ export function become<T>(
6
+ nextVersionOfThing: Modify<T> | T,
7
+ originalThing: T,
8
+ ): T {
9
+ if (isFn(nextVersionOfThing)) {
10
+ return nextVersionOfThing(originalThing)
11
+ }
12
+ return nextVersionOfThing
13
+ }
@@ -1,12 +1,12 @@
1
- import type { AtomUpdateEvent, StateCreationEvent, StateUpdate } from "atom.io"
1
+ import type {
2
+ AtomUpdateEvent,
3
+ StateCreationEvent,
4
+ StateUpdate,
5
+ TimelineEvent,
6
+ } from "atom.io"
2
7
 
3
- import {
4
- type MutableAtom,
5
- newest,
6
- type Subject,
7
- type WritableFamily,
8
- type WritableState,
9
- } from ".."
8
+ import type { MutableAtom, Subject, WritableFamily, WritableState } from ".."
9
+ import { newest } from ".."
10
10
  import { hasRole } from "../atom"
11
11
  import { readOrComputeValue } from "../get-state"
12
12
  import type { Transceiver } from "../mutable"
@@ -15,24 +15,27 @@ import type { OpenOperation } from "../operation"
15
15
  import { deposit, type Store } from "../store"
16
16
  import { isChildStore, isRootStore } from "../transaction"
17
17
  import { evictDownstreamFromAtom } from "./evict-downstream"
18
+ import type { ProtoUpdate } from "./operate-on-store"
18
19
 
19
20
  export function dispatchOrDeferStateUpdate<T>(
20
21
  target: Store & { operation: OpenOperation<any> },
21
22
  state: WritableState<T>,
22
- [oldValue, newValue]: [T, T],
23
+ { oldValue, newValue }: ProtoUpdate<T>,
23
24
  stateIsNewlyCreated: boolean,
24
25
  family?: WritableFamily<T, any>,
25
26
  ): void {
26
27
  const token = deposit(state)
27
28
  if (stateIsNewlyCreated && family) {
28
29
  state.subject.next({ newValue })
29
- const stateCreationEvent: StateCreationEvent<any> = {
30
+ const stateCreationEvent: StateCreationEvent<any> & TimelineEvent<any> = {
31
+ write: true,
30
32
  type: `state_creation`,
31
33
  subType: `writable`,
32
34
  token,
33
35
  timestamp: Date.now(),
34
36
  value: newValue,
35
37
  }
38
+ target.operation.subEvents.push(stateCreationEvent)
36
39
  const familySubject = family.subject as Subject<StateCreationEvent<any>>
37
40
  familySubject.next(stateCreationEvent)
38
41
  const innerTarget = newest(target)
@@ -74,7 +77,7 @@ export function dispatchOrDeferStateUpdate<T>(
74
77
  `is now (`,
75
78
  newValue,
76
79
  `) subscribers:`,
77
- subject.subscribers,
80
+ subject.subscribers.keys(),
78
81
  )
79
82
  break
80
83
  case `atom`:
@@ -89,7 +92,7 @@ export function dispatchOrDeferStateUpdate<T>(
89
92
  `->`,
90
93
  newValue,
91
94
  `) subscribers:`,
92
- subject.subscribers,
95
+ subject.subscribers.keys(),
93
96
  )
94
97
  }
95
98
  subject.next(update)
@@ -13,6 +13,8 @@ import { resetAtomOrSelector } from "./reset-atom-or-selector"
13
13
  import { RESET_STATE } from "./reset-in-store"
14
14
  import { setAtomOrSelector } from "./set-atom-or-selector"
15
15
 
16
+ export type ProtoUpdate<T> = { oldValue: T; newValue: T }
17
+
16
18
  export const OWN_OP: unique symbol = Symbol(`OWN_OP`)
17
19
  export const JOIN_OP: unique symbol = Symbol(`JOIN_OP`)
18
20
 
@@ -114,7 +116,7 @@ export function operateOnStore<T, New extends T>(
114
116
  }
115
117
 
116
118
  const state = withdraw(target, token)
117
- let protoUpdate: [T, T]
119
+ let protoUpdate: ProtoUpdate<T>
118
120
  if (value === RESET_STATE) {
119
121
  protoUpdate = resetAtomOrSelector(target, state)
120
122
  } else {
@@ -1,20 +1,20 @@
1
1
  import type { Atom, OpenOperation, Store, WritableState } from ".."
2
2
  import { traceRootSelectorAtoms } from ".."
3
+ import { isFn } from "../is-fn"
3
4
  import { dispatchOrDeferStateUpdate } from "./dispatch-state-update"
5
+ import type { ProtoUpdate } from "./operate-on-store"
4
6
  import { setAtom } from "./set-atom"
5
7
 
6
8
  function resetAtom<T>(
7
9
  target: Store & { operation: OpenOperation },
8
10
  atom: Atom<T>,
9
- ): [oldValue: T, newValue: T] {
11
+ ): ProtoUpdate<T> {
10
12
  switch (atom.type) {
11
13
  case `mutable_atom`:
12
14
  return setAtom(target, atom, new atom.class())
13
15
  case `atom`: {
14
16
  let def = atom.default
15
- if (def instanceof Function) {
16
- def = def()
17
- }
17
+ if (isFn(def)) def = def()
18
18
  return setAtom(target, atom, def)
19
19
  }
20
20
  }
@@ -23,8 +23,8 @@ function resetAtom<T>(
23
23
  export function resetAtomOrSelector<T>(
24
24
  target: Store & { operation: OpenOperation },
25
25
  state: WritableState<T>,
26
- ): [oldValue: T, newValue: T] {
27
- let protoUpdate: [T, T]
26
+ ): ProtoUpdate<T> {
27
+ let protoUpdate: ProtoUpdate<T>
28
28
  switch (state.type) {
29
29
  case `atom`:
30
30
  case `mutable_atom`:
@@ -40,7 +40,7 @@ export function resetAtomOrSelector<T>(
40
40
  dispatchOrDeferStateUpdate(target, state, rootProtoUpdate, false)
41
41
  }
42
42
  const newValue = state.getFrom(target)
43
- protoUpdate = [oldValue, newValue]
43
+ protoUpdate = { oldValue, newValue }
44
44
  }
45
45
  break
46
46
  }
@@ -1,6 +1,7 @@
1
1
  import type { WritableState } from ".."
2
2
  import type { OpenOperation } from "../operation"
3
3
  import type { Store } from "../store"
4
+ import type { ProtoUpdate } from "./operate-on-store"
4
5
  import { setAtom } from "./set-atom"
5
6
  import { setSelector } from "./set-selector"
6
7
 
@@ -8,8 +9,8 @@ export const setAtomOrSelector = <T>(
8
9
  target: Store & { operation: OpenOperation },
9
10
  state: WritableState<T>,
10
11
  value: T | ((oldValue: T) => T),
11
- ): [oldValue: T, newValue: T] => {
12
- let protoUpdate: [T, T]
12
+ ): ProtoUpdate<T> => {
13
+ let protoUpdate: ProtoUpdate<T>
13
14
  switch (state.type) {
14
15
  case `atom`:
15
16
  case `mutable_atom`:
@@ -4,17 +4,18 @@ import { readOrComputeValue } from "../get-state/read-or-compute-value"
4
4
  import { markDone } from "../operation"
5
5
  import { become } from "./become"
6
6
  import { evictDownstreamFromAtom } from "./evict-downstream"
7
+ import type { ProtoUpdate } from "./operate-on-store"
7
8
 
8
9
  export const setAtom = <T>(
9
10
  target: Store & { operation: OpenOperation<any> },
10
11
  atom: Atom<T>,
11
12
  next: T | ((oldValue: T) => T),
12
- ): [oldValue: T, newValue: T] => {
13
+ ): ProtoUpdate<T> => {
13
14
  const oldValue = readOrComputeValue(target, atom, `mut`)
14
- let newValue = become(next)(oldValue)
15
+ let newValue = become(next, oldValue)
15
16
  target.logger.info(`⭐`, `atom`, atom.key, `setting value`, newValue)
16
17
  newValue = writeToCache(target, atom, newValue)
17
18
  markDone(target, atom.key)
18
19
  evictDownstreamFromAtom(target, atom)
19
- return [oldValue, newValue]
20
+ return { oldValue, newValue }
20
21
  }
@@ -1,14 +1,15 @@
1
- import type { WritableSelector } from ".."
1
+ import { readOrComputeValue, type WritableSelector } from ".."
2
2
  import { writeToCache } from "../caching"
3
3
  import { markDone, type OpenOperation } from "../operation"
4
4
  import type { Store } from "../store"
5
5
  import { become } from "./become"
6
+ import type { ProtoUpdate } from "./operate-on-store"
6
7
 
7
8
  export function setSelector<T>(
8
9
  target: Store & { operation: OpenOperation<any> },
9
10
  selector: WritableSelector<T>,
10
11
  next: T | ((oldValue: T) => T),
11
- ): [oldValue: T, newValue: T] {
12
+ ): ProtoUpdate<T> {
12
13
  let oldValue: T
13
14
  let newValue: T
14
15
  let constant: T
@@ -17,13 +18,13 @@ export function setSelector<T>(
17
18
 
18
19
  switch (selector.type) {
19
20
  case `writable_pure_selector`:
20
- oldValue = selector.getFrom(target)
21
- newValue = become(next)(oldValue)
22
- writeToCache(target, selector, newValue)
21
+ oldValue = readOrComputeValue(target, selector, `mut`)
22
+ newValue = become(next, oldValue)
23
+ newValue = writeToCache(target, selector, newValue)
23
24
  break
24
25
  case `writable_held_selector`:
25
26
  constant = selector.const
26
- become(next)(constant)
27
+ become(next, constant)
27
28
  oldValue = constant
28
29
  newValue = constant
29
30
  }
@@ -31,5 +32,5 @@ export function setSelector<T>(
31
32
  target.logger.info(`⭐`, type, key, `setting to`, newValue)
32
33
  markDone(target, key)
33
34
  selector.setSelf(newValue)
34
- return [oldValue, newValue]
35
+ return { oldValue, newValue }
35
36
  }