atom.io 0.8.0 → 0.8.2

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 (73) hide show
  1. package/dist/index.d.mts +8 -5
  2. package/dist/index.d.ts +8 -5
  3. package/dist/index.js +67 -72
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +34 -38
  6. package/dist/index.mjs.map +1 -1
  7. package/internal/dist/index.d.mts +27 -33
  8. package/internal/dist/index.d.ts +27 -33
  9. package/internal/dist/index.js +45 -69
  10. package/internal/dist/index.js.map +1 -1
  11. package/internal/dist/index.mjs +34 -39
  12. package/internal/dist/index.mjs.map +1 -1
  13. package/internal/src/caching.ts +12 -5
  14. package/internal/src/future.ts +2 -4
  15. package/internal/src/mutable/create-mutable-atom-family.ts +4 -4
  16. package/internal/src/mutable/create-mutable-atom.ts +6 -5
  17. package/internal/src/mutable/is-atom-token-mutable.ts +3 -3
  18. package/internal/src/mutable/tracker-family.ts +4 -4
  19. package/internal/src/mutable/tracker.ts +20 -19
  20. package/internal/src/operation.ts +5 -2
  21. package/internal/src/selector/create-read-write-selector.ts +2 -2
  22. package/internal/src/selector/register-selector.ts +2 -2
  23. package/internal/src/set-state/{set-atom-state.ts → set-atom.ts} +1 -1
  24. package/internal/src/set-state/set-selector-state.ts +1 -12
  25. package/internal/src/set-state/set-state-internal.ts +4 -5
  26. package/internal/src/store/withdraw-new-family-member.ts +7 -7
  27. package/internal/src/store/withdraw.ts +15 -9
  28. package/internal/src/subscribe/subscribe-to-root-atoms.ts +1 -1
  29. package/internal/src/timeline/add-atom-to-timeline.ts +2 -2
  30. package/internal/src/transaction/apply-transaction.ts +1 -1
  31. package/internal/src/transaction/redo-transaction.ts +1 -1
  32. package/internal/src/transaction/undo-transaction.ts +1 -1
  33. package/package.json +11 -8
  34. package/react-devtools/dist/index.d.mts +4 -4
  35. package/react-devtools/dist/index.d.ts +4 -4
  36. package/react-devtools/dist/index.js +3 -3
  37. package/react-devtools/dist/index.js.map +1 -1
  38. package/react-devtools/dist/index.mjs +3 -3
  39. package/react-devtools/dist/index.mjs.map +1 -1
  40. package/realtime-client/dist/index.d.mts +21 -0
  41. package/realtime-client/dist/index.d.ts +21 -0
  42. package/realtime-client/dist/index.js +173 -0
  43. package/realtime-client/dist/index.js.map +1 -0
  44. package/realtime-client/dist/index.mjs +144 -0
  45. package/realtime-client/dist/index.mjs.map +1 -0
  46. package/realtime-client/package.json +15 -0
  47. package/realtime-client/src/index.ts +7 -0
  48. package/realtime-client/src/realtime-state.ts +10 -0
  49. package/realtime-client/src/use-pull-family-member.ts +26 -0
  50. package/realtime-client/src/use-pull-mutable-family-member.ts +38 -0
  51. package/realtime-client/src/use-pull-mutable.ts +32 -0
  52. package/realtime-client/src/use-pull.ts +19 -0
  53. package/realtime-client/src/use-push.ts +25 -0
  54. package/realtime-client/src/use-server-action.ts +49 -0
  55. package/realtime-server/dist/index.d.mts +25 -0
  56. package/realtime-server/dist/index.d.ts +25 -0
  57. package/realtime-server/dist/index.js +316 -0
  58. package/realtime-server/dist/index.js.map +1 -0
  59. package/realtime-server/dist/index.mjs +289 -0
  60. package/realtime-server/dist/index.mjs.map +1 -0
  61. package/realtime-server/package.json +15 -0
  62. package/realtime-server/src/README.md +33 -0
  63. package/realtime-server/src/hook-composition/expose-family.ts +105 -0
  64. package/realtime-server/src/hook-composition/expose-mutable-family.ts +127 -0
  65. package/realtime-server/src/hook-composition/expose-mutable.ts +45 -0
  66. package/realtime-server/src/hook-composition/expose-single.ts +39 -0
  67. package/realtime-server/src/hook-composition/index.ts +14 -0
  68. package/realtime-server/src/hook-composition/receive-state.ts +30 -0
  69. package/realtime-server/src/hook-composition/receive-transaction.ts +37 -0
  70. package/realtime-server/src/index.ts +1 -0
  71. package/src/get-set.ts +48 -0
  72. package/src/index.ts +3 -60
  73. package/src/subscribe.ts +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hook-composition/expose-single.ts","../src/hook-composition/expose-family.ts","../src/hook-composition/expose-mutable.ts","../src/hook-composition/expose-mutable-family.ts","../src/hook-composition/receive-transaction.ts","../src/hook-composition/receive-state.ts"],"names":["AtomIO","getJsonToken","getUpdateToken","parseJson","_a","update"],"mappings":"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","sourcesContent":["import * as AtomIO from \"atom.io\"\nimport type { Json } from \"atom.io/json\"\n\nimport type { ServerConfig } from \"..\"\n\n/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\nexport const useExposeSingle = ({ socket, store }: ServerConfig) => {\n\treturn function exposeSingle<J extends Json.Serializable>(\n\t\ttoken: AtomIO.StateToken<J>,\n\t): () => void {\n\t\tlet unsubscribeFromStateUpdates: (() => void) | null = null\n\n\t\tconst fillUnsubRequest = () => {\n\t\t\tsocket.off(`unsub:${token.key}`, fillUnsubRequest)\n\t\t\tunsubscribeFromStateUpdates?.()\n\t\t\tunsubscribeFromStateUpdates = null\n\t\t}\n\n\t\tconst fillSubRequest = () => {\n\t\t\tsocket.emit(`serve:${token.key}`, AtomIO.getState(token, store))\n\t\t\tunsubscribeFromStateUpdates = AtomIO.subscribe(\n\t\t\t\ttoken,\n\t\t\t\t({ newValue }) => {\n\t\t\t\t\tsocket.emit(`serve:${token.key}`, newValue)\n\t\t\t\t},\n\t\t\t\t`expose-single:${socket.id}`,\n\t\t\t\tstore,\n\t\t\t)\n\t\t\tsocket.on(`unsub:${token.key}`, fillUnsubRequest)\n\t\t}\n\n\t\tsocket.on(`sub:${token.key}`, fillSubRequest)\n\n\t\treturn () => {\n\t\t\tsocket.off(`sub:${token.key}`, fillSubRequest)\n\t\t\tunsubscribeFromStateUpdates?.()\n\t\t}\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport type { Json } from \"atom.io/json\"\nimport { parseJson } from \"atom.io/json\"\n\nimport type { ServerConfig } from \"..\"\n\nconst subscribeToTokenCreation = <T>(\n\tfamily: AtomIO.AtomFamily<T> | AtomIO.SelectorFamily<T>,\n\tkey: string,\n\thandleTokenCreation: (token: AtomIO.StateToken<T>) => void,\n): (() => void) => {\n\tconst unsubscribe =\n\t\tfamily.type === `atom_family`\n\t\t\t? family.subject.subscribe(key, handleTokenCreation)\n\t\t\t: family.subject.subscribe(key, handleTokenCreation)\n\treturn unsubscribe\n}\n\n/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\nexport const useExposeFamily = ({ socket, store }: ServerConfig) => {\n\treturn function exposeFamily<J extends Json.Serializable>(\n\t\tfamily: AtomIO.AtomFamily<J> | AtomIO.SelectorFamily<J>,\n\t\tindex: AtomIO.StateToken<Set<string>>,\n\t): () => void {\n\t\tconst unsubSingleCallbacksByKey = new Map<string, () => void>()\n\t\tconst unsubFamilyCallbacksByKey = new Map<string, () => void>()\n\n\t\tconst fillFamilyUnsubRequest = () => {\n\t\t\tunsubFamilyCallbacksByKey.forEach((unsub) => unsub())\n\t\t\tunsubFamilyCallbacksByKey.clear()\n\t\t\tsocket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)\n\t\t}\n\n\t\tconst fillSingleUnsubRequest = (key: string) => {\n\t\t\tsocket.off(`unsub:${key}`, fillSingleUnsubRequest)\n\t\t\tconst unsub = unsubSingleCallbacksByKey.get(key)\n\t\t\tif (unsub) {\n\t\t\t\tunsub()\n\t\t\t\tunsubSingleCallbacksByKey.delete(key)\n\t\t\t}\n\t\t}\n\n\t\tconst fillSubRequest = (subKey?: Json.Serializable) => {\n\t\t\tif (subKey === undefined) {\n\t\t\t\tconst keys = AtomIO.getState(index, store)\n\t\t\t\tkeys.forEach((key) => {\n\t\t\t\t\tconst token = family(key)\n\t\t\t\t\tsocket.emit(\n\t\t\t\t\t\t`serve:${family.key}`,\n\t\t\t\t\t\tparseJson(token.family?.subKey || `null`),\n\t\t\t\t\t\tAtomIO.getState(token, store),\n\t\t\t\t\t)\n\t\t\t\t})\n\n\t\t\t\tconst unsubscribeFromTokenCreation = subscribeToTokenCreation(\n\t\t\t\t\tfamily,\n\t\t\t\t\t`expose-family:${socket.id}`,\n\t\t\t\t\t(token) => {\n\t\t\t\t\t\tconst unsub = AtomIO.subscribe(\n\t\t\t\t\t\t\ttoken,\n\t\t\t\t\t\t\t({ newValue }) => {\n\t\t\t\t\t\t\t\tsocket.emit(\n\t\t\t\t\t\t\t\t\t`serve:${family.key}`,\n\t\t\t\t\t\t\t\t\tparseJson(token.family?.subKey || `null`),\n\t\t\t\t\t\t\t\t\tnewValue,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t`expose-family:${family.key}:${socket.id}`,\n\t\t\t\t\t\t\tstore,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tunsubFamilyCallbacksByKey.set(token.key, unsub)\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tunsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)\n\n\t\t\t\tsocket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)\n\t\t\t} else {\n\t\t\t\tconst token = family(subKey)\n\t\t\t\tsocket.emit(`serve:${token.key}`, AtomIO.getState(token, store))\n\t\t\t\tconst unsubscribe = AtomIO.subscribe(\n\t\t\t\t\ttoken,\n\t\t\t\t\t({ newValue }) => {\n\t\t\t\t\t\tsocket.emit(`serve:${token.key}`, newValue)\n\t\t\t\t\t},\n\t\t\t\t\t`expose-family:${family.key}:${socket.id}`,\n\t\t\t\t\tstore,\n\t\t\t\t)\n\t\t\t\tunsubSingleCallbacksByKey.set(token.key, unsubscribe)\n\t\t\t\tsocket.on(`unsub:${token.key}`, () => {\n\t\t\t\t\tfillSingleUnsubRequest(token.key)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tsocket.on(`sub:${family.key}`, fillSubRequest)\n\n\t\treturn () => {\n\t\t\tsocket.off(`sub:${family.key}`, fillSubRequest)\n\t\t\tunsubFamilyCallbacksByKey.forEach((unsub) => unsub())\n\t\t\tunsubSingleCallbacksByKey.forEach((unsub) => unsub())\n\t\t\tunsubFamilyCallbacksByKey.clear()\n\t\t\tunsubSingleCallbacksByKey.clear()\n\t\t}\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport { getJsonToken, getUpdateToken } from \"atom.io/internal\"\nimport type { Transceiver } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\n\nimport type { ServerConfig } from \"..\"\n\n/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\nexport const useExposeMutable = ({ socket, store }: ServerConfig) => {\n\treturn function exposeMutable<\n\t\tCore extends Transceiver<Json.Serializable>,\n\t\tSerializableCore extends Json.Serializable,\n\t>(token: AtomIO.MutableAtomToken<Core, SerializableCore>): () => void {\n\t\tlet unsubscribeFromStateUpdates: (() => void) | null = null\n\n\t\tconst jsonToken = getJsonToken(token)\n\t\tconst trackerToken = getUpdateToken(token)\n\n\t\tconst fillUnsubRequest = () => {\n\t\t\tsocket.off(`unsub:${token.key}`, fillUnsubRequest)\n\t\t\tunsubscribeFromStateUpdates?.()\n\t\t\tunsubscribeFromStateUpdates = null\n\t\t}\n\n\t\tconst fillSubRequest = () => {\n\t\t\tsocket.emit(`init:${token.key}`, AtomIO.getState(jsonToken, store))\n\t\t\tunsubscribeFromStateUpdates = AtomIO.subscribe(\n\t\t\t\ttrackerToken,\n\t\t\t\t({ newValue }) => {\n\t\t\t\t\tsocket.emit(`next:${token.key}`, newValue)\n\t\t\t\t},\n\t\t\t\t`expose-single:${socket.id}`,\n\t\t\t\tstore,\n\t\t\t)\n\t\t\tsocket.on(`unsub:${token.key}`, fillUnsubRequest)\n\t\t}\n\n\t\tsocket.on(`sub:${token.key}`, fillSubRequest)\n\n\t\treturn () => {\n\t\t\tsocket.off(`sub:${token.key}`, fillSubRequest)\n\t\t\tunsubscribeFromStateUpdates?.()\n\t\t}\n\t}\n}\n","import * as AtomIO from \"atom.io\"\nimport type { Transceiver } from \"atom.io/internal\"\nimport { getJsonToken, getUpdateToken } from \"atom.io/internal\"\nimport type { Json } from \"atom.io/json\"\nimport { parseJson } from \"atom.io/json\"\n\nimport type { ServerConfig } from \"..\"\n\nexport const useExposeMutableFamily = ({ socket, store }: ServerConfig) => {\n\treturn function exposeMutableFamily<\n\t\tFamily extends AtomIO.MutableAtomFamily<\n\t\t\tTransceiver<Json.Serializable>,\n\t\t\tJson.Serializable,\n\t\t\tJson.Serializable\n\t\t>,\n\t>(family: Family, index: AtomIO.StateToken<Set<string>>): () => void {\n\t\ttype FamilyKey = Family extends AtomIO.MutableAtomFamily<\n\t\t\tTransceiver<any>,\n\t\t\tany,\n\t\t\tinfer Key\n\t\t>\n\t\t\t? Key\n\t\t\t: never\n\n\t\tconst unsubSingleCallbacksByKey = new Map<string, () => void>()\n\t\tconst unsubFamilyCallbacksByKey = new Map<string, () => void>()\n\n\t\tconst fillFamilyUnsubRequest = () => {\n\t\t\tunsubFamilyCallbacksByKey.forEach((unsub) => unsub())\n\t\t\tunsubFamilyCallbacksByKey.clear()\n\t\t\tsocket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)\n\t\t}\n\n\t\tconst fillSingleUnsubRequest = (key: string) => {\n\t\t\tsocket.off(`unsub:${key}`, fillSingleUnsubRequest)\n\t\t\tconst unsub = unsubSingleCallbacksByKey.get(key)\n\t\t\tif (unsub) {\n\t\t\t\tunsub()\n\t\t\t\tunsubSingleCallbacksByKey.delete(key)\n\t\t\t}\n\t\t}\n\n\t\tconst fillSubRequest = (subKey?: FamilyKey) => {\n\t\t\tif (subKey === undefined) {\n\t\t\t\tconst keys = AtomIO.getState(index, store)\n\t\t\t\tkeys.forEach((key) => {\n\t\t\t\t\tconst token = family(key)\n\t\t\t\t\tconst jsonToken = getJsonToken(token)\n\t\t\t\t\tconst trackerToken = getUpdateToken(token)\n\t\t\t\t\tsocket.emit(\n\t\t\t\t\t\t`init:${family.key}`,\n\t\t\t\t\t\tparseJson(jsonToken.family?.subKey || `null`),\n\t\t\t\t\t\tAtomIO.getState(jsonToken, store),\n\t\t\t\t\t)\n\t\t\t\t\tconst unsubFromUpdates = AtomIO.subscribe(\n\t\t\t\t\t\ttrackerToken,\n\t\t\t\t\t\t({ newValue }) => {\n\t\t\t\t\t\t\tsocket.emit(\n\t\t\t\t\t\t\t\t`next:${token.key}`,\n\t\t\t\t\t\t\t\tparseJson(jsonToken.family?.subKey || `null`),\n\t\t\t\t\t\t\t\tnewValue,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t},\n\t\t\t\t\t\t`expose-family:${family.key}:${socket.id}`,\n\t\t\t\t\t\tstore,\n\t\t\t\t\t)\n\t\t\t\t\tunsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)\n\t\t\t\t})\n\t\t\t\tconst unsubscribeFromTokenCreation = family.subject.subscribe(\n\t\t\t\t\t`expose-family:${socket.id}`,\n\t\t\t\t\t(token) => {\n\t\t\t\t\t\tconst jsonToken = getJsonToken(token)\n\t\t\t\t\t\tconst trackerToken = getUpdateToken(token)\n\t\t\t\t\t\tsocket.emit(\n\t\t\t\t\t\t\t`init:${family.key}`,\n\t\t\t\t\t\t\tparseJson(jsonToken.family?.subKey || `null`),\n\t\t\t\t\t\t\tAtomIO.getState(jsonToken, store),\n\t\t\t\t\t\t)\n\t\t\t\t\t\tconst unsubFromUpdates = AtomIO.subscribe(\n\t\t\t\t\t\t\ttrackerToken,\n\t\t\t\t\t\t\t({ newValue }) => {\n\t\t\t\t\t\t\t\tsocket.emit(\n\t\t\t\t\t\t\t\t\t`next:${token.key}`,\n\t\t\t\t\t\t\t\t\tparseJson(jsonToken.family?.subKey || `null`),\n\t\t\t\t\t\t\t\t\tnewValue,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t`expose-family:${family.key}:${socket.id}`,\n\t\t\t\t\t\t\tstore,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tunsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tunsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)\n\n\t\t\t\tsocket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)\n\t\t\t} else {\n\t\t\t\tconst token = family(subKey)\n\t\t\t\tconst jsonToken = getJsonToken(token)\n\t\t\t\tconst updateToken = getUpdateToken(token)\n\t\t\t\tsocket.emit(`init:${token.key}`, AtomIO.getState(jsonToken, store))\n\t\t\t\tconst unsubscribe = AtomIO.subscribe(\n\t\t\t\t\tupdateToken,\n\t\t\t\t\t({ newValue }) => {\n\t\t\t\t\t\tsocket.emit(`next:${token.key}`, newValue)\n\t\t\t\t\t},\n\t\t\t\t\t`expose-family:${family.key}:${socket.id}`,\n\t\t\t\t\tstore,\n\t\t\t\t)\n\t\t\t\tunsubSingleCallbacksByKey.set(token.key, unsubscribe)\n\t\t\t\tsocket.on(`unsub:${token.key}`, () => {\n\t\t\t\t\tfillSingleUnsubRequest(token.key)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tsocket.on(`sub:${family.key}`, fillSubRequest)\n\n\t\treturn () => {\n\t\t\tsocket.off(`sub:${family.key}`, fillSubRequest)\n\t\t\tunsubFamilyCallbacksByKey.forEach((unsub) => unsub())\n\t\t\tunsubSingleCallbacksByKey.forEach((unsub) => unsub())\n\t\t\tunsubFamilyCallbacksByKey.clear()\n\t\t\tunsubSingleCallbacksByKey.clear()\n\t\t}\n\t}\n}\n","import * as AtomIO from \"atom.io\"\n\nimport type { ServerConfig } from \".\"\n\nexport const useReceiveTransaction = ({ socket, store }: ServerConfig) => {\n\treturn function receiveTransaction<ƒ extends AtomIO.ƒn>(\n\t\ttx: AtomIO.TransactionToken<ƒ>,\n\t): () => void {\n\t\tconst fillTransactionRequest = (update: AtomIO.TransactionUpdate<ƒ>) =>\n\t\t\tAtomIO.runTransaction<ƒ>(tx, store)(...update.params)\n\n\t\tsocket.on(`tx:${tx.key}`, fillTransactionRequest)\n\n\t\treturn () => socket.off(`tx:${tx.key}`, fillTransactionRequest)\n\t}\n}\n\nexport function useSyncTransaction({ socket, store }: ServerConfig) {\n\treturn function receiveTransaction<ƒ extends AtomIO.ƒn>(\n\t\ttx: AtomIO.TransactionToken<ƒ>,\n\t): () => void {\n\t\tconst fillTransactionRequest = (\n\t\t\tupdate: AtomIO.TransactionUpdate<ƒ>,\n\t\t\ttransactionId: string,\n\t\t) => {\n\t\t\tconst unsubscribe = AtomIO.subscribeToTransaction(tx, (update) => {\n\t\t\t\tunsubscribe()\n\t\t\t\tsocket.emit(`tx:sync:${transactionId}`, update)\n\t\t\t})\n\t\t\tAtomIO.runTransaction<ƒ>(tx, store)(...update.params)\n\t\t}\n\n\t\tsocket.on(`tx:${tx.key}`, fillTransactionRequest)\n\n\t\treturn () => socket.off(`tx:${tx.key}`, fillTransactionRequest)\n\t}\n}\n","import { setState } from \"atom.io\"\nimport type { StateToken } from \"atom.io\"\nimport type { Json } from \"atom.io/json\"\n\nimport type { ServerConfig } from \"..\"\n\n/* eslint-disable @typescript-eslint/explicit-module-boundary-types */\nexport const useReceiveState = ({ socket, store }: ServerConfig) => {\n\treturn function receiveState<J extends Json.Serializable>(\n\t\ttoken: StateToken<J>,\n\t): () => void {\n\t\tconst publish = (newValue: J) => setState(token, newValue, store)\n\n\t\tconst fillPubUnclaim = () => {\n\t\t\tsocket.off(`pub:${token.key}`, publish)\n\t\t\tsocket.off(`unclaim:${token.key}`, fillPubUnclaim)\n\t\t}\n\t\tconst fillPubClaim = () => {\n\t\t\tsocket.on(`pub:${token.key}`, publish)\n\t\t\tsocket.on(`unclaim:${token.key}`, fillPubUnclaim)\n\t\t}\n\n\t\tsocket.on(`claim:${token.key}`, fillPubClaim)\n\n\t\treturn () => {\n\t\t\tsocket.off(`claim:${token.key}`, fillPubClaim)\n\t\t\tsocket.off(`pub:${token.key}`, publish)\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "atom.io-react",
3
+ "private": true,
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "module": "dist/index.mjs",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "browser": "./dist/index.mjs",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,33 @@
1
+ # CLIENT ACTS AND REPORTS
2
+ - [x] input event fires
3
+ - [x] event handler runs transaction
4
+ - [x] client store updates optimistically
5
+ - [ ] on success
6
+ - [ ] client generates transactionId and optimistic TransactionUpdate
7
+ - [ ] client pushes TransactionUpdate to TimelineData.history
8
+ - [ ] client sets TransactionUpdate in optimisticTransactions map by transactionId
9
+ - [ ] client emits TransactionRequest { key, params, transactionId }
10
+
11
+ # SERVER VALIDATES, INTEGRATES, AND BROADCASTS
12
+ ## use
13
+ - [x] server receives TransactionRequest
14
+ - `{ key, params, transactionId }`
15
+ - [ ] verify `transactionId` is unique
16
+ - [ ] server adds timestamp to `TransactionRequest`
17
+ - `{ key, params, transactionId, timestamp }`
18
+ - [ ] server runs transaction, computing `TransactionUpdate` in the process
19
+ - [ ] emit `TransactionUpdate`
20
+ - `{ key, params, transactionId, timestamp, atomUpdates, output }`
21
+ - [ ] server adds `TransactionUpdate` to TimelineData.history
22
+
23
+ # CLIENT BEHOLDS AND REACTS
24
+ - [ ] client receives official TransactionUpdate
25
+ - [ ] client retrieves its own TransactionUpdate from optimisticTransactions map
26
+ - [ ] client compares official and optimistic TransactionUpdates
27
+ - [ ] (stringify atomUpdates and compare strict)
28
+ - [ ] if match, client removes TransactionUpdate from optimisticTransactions map
29
+ - [ ] if mismatch
30
+ - [ ] client undoes timeline until it finds its own TransactionUpdate
31
+ - [ ] client replaces its own TransactionUpdate with official TransactionUpdate
32
+ - [ ] client removes its own TransactionUpdate from optimisticTransactions map
33
+ - [ ] client redoes timeline until it reaches the "HEAD"
@@ -0,0 +1,105 @@
1
+ import * as AtomIO from "atom.io"
2
+ import type { Json } from "atom.io/json"
3
+ import { parseJson } from "atom.io/json"
4
+
5
+ import type { ServerConfig } from ".."
6
+
7
+ const subscribeToTokenCreation = <T>(
8
+ family: AtomIO.AtomFamily<T> | AtomIO.SelectorFamily<T>,
9
+ key: string,
10
+ handleTokenCreation: (token: AtomIO.StateToken<T>) => void,
11
+ ): (() => void) => {
12
+ const unsubscribe =
13
+ family.type === `atom_family`
14
+ ? family.subject.subscribe(key, handleTokenCreation)
15
+ : family.subject.subscribe(key, handleTokenCreation)
16
+ return unsubscribe
17
+ }
18
+
19
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
20
+ export const useExposeFamily = ({ socket, store }: ServerConfig) => {
21
+ return function exposeFamily<J extends Json.Serializable>(
22
+ family: AtomIO.AtomFamily<J> | AtomIO.SelectorFamily<J>,
23
+ index: AtomIO.StateToken<Set<string>>,
24
+ ): () => void {
25
+ const unsubSingleCallbacksByKey = new Map<string, () => void>()
26
+ const unsubFamilyCallbacksByKey = new Map<string, () => void>()
27
+
28
+ const fillFamilyUnsubRequest = () => {
29
+ unsubFamilyCallbacksByKey.forEach((unsub) => unsub())
30
+ unsubFamilyCallbacksByKey.clear()
31
+ socket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)
32
+ }
33
+
34
+ const fillSingleUnsubRequest = (key: string) => {
35
+ socket.off(`unsub:${key}`, fillSingleUnsubRequest)
36
+ const unsub = unsubSingleCallbacksByKey.get(key)
37
+ if (unsub) {
38
+ unsub()
39
+ unsubSingleCallbacksByKey.delete(key)
40
+ }
41
+ }
42
+
43
+ const fillSubRequest = (subKey?: Json.Serializable) => {
44
+ if (subKey === undefined) {
45
+ const keys = AtomIO.getState(index, store)
46
+ keys.forEach((key) => {
47
+ const token = family(key)
48
+ socket.emit(
49
+ `serve:${family.key}`,
50
+ parseJson(token.family?.subKey || `null`),
51
+ AtomIO.getState(token, store),
52
+ )
53
+ })
54
+
55
+ const unsubscribeFromTokenCreation = subscribeToTokenCreation(
56
+ family,
57
+ `expose-family:${socket.id}`,
58
+ (token) => {
59
+ const unsub = AtomIO.subscribe(
60
+ token,
61
+ ({ newValue }) => {
62
+ socket.emit(
63
+ `serve:${family.key}`,
64
+ parseJson(token.family?.subKey || `null`),
65
+ newValue,
66
+ )
67
+ },
68
+ `expose-family:${family.key}:${socket.id}`,
69
+ store,
70
+ )
71
+ unsubFamilyCallbacksByKey.set(token.key, unsub)
72
+ },
73
+ )
74
+ unsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)
75
+
76
+ socket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)
77
+ } else {
78
+ const token = family(subKey)
79
+ socket.emit(`serve:${token.key}`, AtomIO.getState(token, store))
80
+ const unsubscribe = AtomIO.subscribe(
81
+ token,
82
+ ({ newValue }) => {
83
+ socket.emit(`serve:${token.key}`, newValue)
84
+ },
85
+ `expose-family:${family.key}:${socket.id}`,
86
+ store,
87
+ )
88
+ unsubSingleCallbacksByKey.set(token.key, unsubscribe)
89
+ socket.on(`unsub:${token.key}`, () => {
90
+ fillSingleUnsubRequest(token.key)
91
+ })
92
+ }
93
+ }
94
+
95
+ socket.on(`sub:${family.key}`, fillSubRequest)
96
+
97
+ return () => {
98
+ socket.off(`sub:${family.key}`, fillSubRequest)
99
+ unsubFamilyCallbacksByKey.forEach((unsub) => unsub())
100
+ unsubSingleCallbacksByKey.forEach((unsub) => unsub())
101
+ unsubFamilyCallbacksByKey.clear()
102
+ unsubSingleCallbacksByKey.clear()
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,127 @@
1
+ import * as AtomIO from "atom.io"
2
+ import type { Transceiver } from "atom.io/internal"
3
+ import { getJsonToken, getUpdateToken } from "atom.io/internal"
4
+ import type { Json } from "atom.io/json"
5
+ import { parseJson } from "atom.io/json"
6
+
7
+ import type { ServerConfig } from ".."
8
+
9
+ export const useExposeMutableFamily = ({ socket, store }: ServerConfig) => {
10
+ return function exposeMutableFamily<
11
+ Family extends AtomIO.MutableAtomFamily<
12
+ Transceiver<Json.Serializable>,
13
+ Json.Serializable,
14
+ Json.Serializable
15
+ >,
16
+ >(family: Family, index: AtomIO.StateToken<Set<string>>): () => void {
17
+ type FamilyKey = Family extends AtomIO.MutableAtomFamily<
18
+ Transceiver<any>,
19
+ any,
20
+ infer Key
21
+ >
22
+ ? Key
23
+ : never
24
+
25
+ const unsubSingleCallbacksByKey = new Map<string, () => void>()
26
+ const unsubFamilyCallbacksByKey = new Map<string, () => void>()
27
+
28
+ const fillFamilyUnsubRequest = () => {
29
+ unsubFamilyCallbacksByKey.forEach((unsub) => unsub())
30
+ unsubFamilyCallbacksByKey.clear()
31
+ socket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)
32
+ }
33
+
34
+ const fillSingleUnsubRequest = (key: string) => {
35
+ socket.off(`unsub:${key}`, fillSingleUnsubRequest)
36
+ const unsub = unsubSingleCallbacksByKey.get(key)
37
+ if (unsub) {
38
+ unsub()
39
+ unsubSingleCallbacksByKey.delete(key)
40
+ }
41
+ }
42
+
43
+ const fillSubRequest = (subKey?: FamilyKey) => {
44
+ if (subKey === undefined) {
45
+ const keys = AtomIO.getState(index, store)
46
+ keys.forEach((key) => {
47
+ const token = family(key)
48
+ const jsonToken = getJsonToken(token)
49
+ const trackerToken = getUpdateToken(token)
50
+ socket.emit(
51
+ `init:${family.key}`,
52
+ parseJson(jsonToken.family?.subKey || `null`),
53
+ AtomIO.getState(jsonToken, store),
54
+ )
55
+ const unsubFromUpdates = AtomIO.subscribe(
56
+ trackerToken,
57
+ ({ newValue }) => {
58
+ socket.emit(
59
+ `next:${token.key}`,
60
+ parseJson(jsonToken.family?.subKey || `null`),
61
+ newValue,
62
+ )
63
+ },
64
+ `expose-family:${family.key}:${socket.id}`,
65
+ store,
66
+ )
67
+ unsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)
68
+ })
69
+ const unsubscribeFromTokenCreation = family.subject.subscribe(
70
+ `expose-family:${socket.id}`,
71
+ (token) => {
72
+ const jsonToken = getJsonToken(token)
73
+ const trackerToken = getUpdateToken(token)
74
+ socket.emit(
75
+ `init:${family.key}`,
76
+ parseJson(jsonToken.family?.subKey || `null`),
77
+ AtomIO.getState(jsonToken, store),
78
+ )
79
+ const unsubFromUpdates = AtomIO.subscribe(
80
+ trackerToken,
81
+ ({ newValue }) => {
82
+ socket.emit(
83
+ `next:${token.key}`,
84
+ parseJson(jsonToken.family?.subKey || `null`),
85
+ newValue,
86
+ )
87
+ },
88
+ `expose-family:${family.key}:${socket.id}`,
89
+ store,
90
+ )
91
+ unsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)
92
+ },
93
+ )
94
+ unsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)
95
+
96
+ socket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)
97
+ } else {
98
+ const token = family(subKey)
99
+ const jsonToken = getJsonToken(token)
100
+ const updateToken = getUpdateToken(token)
101
+ socket.emit(`init:${token.key}`, AtomIO.getState(jsonToken, store))
102
+ const unsubscribe = AtomIO.subscribe(
103
+ updateToken,
104
+ ({ newValue }) => {
105
+ socket.emit(`next:${token.key}`, newValue)
106
+ },
107
+ `expose-family:${family.key}:${socket.id}`,
108
+ store,
109
+ )
110
+ unsubSingleCallbacksByKey.set(token.key, unsubscribe)
111
+ socket.on(`unsub:${token.key}`, () => {
112
+ fillSingleUnsubRequest(token.key)
113
+ })
114
+ }
115
+ }
116
+
117
+ socket.on(`sub:${family.key}`, fillSubRequest)
118
+
119
+ return () => {
120
+ socket.off(`sub:${family.key}`, fillSubRequest)
121
+ unsubFamilyCallbacksByKey.forEach((unsub) => unsub())
122
+ unsubSingleCallbacksByKey.forEach((unsub) => unsub())
123
+ unsubFamilyCallbacksByKey.clear()
124
+ unsubSingleCallbacksByKey.clear()
125
+ }
126
+ }
127
+ }
@@ -0,0 +1,45 @@
1
+ import * as AtomIO from "atom.io"
2
+ import { getJsonToken, getUpdateToken } from "atom.io/internal"
3
+ import type { Transceiver } from "atom.io/internal"
4
+ import type { Json } from "atom.io/json"
5
+
6
+ import type { ServerConfig } from ".."
7
+
8
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
9
+ export const useExposeMutable = ({ socket, store }: ServerConfig) => {
10
+ return function exposeMutable<
11
+ Core extends Transceiver<Json.Serializable>,
12
+ SerializableCore extends Json.Serializable,
13
+ >(token: AtomIO.MutableAtomToken<Core, SerializableCore>): () => void {
14
+ let unsubscribeFromStateUpdates: (() => void) | null = null
15
+
16
+ const jsonToken = getJsonToken(token)
17
+ const trackerToken = getUpdateToken(token)
18
+
19
+ const fillUnsubRequest = () => {
20
+ socket.off(`unsub:${token.key}`, fillUnsubRequest)
21
+ unsubscribeFromStateUpdates?.()
22
+ unsubscribeFromStateUpdates = null
23
+ }
24
+
25
+ const fillSubRequest = () => {
26
+ socket.emit(`init:${token.key}`, AtomIO.getState(jsonToken, store))
27
+ unsubscribeFromStateUpdates = AtomIO.subscribe(
28
+ trackerToken,
29
+ ({ newValue }) => {
30
+ socket.emit(`next:${token.key}`, newValue)
31
+ },
32
+ `expose-single:${socket.id}`,
33
+ store,
34
+ )
35
+ socket.on(`unsub:${token.key}`, fillUnsubRequest)
36
+ }
37
+
38
+ socket.on(`sub:${token.key}`, fillSubRequest)
39
+
40
+ return () => {
41
+ socket.off(`sub:${token.key}`, fillSubRequest)
42
+ unsubscribeFromStateUpdates?.()
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,39 @@
1
+ import * as AtomIO from "atom.io"
2
+ import type { Json } from "atom.io/json"
3
+
4
+ import type { ServerConfig } from ".."
5
+
6
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
7
+ export const useExposeSingle = ({ socket, store }: ServerConfig) => {
8
+ return function exposeSingle<J extends Json.Serializable>(
9
+ token: AtomIO.StateToken<J>,
10
+ ): () => void {
11
+ let unsubscribeFromStateUpdates: (() => void) | null = null
12
+
13
+ const fillUnsubRequest = () => {
14
+ socket.off(`unsub:${token.key}`, fillUnsubRequest)
15
+ unsubscribeFromStateUpdates?.()
16
+ unsubscribeFromStateUpdates = null
17
+ }
18
+
19
+ const fillSubRequest = () => {
20
+ socket.emit(`serve:${token.key}`, AtomIO.getState(token, store))
21
+ unsubscribeFromStateUpdates = AtomIO.subscribe(
22
+ token,
23
+ ({ newValue }) => {
24
+ socket.emit(`serve:${token.key}`, newValue)
25
+ },
26
+ `expose-single:${socket.id}`,
27
+ store,
28
+ )
29
+ socket.on(`unsub:${token.key}`, fillUnsubRequest)
30
+ }
31
+
32
+ socket.on(`sub:${token.key}`, fillSubRequest)
33
+
34
+ return () => {
35
+ socket.off(`sub:${token.key}`, fillSubRequest)
36
+ unsubscribeFromStateUpdates?.()
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,14 @@
1
+ import type { Store } from "atom.io/internal"
2
+ import type * as SocketIO from "socket.io"
3
+
4
+ export * from "./expose-single"
5
+ export * from "./expose-family"
6
+ export * from "./expose-mutable"
7
+ export * from "./expose-mutable-family"
8
+ export * from "./receive-transaction"
9
+ export * from "./receive-state"
10
+
11
+ export type ServerConfig = {
12
+ socket: SocketIO.Socket
13
+ store?: Store
14
+ }
@@ -0,0 +1,30 @@
1
+ import { setState } from "atom.io"
2
+ import type { StateToken } from "atom.io"
3
+ import type { Json } from "atom.io/json"
4
+
5
+ import type { ServerConfig } from ".."
6
+
7
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
8
+ export const useReceiveState = ({ socket, store }: ServerConfig) => {
9
+ return function receiveState<J extends Json.Serializable>(
10
+ token: StateToken<J>,
11
+ ): () => void {
12
+ const publish = (newValue: J) => setState(token, newValue, store)
13
+
14
+ const fillPubUnclaim = () => {
15
+ socket.off(`pub:${token.key}`, publish)
16
+ socket.off(`unclaim:${token.key}`, fillPubUnclaim)
17
+ }
18
+ const fillPubClaim = () => {
19
+ socket.on(`pub:${token.key}`, publish)
20
+ socket.on(`unclaim:${token.key}`, fillPubUnclaim)
21
+ }
22
+
23
+ socket.on(`claim:${token.key}`, fillPubClaim)
24
+
25
+ return () => {
26
+ socket.off(`claim:${token.key}`, fillPubClaim)
27
+ socket.off(`pub:${token.key}`, publish)
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,37 @@
1
+ import * as AtomIO from "atom.io"
2
+
3
+ import type { ServerConfig } from "."
4
+
5
+ export const useReceiveTransaction = ({ socket, store }: ServerConfig) => {
6
+ return function receiveTransaction<ƒ extends AtomIO.ƒn>(
7
+ tx: AtomIO.TransactionToken<ƒ>,
8
+ ): () => void {
9
+ const fillTransactionRequest = (update: AtomIO.TransactionUpdate<ƒ>) =>
10
+ AtomIO.runTransaction<ƒ>(tx, store)(...update.params)
11
+
12
+ socket.on(`tx:${tx.key}`, fillTransactionRequest)
13
+
14
+ return () => socket.off(`tx:${tx.key}`, fillTransactionRequest)
15
+ }
16
+ }
17
+
18
+ export function useSyncTransaction({ socket, store }: ServerConfig) {
19
+ return function receiveTransaction<ƒ extends AtomIO.ƒn>(
20
+ tx: AtomIO.TransactionToken<ƒ>,
21
+ ): () => void {
22
+ const fillTransactionRequest = (
23
+ update: AtomIO.TransactionUpdate<ƒ>,
24
+ transactionId: string,
25
+ ) => {
26
+ const unsubscribe = AtomIO.subscribeToTransaction(tx, (update) => {
27
+ unsubscribe()
28
+ socket.emit(`tx:sync:${transactionId}`, update)
29
+ })
30
+ AtomIO.runTransaction<ƒ>(tx, store)(...update.params)
31
+ }
32
+
33
+ socket.on(`tx:${tx.key}`, fillTransactionRequest)
34
+
35
+ return () => socket.off(`tx:${tx.key}`, fillTransactionRequest)
36
+ }
37
+ }
@@ -0,0 +1 @@
1
+ export * from "./hook-composition"
package/src/get-set.ts ADDED
@@ -0,0 +1,48 @@
1
+ import * as Internal from "atom.io/internal"
2
+ import type { ReadonlySelectorToken, StateToken } from "."
3
+
4
+ export const getState = <T>(
5
+ token: ReadonlySelectorToken<T> | StateToken<T>,
6
+ store: Internal.Store = Internal.IMPLICIT.STORE,
7
+ ): T => {
8
+ const state =
9
+ Internal.withdraw(token, store) ??
10
+ Internal.withdrawNewFamilyMember(token, store)
11
+ if (state === undefined) {
12
+ throw new NotFoundError(token, store)
13
+ }
14
+ return Internal.getState__INTERNAL(state, store)
15
+ }
16
+
17
+ export const setState = <T, New extends T>(
18
+ token: StateToken<T>,
19
+ value: New | ((oldValue: T) => New),
20
+ store: Internal.Store = Internal.IMPLICIT.STORE,
21
+ ): void => {
22
+ const rejection = Internal.openOperation(token, store)
23
+ if (rejection) {
24
+ return
25
+ }
26
+ const state =
27
+ Internal.withdraw(token, store) ??
28
+ Internal.withdrawNewFamilyMember(token, store)
29
+ if (state === undefined) {
30
+ throw new NotFoundError(token, store)
31
+ }
32
+ Internal.setState__INTERNAL(state, value, store)
33
+ Internal.closeOperation(store)
34
+ }
35
+
36
+ const capitalize = (str: string) => str[0].toUpperCase() + str.slice(1)
37
+ export class NotFoundError extends Error {
38
+ public constructor(
39
+ token: ReadonlySelectorToken<any> | StateToken<any>,
40
+ store: Internal.Store,
41
+ ) {
42
+ super(
43
+ `${capitalize(token.type)} "${token.key}" not found in store "${
44
+ store.config.name
45
+ }".`,
46
+ )
47
+ }
48
+ }
package/src/index.ts CHANGED
@@ -1,12 +1,11 @@
1
- import * as IO from "atom.io/internal"
2
- import type { Store, Transceiver } from "atom.io/internal"
1
+ import type { Transceiver } from "atom.io/internal"
3
2
  import type { Json } from "atom.io/json"
4
3
 
5
4
  export * from "./atom"
5
+ export * from "./get-set"
6
6
  export * from "./logger"
7
7
  export * from "./selector"
8
8
  export * from "./silo"
9
- export { subscribe } from "./subscribe"
10
9
  export * from "./subscribe"
11
10
  export * from "./timeline"
12
11
  export * from "./transaction"
@@ -41,60 +40,4 @@ export type ReadonlySelectorToken<_> = {
41
40
  __brand?: _
42
41
  }
43
42
 
44
- export type FamilyMetadata = {
45
- key: string
46
- subKey: string
47
- }
48
-
49
- export const capitalize = (str: string): string =>
50
- str[0].toUpperCase() + str.slice(1)
51
-
52
- export const getState = <T>(
53
- token: ReadonlySelectorToken<T> | StateToken<T>,
54
- store: Store = IO.IMPLICIT.STORE,
55
- ): T => {
56
- const state =
57
- IO.withdraw(token, store) ?? IO.withdrawNewFamilyMember(token, store)
58
- if (state === null) {
59
- throw new Error(
60
- `${capitalize(token.type)} "${token.key}" not found in store "${
61
- store.config.name
62
- }".`,
63
- )
64
- }
65
- return IO.getState__INTERNAL(state, store)
66
- }
67
-
68
- export const setState = <T, New extends T>(
69
- token: StateToken<T>,
70
- value: New | ((oldValue: T) => New),
71
- store: Store = IO.IMPLICIT.STORE,
72
- ): void => {
73
- try {
74
- IO.openOperation(token, store)
75
- } catch (thrown) {
76
- if (!(typeof thrown === `symbol`)) {
77
- throw thrown
78
- }
79
- return
80
- }
81
- const state =
82
- IO.withdraw(token, store) ?? IO.withdrawNewFamilyMember(token, store)
83
- if (state === null) {
84
- throw new Error(
85
- `${capitalize(token.type)} "${token.key}" not found in store "${
86
- store.config.name
87
- }".`,
88
- )
89
- }
90
- IO.setState__INTERNAL(state, value, store)
91
- IO.closeOperation(store)
92
- }
93
-
94
- export const isDefault = (
95
- token: ReadonlySelectorToken<unknown> | StateToken<unknown>,
96
- store: Store = IO.IMPLICIT.STORE,
97
- ): boolean =>
98
- token.type === `atom`
99
- ? IO.isAtomDefault(token.key, store)
100
- : IO.isSelectorDefault(token.key, store)
43
+ export type FamilyMetadata = { key: string; subKey: string }
package/src/subscribe.ts CHANGED
@@ -26,7 +26,7 @@ export function subscribe<T>(
26
26
  store: Store = IMPLICIT.STORE,
27
27
  ): () => void {
28
28
  const state = withdraw<T>(token, store)
29
- if (state === null) {
29
+ if (state === undefined) {
30
30
  throw new Error(
31
31
  `State "${token.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`,
32
32
  )
@@ -66,7 +66,7 @@ export const subscribeToTransaction = <ƒ extends ƒn>(
66
66
  store = IMPLICIT.STORE,
67
67
  ): (() => void) => {
68
68
  const tx = withdraw(token, store)
69
- if (tx === null) {
69
+ if (tx === undefined) {
70
70
  throw new Error(
71
71
  `Cannot subscribe to transaction "${token.key}": transaction not found in store "${store.config.name}".`,
72
72
  )
@@ -86,7 +86,7 @@ export const subscribeToTimeline = (
86
86
  store = IMPLICIT.STORE,
87
87
  ): (() => void) => {
88
88
  const tl = withdraw(token, store)
89
- if (tl === null) {
89
+ if (tl === undefined) {
90
90
  throw new Error(
91
91
  `Cannot subscribe to timeline "${token.key}": timeline not found in store "${store.config.name}".`,
92
92
  )