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.
- package/dist/data/index.js.map +1 -1
- package/dist/eslint-plugin/index.js +2 -1
- package/dist/eslint-plugin/index.js.map +1 -1
- package/dist/internal/index.d.ts +14 -8
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +129 -87
- package/dist/internal/index.js.map +1 -1
- package/dist/introspection/index.d.ts.map +1 -1
- package/dist/introspection/index.js.map +1 -1
- package/dist/json/index.d.ts.map +1 -1
- package/dist/json/index.js.map +1 -1
- package/dist/main/index.d.ts +8 -7
- package/dist/main/index.d.ts.map +1 -1
- package/dist/main/index.js.map +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +43 -2
- package/dist/react/index.js.map +1 -1
- package/dist/react-devtools/index.d.ts.map +1 -1
- package/dist/react-devtools/index.js +12 -10
- package/dist/react-devtools/index.js.map +1 -1
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime-client/index.js.map +1 -1
- package/dist/realtime-react/index.js.map +1 -1
- package/dist/realtime-server/index.d.ts.map +1 -1
- package/dist/realtime-server/index.js.map +1 -1
- package/dist/realtime-testing/index.d.ts.map +1 -1
- package/dist/realtime-testing/index.js.map +1 -1
- package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
- package/dist/transceivers/set-rtx/index.js.map +1 -1
- package/dist/web/index.js.map +1 -1
- package/package.json +8 -8
- package/src/internal/events/ingest-selector-update.ts +13 -4
- package/src/internal/families/create-regular-atom-family.ts +3 -2
- package/src/internal/get-state/read-or-compute-value.ts +2 -1
- package/src/internal/get-state/reduce-reference.ts +15 -2
- package/src/internal/is-fn.ts +9 -0
- package/src/internal/operation.ts +3 -1
- package/src/internal/set-state/become.ts +11 -6
- package/src/internal/set-state/dispatch-state-update.ts +15 -12
- package/src/internal/set-state/operate-on-store.ts +3 -1
- package/src/internal/set-state/reset-atom-or-selector.ts +7 -7
- package/src/internal/set-state/set-atom-or-selector.ts +3 -2
- package/src/internal/set-state/set-atom.ts +4 -3
- package/src/internal/set-state/set-selector.ts +8 -7
- package/src/internal/timeline/create-timeline.ts +133 -98
- package/src/internal/timeline/time-travel.ts +42 -31
- package/src/internal/transaction/apply-transaction.ts +2 -2
- package/src/internal/transaction/build-transaction.ts +1 -1
- package/src/main/events.ts +8 -2
- package/src/main/logger.ts +1 -1
- package/src/main/timeline.ts +1 -7
- package/src/react-devtools/Updates.tsx +14 -9
- package/src/react-devtools/json-editor/editors-by-type/array-editor.tsx +1 -1
- package/src/react-devtools/json-editor/editors-by-type/object-editor.tsx +2 -3
- package/src/react-devtools/json-editor/editors-by-type/utilities/array-elements.ts +1 -1
- package/src/react-devtools/json-editor/editors-by-type/utilities/object-properties.ts +1 -1
- package/dist/use-o-DXPncKmZ.js +0 -47
- 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;
|
|
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":";;;;AAuBA,IAAa,SAAb,MAAa,eACJ,IAIT;CACC,AAAO,OAAwB;CAC/B,AAAgB,UAA8B,IAAI;CAClD,AAAO,aAAa;CACpB,AAAO,QAAsC;CAC7C,AAAO,WAAW;CAClB,AAAO,oBAAoB;CAE3B,AAAO,YAAY,QAAsB,aAAa,GAAG;AACxD,QAAM;AACN,MAAI,kBAAkB,QAAQ;AAC7B,QAAK,SAAS;AACd,QAAK,oBAAoB,OAAO;;AAEjC,MAAI,YAAY;AACf,QAAK,aAAa;AAClB,QAAK,QAAQ,IAAI,MAAM;AACvB,QAAK,UAAU,eAAe,WAAW;AACxC,SAAK;AACL,SAAK,YAAY,KAAK;AACtB,SAAK,MAAM,KAAK,YAAY;;;;CAK/B,AAAgB,gBAA+B;CAE/C,AAAO,SAAwB;AAC9B,SAAO;GACN,SAAS,CAAC,GAAG;GACb,OAAO,KAAK;GACZ,YAAY,KAAK;GACjB,UAAU,KAAK;GACf,mBAAmB,KAAK;;;CAI1B,OAAc,SAA8B,MAAgC;EAC3E,MAAM,MAAM,IAAI,OAAU,KAAK,SAAS,KAAK;AAC7C,MAAI,QAAQ,KAAK;AACjB,MAAI,WAAW,KAAK;AACpB,MAAI,oBAAoB,KAAK;AAC7B,SAAO;;CAGR,AAAO,IAAI,OAAgB;EAC1B,MAAM,SAAS,MAAM,IAAI;AACzB,MAAI,KAAK,SAAS,UAAU;AAC3B,QAAK;AACL,QAAK,KAAK,OAAO,cAAiB;;AAEnC,SAAO;;CAGR,AAAO,QAAc;EACpB,MAAM,mBAAmB,KAAK,SAAS,WAAW,CAAC,GAAG,QAAQ;AAC9D,QAAM;AACN,MAAI,kBAAkB;AACrB,QAAK;AACL,QAAK,KAAK,SAAS,KAAK,UAAU;;;CAIpC,AAAO,OAAO,OAAmB;EAChC,MAAM,SAAS,MAAM,OAAO;AAC5B,MAAI,KAAK,SAAS,UAAU;AAC3B,QAAK;AACL,QAAK,KAAK,OAAO,cAAiB;;AAEnC,SAAO;;CAGR,AAAgB,SAA2B;CAC3C,AAAO,QAA0B;CACjC,AAAO,qBAAyC;CAChD,AAAO,YAAY,KAA0C;AAC5D,OAAK,OAAO;AACZ,OAAK,qBAAqB;AAC1B,OAAK,QAAQ,IAAI,OAAO;EACxB,MAAM,cAAc,KAAK,MAAM,WAAW,gBAAgB,WAAW;AACpE,QAAK,oBAAoB,KAAK;;AAE/B,MAAI;GACH,MAAM,eAAe,IAAI,KAAK;AAC9B,OAAI,cAAc;AACjB,SAAK,MAAM,UAAU,KAAK,mBACzB,MAAK,OAAO;AAEb,SAAK;AACL,SAAK,KAAK,MAAM,KAAK,mBAAmB,KAAK;;WAEtC,QAAQ;AAEhB,WAAQ,KACP,+DACA;AAED,SAAM;YACG;AACT;AACA,QAAK,QAAQ;AACb,QAAK,qBAAqB;AAC1B,QAAK,OAAO;;;CAId,AAAU,WACT,KACA,IACa;AACb,SAAO,KAAK,QAAQ,UAAU,KAAK;;CAEpC,AAAO,UACN,KACA,IACa;AACb,SAAO,KAAK,QAAQ,UAAU,MAAM,WAAW;AAC9C,MAAG,GAAG,KAAK,kBAAkB,GAAG;;;CAIlC,AAAO,KAAK,QAAyB;AACpC,OAAK,QAAQ,KAAK;;CAGnB,AAAQ,OAAO,QAAyB;EACvC,MAAM,iBAAiB,OAAO,QAAQ;EACtC,MAAM,OAAO,OAAO,UAAU,GAAG;EACjC,MAAM,QAAQ,OAAO,UAAU,iBAAiB;AAChD,UAAQ,MAAR;GACC,KAAK;AACJ,SAAK,IAAI,KAAK,MAAM;AACpB;GACD,KAAK;AACJ,SAAK;AACL;GACD,KAAK;AACJ,SAAK,OAAO,KAAK,MAAM;AACvB;GACD,KAAK,KACJ,MAAK,MAAM,aAAa,MAAM,MAAM,KACnC,MAAK,OAAO;;;CAKhB,AAAO,gBAAgB,QAAmC;EACzD,MAAM,aAAa,OAAO,QAAQ;AAClC,SAAO,OAAO,OAAO,UAAU,GAAG;;CAGnC,AAAO,GAAG,QAA2D;EACpE,MAAM,aAAa,OAAO,QAAQ;EAClC,MAAM,eAAe,OAAO,OAAO,UAAU,GAAG;EAChD,MAAM,cAAc,eAAe,KAAK;EACxC,MAAM,WAAW,cAAc;AAC/B,MAAI,UAAU;AACb,OAAI,gBAAgB,GAAG;AACtB,SAAK,OAAO;IACZ,MAAM,cAAc,OAAO,UAAU,aAAa;AAClD,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,oBAAoB;AACzB,WAAO;;AAER,UAAO,KAAK,oBAAoB;;AAEjC,MAAI,KAAK,IAAI,eAAe,KAAK,YAAY;GAC5C,MAAM,WAAW,KAAK,WAAW;GACjC,MAAM,eAAe,KAAK,MAAM;AAChC,OAAI,iBAAiB,OACpB,QAAO;AAER,QAAK,OAAO;GACZ,IAAI,OAAO;AACX,UAAO,CAAC,MAAM;AACb,SAAK,YAAY,KAAK;IACtB,MAAM,IAAI,KAAK,MAAM,KAAK;AAC1B,SAAK;AACL,QAAI,CAAC,EACJ,QAAO;AAER,SAAK,KAAK;AACV,WAAO,KAAK,aAAa,WAAW;;GAErC,MAAM,cAAc,OAAO,UAAU,aAAa;AAClD,QAAK,OAAO;AACZ,QAAK,OAAO;AACZ,QAAK,oBAAoB;AACzB,UAAO;;AAER,SAAO;;CAGR,AAAO,SAAS,QAAyB;EACxC,MAAM,aAAa,OAAO,QAAQ;EAClC,MAAM,OAAO,OAAO,UAAU,GAAG;EACjC,MAAM,QAAQ,OAAO,UAAU,aAAa;AAC5C,UAAQ,MAAR;GACC,KAAK;AACJ,SAAK,OAAO,KAAK,MAAM;AACvB;GACD,KAAK;AACJ,SAAK,IAAI,KAAK,MAAM;AACpB;GACD,KAAK,SAAS;IACb,MAAM,SAAS,KAAK,MAAM;AAC1B,SAAK,MAAM,KAAK,OAAQ,MAAK,IAAI;AACjC;;GAED,KAAK,MAAM;IACV,MAAM,UAAU,MAAM,MAAM;AAC5B,SAAK,IAAI,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,IACxC,MAAK,SAAS,QAAQ;;;;CAM1B,AAAO,KAAK,QAA0C;EACrD,MAAM,aAAa,OAAO,QAAQ;EAClC,MAAM,eAAe,OAAO,OAAO,UAAU,GAAG;AAChD,MAAI,iBAAiB,KAAK,mBAAmB;AAC5C,QAAK,OAAO;GACZ,MAAM,cAAc,OAAO,UAAU,aAAa;AAClD,QAAK,SAAS;AACd,QAAK,OAAO;AACZ,QAAK;AACL,UAAO;;AAER,SAAO,KAAK"}
|
package/dist/web/index.js.map
CHANGED
|
@@ -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,
|
|
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.
|
|
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.
|
|
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.
|
|
76
|
-
"@typescript-eslint/rule-tester": "8.
|
|
77
|
-
"@typescript-eslint/utils": "8.
|
|
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.
|
|
80
|
+
"concurrently": "9.2.1",
|
|
81
81
|
"drizzle-kit": "0.31.4",
|
|
82
|
-
"drizzle-orm": "0.44.
|
|
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.
|
|
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:
|
|
18
|
+
let updates: (
|
|
19
|
+
| AtomUpdateEvent<AtomOnly<TimelineManageable>>
|
|
20
|
+
| StateCreationEvent<any>
|
|
21
|
+
)[]
|
|
17
22
|
if (applying === `newValue`) {
|
|
18
|
-
updates = selectorUpdate.
|
|
23
|
+
updates = selectorUpdate.subEvents
|
|
19
24
|
} else {
|
|
20
|
-
updates = selectorUpdate.
|
|
25
|
+
updates = selectorUpdate.subEvents.toReversed()
|
|
21
26
|
}
|
|
22
27
|
for (const atomUpdate of updates) {
|
|
23
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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 {
|
|
1
|
+
import type {
|
|
2
|
+
AtomUpdateEvent,
|
|
3
|
+
StateCreationEvent,
|
|
4
|
+
StateUpdate,
|
|
5
|
+
TimelineEvent,
|
|
6
|
+
} from "atom.io"
|
|
2
7
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
):
|
|
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
|
|
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
|
-
):
|
|
27
|
-
let protoUpdate:
|
|
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 =
|
|
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
|
-
):
|
|
12
|
-
let protoUpdate:
|
|
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
|
-
):
|
|
13
|
+
): ProtoUpdate<T> => {
|
|
13
14
|
const oldValue = readOrComputeValue(target, atom, `mut`)
|
|
14
|
-
let newValue = become(next
|
|
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
|
|
20
|
+
return { oldValue, newValue }
|
|
20
21
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import type
|
|
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
|
-
):
|
|
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 =
|
|
21
|
-
newValue = become(next
|
|
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
|
|
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
|
|
35
|
+
return { oldValue, newValue }
|
|
35
36
|
}
|