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.
- package/dist/index.d.mts +8 -5
- package/dist/index.d.ts +8 -5
- package/dist/index.js +67 -72
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +34 -38
- package/dist/index.mjs.map +1 -1
- package/internal/dist/index.d.mts +27 -33
- package/internal/dist/index.d.ts +27 -33
- package/internal/dist/index.js +45 -69
- package/internal/dist/index.js.map +1 -1
- package/internal/dist/index.mjs +34 -39
- package/internal/dist/index.mjs.map +1 -1
- package/internal/src/caching.ts +12 -5
- package/internal/src/future.ts +2 -4
- package/internal/src/mutable/create-mutable-atom-family.ts +4 -4
- package/internal/src/mutable/create-mutable-atom.ts +6 -5
- package/internal/src/mutable/is-atom-token-mutable.ts +3 -3
- package/internal/src/mutable/tracker-family.ts +4 -4
- package/internal/src/mutable/tracker.ts +20 -19
- package/internal/src/operation.ts +5 -2
- package/internal/src/selector/create-read-write-selector.ts +2 -2
- package/internal/src/selector/register-selector.ts +2 -2
- package/internal/src/set-state/{set-atom-state.ts → set-atom.ts} +1 -1
- package/internal/src/set-state/set-selector-state.ts +1 -12
- package/internal/src/set-state/set-state-internal.ts +4 -5
- package/internal/src/store/withdraw-new-family-member.ts +7 -7
- package/internal/src/store/withdraw.ts +15 -9
- package/internal/src/subscribe/subscribe-to-root-atoms.ts +1 -1
- package/internal/src/timeline/add-atom-to-timeline.ts +2 -2
- package/internal/src/transaction/apply-transaction.ts +1 -1
- package/internal/src/transaction/redo-transaction.ts +1 -1
- package/internal/src/transaction/undo-transaction.ts +1 -1
- package/package.json +11 -8
- package/react-devtools/dist/index.d.mts +4 -4
- package/react-devtools/dist/index.d.ts +4 -4
- package/react-devtools/dist/index.js +3 -3
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/dist/index.mjs +3 -3
- package/react-devtools/dist/index.mjs.map +1 -1
- package/realtime-client/dist/index.d.mts +21 -0
- package/realtime-client/dist/index.d.ts +21 -0
- package/realtime-client/dist/index.js +173 -0
- package/realtime-client/dist/index.js.map +1 -0
- package/realtime-client/dist/index.mjs +144 -0
- package/realtime-client/dist/index.mjs.map +1 -0
- package/realtime-client/package.json +15 -0
- package/realtime-client/src/index.ts +7 -0
- package/realtime-client/src/realtime-state.ts +10 -0
- package/realtime-client/src/use-pull-family-member.ts +26 -0
- package/realtime-client/src/use-pull-mutable-family-member.ts +38 -0
- package/realtime-client/src/use-pull-mutable.ts +32 -0
- package/realtime-client/src/use-pull.ts +19 -0
- package/realtime-client/src/use-push.ts +25 -0
- package/realtime-client/src/use-server-action.ts +49 -0
- package/realtime-server/dist/index.d.mts +25 -0
- package/realtime-server/dist/index.d.ts +25 -0
- package/realtime-server/dist/index.js +316 -0
- package/realtime-server/dist/index.js.map +1 -0
- package/realtime-server/dist/index.mjs +289 -0
- package/realtime-server/dist/index.mjs.map +1 -0
- package/realtime-server/package.json +15 -0
- package/realtime-server/src/README.md +33 -0
- package/realtime-server/src/hook-composition/expose-family.ts +105 -0
- package/realtime-server/src/hook-composition/expose-mutable-family.ts +127 -0
- package/realtime-server/src/hook-composition/expose-mutable.ts +45 -0
- package/realtime-server/src/hook-composition/expose-single.ts +39 -0
- package/realtime-server/src/hook-composition/index.ts +14 -0
- package/realtime-server/src/hook-composition/receive-state.ts +30 -0
- package/realtime-server/src/hook-composition/receive-transaction.ts +37 -0
- package/realtime-server/src/index.ts +1 -0
- package/src/get-set.ts +48 -0
- package/src/index.ts +3 -60
- 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
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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
|
)
|