atom.io 0.40.7 → 0.40.8
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 +1 -2
- package/dist/data/index.js.map +1 -1
- package/dist/employ-socket-D6wgByWh.js.map +1 -1
- package/dist/eslint-plugin/index.js.map +1 -1
- package/dist/{has-role-hv4-hJMw.js → has-role-CMlaUlaf.js} +30 -46
- package/dist/has-role-CMlaUlaf.js.map +1 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +103 -174
- package/dist/internal/index.js.map +1 -1
- package/dist/introspection/index.d.ts.map +1 -1
- package/dist/introspection/index.js +13 -32
- package/dist/introspection/index.js.map +1 -1
- package/dist/is-fn-DY1wZ-md.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.map +1 -1
- package/dist/main/index.js +1 -2
- package/dist/main/index.js.map +1 -1
- package/dist/mutex-store-CSvxY9i3.js.map +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react-devtools/index.d.ts.map +1 -1
- package/dist/react-devtools/index.js +2 -4
- package/dist/react-devtools/index.js.map +1 -1
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js +2 -3
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime-client/index.d.ts +1 -4
- package/dist/realtime-client/index.d.ts.map +1 -1
- package/dist/realtime-client/index.js +5 -20
- package/dist/realtime-client/index.js.map +1 -1
- package/dist/realtime-react/index.d.ts +2 -5
- package/dist/realtime-react/index.d.ts.map +1 -1
- package/dist/realtime-react/index.js +4 -15
- package/dist/realtime-react/index.js.map +1 -1
- package/dist/realtime-server/index.d.ts +36 -44
- package/dist/realtime-server/index.d.ts.map +1 -1
- package/dist/realtime-server/index.js +125 -171
- package/dist/realtime-server/index.js.map +1 -1
- package/dist/realtime-testing/index.d.ts +4 -2
- package/dist/realtime-testing/index.d.ts.map +1 -1
- package/dist/realtime-testing/index.js +13 -7
- package/dist/realtime-testing/index.js.map +1 -1
- package/dist/{shared-room-store-COGGKqes.js → shared-room-store-BfW3nWif.js} +2 -3
- package/dist/{shared-room-store-COGGKqes.js.map → shared-room-store-BfW3nWif.js.map} +1 -1
- package/dist/shared-room-store-D2o4ZLjC.d.ts.map +1 -1
- package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
- package/dist/transceivers/set-rtx/index.js +4 -8
- package/dist/transceivers/set-rtx/index.js.map +1 -1
- package/dist/web/index.js.map +1 -1
- package/package.json +11 -11
- package/src/internal/mutable/tracker.ts +66 -51
- package/src/internal/subscribe/subscribe-to-state.ts +9 -0
- package/src/realtime-client/index.ts +0 -1
- package/src/realtime-react/index.ts +0 -1
- package/src/realtime-server/continuity/continuity-store.ts +1 -26
- package/src/realtime-server/continuity/provide-continuity.ts +50 -0
- package/src/realtime-server/continuity/{subscribe-to-continuity-actions.ts → provide-outcomes.ts} +14 -12
- package/src/realtime-server/continuity/{subscribe-to-continuity-perpectives.ts → provide-perspectives.ts} +10 -8
- package/src/realtime-server/continuity/{prepare-to-send-initial-payload.ts → provide-startup-payloads.ts} +6 -4
- package/src/realtime-server/continuity/receive-action-requests.ts +68 -0
- package/src/realtime-server/continuity/{prepare-to-track-client-acknowledgement.ts → track-acknowledgements.ts} +15 -8
- package/src/realtime-server/index.ts +1 -2
- package/src/realtime-server/ipc-sockets/custom-socket.ts +3 -3
- package/src/realtime-server/ipc-sockets/parent-socket.ts +19 -13
- package/src/realtime-server/server-config.ts +0 -1
- package/src/realtime-testing/setup-realtime-test.tsx +20 -14
- package/dist/has-role-hv4-hJMw.js.map +0 -1
- package/src/realtime-client/server-action.ts +0 -23
- package/src/realtime-react/use-server-action.ts +0 -19
- package/src/realtime-server/continuity/prepare-to-serve-transaction-request.ts +0 -59
- package/src/realtime-server/continuity/prepare-to-sync-realtime-continuity.ts +0 -145
- package/src/realtime-server/realtime-action-receiver.ts +0 -40
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/transceivers/set-rtx/set-rtx.ts"],"sourcesContent":["import type { Lineage, Transceiver, TransceiverMode } from \"atom.io/internal\"\nimport { Subject } from \"atom.io/internal\"\nimport type { Json, primitive } from \"atom.io/json\"\nimport { stringifyJson } from \"atom.io/json\"\n\nexport type SetUpdateType = `add` | `clear` | `del` | `tx`\nexport type SetUpdate = `${SetUpdateType}:${string}`\nexport type NumberedSetUpdate = `${number}=${SetUpdate}`\n\nexport interface SetRTXView<P extends primitive> extends ReadonlySet<P> {\n\treadonly cache: ReadonlyArray<NumberedSetUpdate | null>\n\treadonly cacheLimit: number\n\treadonly cacheIdx: number\n\treadonly cacheUpdateNumber: number\n}\n\nexport interface SetRTXJson<P extends primitive> extends Json.Object {\n\tmembers: P[]\n\tcache: (NumberedSetUpdate | null)[]\n\tcacheLimit: number\n\tcacheIdx: number\n\tcacheUpdateNumber: number\n}\nexport class SetRTX<P extends primitive>\n\textends Set<P>\n\timplements\n\t\tTransceiver<SetRTXView<P>, NumberedSetUpdate, SetRTXJson<P>>,\n\t\tLineage\n{\n\tpublic mode: TransceiverMode = `record`\n\tpublic readonly subject: Subject<SetUpdate> = new Subject<SetUpdate>()\n\tpublic cacheLimit = 0\n\tpublic cache: (NumberedSetUpdate | null)[] = []\n\tpublic cacheIdx = -1\n\tpublic cacheUpdateNumber = -1\n\n\tpublic constructor(values?: Iterable<P>, cacheLimit = 0) {\n\t\tsuper(values)\n\t\tif (values instanceof SetRTX) {\n\t\t\tthis.parent = values\n\t\t\tthis.cacheUpdateNumber = values.cacheUpdateNumber\n\t\t}\n\t\tif (cacheLimit) {\n\t\t\tthis.cacheLimit = cacheLimit\n\t\t\tthis.cache = new Array(cacheLimit)\n\t\t\tthis.subscribe(`auto cache`, (update) => {\n\t\t\t\tthis.cacheIdx++\n\t\t\t\tthis.cacheIdx %= this.cacheLimit\n\t\t\t\tthis.cache[this.cacheIdx] = update\n\t\t\t})\n\t\t}\n\t}\n\n\tpublic readonly READONLY_VIEW: SetRTXView<P> = this\n\n\tpublic toJSON(): SetRTXJson<P> {\n\t\treturn {\n\t\t\tmembers: [...this],\n\t\t\tcache: this.cache,\n\t\t\tcacheLimit: this.cacheLimit,\n\t\t\tcacheIdx: this.cacheIdx,\n\t\t\tcacheUpdateNumber: this.cacheUpdateNumber,\n\t\t}\n\t}\n\n\tpublic static fromJSON<P extends primitive>(json: SetRTXJson<P>): SetRTX<P> {\n\t\tconst set = new SetRTX<P>(json.members, json.cacheLimit)\n\t\tset.cache = json.cache\n\t\tset.cacheIdx = json.cacheIdx\n\t\tset.cacheUpdateNumber = json.cacheUpdateNumber\n\t\treturn set\n\t}\n\n\tpublic add(value: P): this {\n\t\tconst result = super.add(value)\n\t\tif (this.mode === `record`) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`add:${stringifyJson<P>(value)}`)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic clear(): void {\n\t\tconst capturedContents = this.mode === `record` ? [...this] : null\n\t\tsuper.clear()\n\t\tif (capturedContents) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`clear:${JSON.stringify(capturedContents)}`)\n\t\t}\n\t}\n\n\tpublic delete(value: P): boolean {\n\t\tconst result = super.delete(value)\n\t\tif (this.mode === `record`) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`del:${stringifyJson<P>(value)}`)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic readonly parent: SetRTX<P> | null = null\n\tpublic child: SetRTX<P> | null = null\n\tpublic transactionUpdates: SetUpdate[] | null = null\n\tpublic transaction(run: (child: SetRTX<P>) => boolean): void {\n\t\tthis.mode = `transaction`\n\t\tthis.transactionUpdates = []\n\t\tthis.child = new SetRTX(this)\n\t\tconst unsubscribe = this.child._subscribe(`transaction`, (update) => {\n\t\t\tthis.transactionUpdates?.push(update)\n\t\t})\n\t\ttry {\n\t\t\tconst shouldCommit = run(this.child)\n\t\t\tif (shouldCommit) {\n\t\t\t\tfor (const update of this.transactionUpdates) {\n\t\t\t\t\tthis.doStep(update)\n\t\t\t\t}\n\t\t\t\tthis.cacheUpdateNumber++\n\t\t\t\tthis.emit(`tx:${this.transactionUpdates.join(`;`)}`)\n\t\t\t}\n\t\t} catch (thrown) {\n\t\t\t/* eslint-disable-next-line no-console */\n\t\t\tconsole.warn(\n\t\t\t\t`Did not apply transaction to SetRTX; this error was thrown:`,\n\t\t\t\tthrown,\n\t\t\t)\n\t\t\tthrow thrown\n\t\t} finally {\n\t\t\tunsubscribe()\n\t\t\tthis.child = null\n\t\t\tthis.transactionUpdates = null\n\t\t\tthis.mode = `record`\n\t\t}\n\t}\n\n\tprotected _subscribe(\n\t\tkey: string,\n\t\tfn: (update: SetUpdate) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, fn)\n\t}\n\tpublic subscribe(\n\t\tkey: string,\n\t\tfn: (update: NumberedSetUpdate) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, (update) => {\n\t\t\tfn(`${this.cacheUpdateNumber}=${update}`)\n\t\t})\n\t}\n\n\tpublic emit(update: SetUpdate): void {\n\t\tthis.subject.next(update)\n\t}\n\n\tprivate doStep(update: SetUpdate): void {\n\t\tconst typeValueBreak = update.indexOf(`:`)\n\t\tconst type = update.substring(0, typeValueBreak) as SetUpdateType\n\t\tconst value = update.substring(typeValueBreak + 1)\n\t\tswitch (type) {\n\t\t\tcase `add`:\n\t\t\t\tthis.add(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `clear`:\n\t\t\t\tthis.clear()\n\t\t\t\tbreak\n\t\t\tcase `del`:\n\t\t\t\tthis.delete(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `tx`:\n\t\t\t\tfor (const subUpdate of value.split(`;`)) {\n\t\t\t\t\tthis.doStep(subUpdate as SetUpdate)\n\t\t\t\t}\n\t\t}\n\t}\n\n\tpublic getUpdateNumber(update: NumberedSetUpdate): number {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\treturn Number(update.substring(0, breakpoint))\n\t}\n\n\tpublic do(update: NumberedSetUpdate): number | `OUT_OF_RANGE` | null {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\tconst updateNumber = Number(update.substring(0, breakpoint))\n\t\tconst eventOffset = updateNumber - this.cacheUpdateNumber\n\t\tconst isFuture = eventOffset > 0\n\t\tif (isFuture) {\n\t\t\tif (eventOffset === 1) {\n\t\t\t\tthis.mode = `playback`\n\t\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\t\tthis.doStep(innerUpdate)\n\t\t\t\tthis.mode = `record`\n\t\t\t\tthis.cacheUpdateNumber = updateNumber\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.cacheUpdateNumber + 1\n\t\t}\n\t\tif (Math.abs(eventOffset) < this.cacheLimit) {\n\t\t\tconst eventIdx = this.cacheIdx + eventOffset\n\t\t\tconst cachedUpdate = this.cache[eventIdx]\n\t\t\tif (cachedUpdate === update) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tthis.mode = `playback`\n\t\t\tlet done = false\n\t\t\twhile (!done) {\n\t\t\t\tthis.cacheIdx %= this.cacheLimit\n\t\t\t\tconst u = this.cache[this.cacheIdx]\n\t\t\t\tthis.cacheIdx--\n\t\t\t\tif (!u) {\n\t\t\t\t\treturn `OUT_OF_RANGE`\n\t\t\t\t}\n\t\t\t\tthis.undo(u)\n\t\t\t\tdone = this.cacheIdx === eventIdx - 1\n\t\t\t}\n\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\tthis.doStep(innerUpdate)\n\t\t\tthis.mode = `record`\n\t\t\tthis.cacheUpdateNumber = updateNumber\n\t\t\treturn null\n\t\t}\n\t\treturn `OUT_OF_RANGE`\n\t}\n\n\tpublic undoStep(update: SetUpdate): void {\n\t\tconst breakpoint = update.indexOf(`:`)\n\t\tconst type = update.substring(0, breakpoint) as SetUpdateType\n\t\tconst value = update.substring(breakpoint + 1)\n\t\tswitch (type) {\n\t\t\tcase `add`:\n\t\t\t\tthis.delete(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `del`:\n\t\t\t\tthis.add(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `clear`: {\n\t\t\t\tconst values = JSON.parse(value) as P[]\n\t\t\t\tfor (const v of values) this.add(v)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase `tx`: {\n\t\t\t\tconst updates = value.split(`;`) as SetUpdate[]\n\t\t\t\tfor (let i = updates.length - 1; i >= 0; i--) {\n\t\t\t\t\tthis.undoStep(updates[i])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic undo(update: NumberedSetUpdate): number | null {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\tconst updateNumber = Number(update.substring(0, breakpoint))\n\t\tif (updateNumber === this.cacheUpdateNumber) {\n\t\t\tthis.mode = `playback`\n\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\tthis.undoStep(innerUpdate)\n\t\t\tthis.mode = `record`\n\t\t\tthis.cacheUpdateNumber--\n\t\t\treturn null\n\t\t}\n\t\treturn this.cacheUpdateNumber\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/transceivers/set-rtx/set-rtx.ts"],"sourcesContent":["import type { Lineage, Transceiver, TransceiverMode } from \"atom.io/internal\"\nimport { Subject } from \"atom.io/internal\"\nimport type { Json, primitive } from \"atom.io/json\"\nimport { stringifyJson } from \"atom.io/json\"\n\nexport type SetUpdateType = `add` | `clear` | `del` | `tx`\nexport type SetUpdate = `${SetUpdateType}:${string}`\nexport type NumberedSetUpdate = `${number}=${SetUpdate}`\n\nexport interface SetRTXView<P extends primitive> extends ReadonlySet<P> {\n\treadonly cache: ReadonlyArray<NumberedSetUpdate | null>\n\treadonly cacheLimit: number\n\treadonly cacheIdx: number\n\treadonly cacheUpdateNumber: number\n}\n\nexport interface SetRTXJson<P extends primitive> extends Json.Object {\n\tmembers: P[]\n\tcache: (NumberedSetUpdate | null)[]\n\tcacheLimit: number\n\tcacheIdx: number\n\tcacheUpdateNumber: number\n}\nexport class SetRTX<P extends primitive>\n\textends Set<P>\n\timplements\n\t\tTransceiver<SetRTXView<P>, NumberedSetUpdate, SetRTXJson<P>>,\n\t\tLineage\n{\n\tpublic mode: TransceiverMode = `record`\n\tpublic readonly subject: Subject<SetUpdate> = new Subject<SetUpdate>()\n\tpublic cacheLimit = 0\n\tpublic cache: (NumberedSetUpdate | null)[] = []\n\tpublic cacheIdx = -1\n\tpublic cacheUpdateNumber = -1\n\n\tpublic constructor(values?: Iterable<P>, cacheLimit = 0) {\n\t\tsuper(values)\n\t\tif (values instanceof SetRTX) {\n\t\t\tthis.parent = values\n\t\t\tthis.cacheUpdateNumber = values.cacheUpdateNumber\n\t\t}\n\t\tif (cacheLimit) {\n\t\t\tthis.cacheLimit = cacheLimit\n\t\t\tthis.cache = new Array(cacheLimit)\n\t\t\tthis.subscribe(`auto cache`, (update) => {\n\t\t\t\tthis.cacheIdx++\n\t\t\t\tthis.cacheIdx %= this.cacheLimit\n\t\t\t\tthis.cache[this.cacheIdx] = update\n\t\t\t})\n\t\t}\n\t}\n\n\tpublic readonly READONLY_VIEW: SetRTXView<P> = this\n\n\tpublic toJSON(): SetRTXJson<P> {\n\t\treturn {\n\t\t\tmembers: [...this],\n\t\t\tcache: this.cache,\n\t\t\tcacheLimit: this.cacheLimit,\n\t\t\tcacheIdx: this.cacheIdx,\n\t\t\tcacheUpdateNumber: this.cacheUpdateNumber,\n\t\t}\n\t}\n\n\tpublic static fromJSON<P extends primitive>(json: SetRTXJson<P>): SetRTX<P> {\n\t\tconst set = new SetRTX<P>(json.members, json.cacheLimit)\n\t\tset.cache = json.cache\n\t\tset.cacheIdx = json.cacheIdx\n\t\tset.cacheUpdateNumber = json.cacheUpdateNumber\n\t\treturn set\n\t}\n\n\tpublic add(value: P): this {\n\t\tconst result = super.add(value)\n\t\tif (this.mode === `record`) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`add:${stringifyJson<P>(value)}`)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic clear(): void {\n\t\tconst capturedContents = this.mode === `record` ? [...this] : null\n\t\tsuper.clear()\n\t\tif (capturedContents) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`clear:${JSON.stringify(capturedContents)}`)\n\t\t}\n\t}\n\n\tpublic delete(value: P): boolean {\n\t\tconst result = super.delete(value)\n\t\tif (this.mode === `record`) {\n\t\t\tthis.cacheUpdateNumber++\n\t\t\tthis.emit(`del:${stringifyJson<P>(value)}`)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic readonly parent: SetRTX<P> | null = null\n\tpublic child: SetRTX<P> | null = null\n\tpublic transactionUpdates: SetUpdate[] | null = null\n\tpublic transaction(run: (child: SetRTX<P>) => boolean): void {\n\t\tthis.mode = `transaction`\n\t\tthis.transactionUpdates = []\n\t\tthis.child = new SetRTX(this)\n\t\tconst unsubscribe = this.child._subscribe(`transaction`, (update) => {\n\t\t\tthis.transactionUpdates?.push(update)\n\t\t})\n\t\ttry {\n\t\t\tconst shouldCommit = run(this.child)\n\t\t\tif (shouldCommit) {\n\t\t\t\tfor (const update of this.transactionUpdates) {\n\t\t\t\t\tthis.doStep(update)\n\t\t\t\t}\n\t\t\t\tthis.cacheUpdateNumber++\n\t\t\t\tthis.emit(`tx:${this.transactionUpdates.join(`;`)}`)\n\t\t\t}\n\t\t} catch (thrown) {\n\t\t\t/* eslint-disable-next-line no-console */\n\t\t\tconsole.warn(\n\t\t\t\t`Did not apply transaction to SetRTX; this error was thrown:`,\n\t\t\t\tthrown,\n\t\t\t)\n\t\t\tthrow thrown\n\t\t} finally {\n\t\t\tunsubscribe()\n\t\t\tthis.child = null\n\t\t\tthis.transactionUpdates = null\n\t\t\tthis.mode = `record`\n\t\t}\n\t}\n\n\tprotected _subscribe(\n\t\tkey: string,\n\t\tfn: (update: SetUpdate) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, fn)\n\t}\n\tpublic subscribe(\n\t\tkey: string,\n\t\tfn: (update: NumberedSetUpdate) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, (update) => {\n\t\t\tfn(`${this.cacheUpdateNumber}=${update}`)\n\t\t})\n\t}\n\n\tpublic emit(update: SetUpdate): void {\n\t\tthis.subject.next(update)\n\t}\n\n\tprivate doStep(update: SetUpdate): void {\n\t\tconst typeValueBreak = update.indexOf(`:`)\n\t\tconst type = update.substring(0, typeValueBreak) as SetUpdateType\n\t\tconst value = update.substring(typeValueBreak + 1)\n\t\tswitch (type) {\n\t\t\tcase `add`:\n\t\t\t\tthis.add(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `clear`:\n\t\t\t\tthis.clear()\n\t\t\t\tbreak\n\t\t\tcase `del`:\n\t\t\t\tthis.delete(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `tx`:\n\t\t\t\tfor (const subUpdate of value.split(`;`)) {\n\t\t\t\t\tthis.doStep(subUpdate as SetUpdate)\n\t\t\t\t}\n\t\t}\n\t}\n\n\tpublic getUpdateNumber(update: NumberedSetUpdate): number {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\treturn Number(update.substring(0, breakpoint))\n\t}\n\n\tpublic do(update: NumberedSetUpdate): number | `OUT_OF_RANGE` | null {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\tconst updateNumber = Number(update.substring(0, breakpoint))\n\t\tconst eventOffset = updateNumber - this.cacheUpdateNumber\n\t\tconst isFuture = eventOffset > 0\n\t\tif (isFuture) {\n\t\t\tif (eventOffset === 1) {\n\t\t\t\tthis.mode = `playback`\n\t\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\t\tthis.doStep(innerUpdate)\n\t\t\t\tthis.mode = `record`\n\t\t\t\tthis.cacheUpdateNumber = updateNumber\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.cacheUpdateNumber + 1\n\t\t}\n\t\tif (Math.abs(eventOffset) < this.cacheLimit) {\n\t\t\tconst eventIdx = this.cacheIdx + eventOffset\n\t\t\tconst cachedUpdate = this.cache[eventIdx]\n\t\t\tif (cachedUpdate === update) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tthis.mode = `playback`\n\t\t\tlet done = false\n\t\t\twhile (!done) {\n\t\t\t\tthis.cacheIdx %= this.cacheLimit\n\t\t\t\tconst u = this.cache[this.cacheIdx]\n\t\t\t\tthis.cacheIdx--\n\t\t\t\tif (!u) {\n\t\t\t\t\treturn `OUT_OF_RANGE`\n\t\t\t\t}\n\t\t\t\tthis.undo(u)\n\t\t\t\tdone = this.cacheIdx === eventIdx - 1\n\t\t\t}\n\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\tthis.doStep(innerUpdate)\n\t\t\tthis.mode = `record`\n\t\t\tthis.cacheUpdateNumber = updateNumber\n\t\t\treturn null\n\t\t}\n\t\treturn `OUT_OF_RANGE`\n\t}\n\n\tpublic undoStep(update: SetUpdate): void {\n\t\tconst breakpoint = update.indexOf(`:`)\n\t\tconst type = update.substring(0, breakpoint) as SetUpdateType\n\t\tconst value = update.substring(breakpoint + 1)\n\t\tswitch (type) {\n\t\t\tcase `add`:\n\t\t\t\tthis.delete(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `del`:\n\t\t\t\tthis.add(JSON.parse(value))\n\t\t\t\tbreak\n\t\t\tcase `clear`: {\n\t\t\t\tconst values = JSON.parse(value) as P[]\n\t\t\t\tfor (const v of values) this.add(v)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase `tx`: {\n\t\t\t\tconst updates = value.split(`;`) as SetUpdate[]\n\t\t\t\tfor (let i = updates.length - 1; i >= 0; i--) {\n\t\t\t\t\tthis.undoStep(updates[i])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic undo(update: NumberedSetUpdate): number | null {\n\t\tconst breakpoint = update.indexOf(`=`)\n\t\tconst updateNumber = Number(update.substring(0, breakpoint))\n\t\tif (updateNumber === this.cacheUpdateNumber) {\n\t\t\tthis.mode = `playback`\n\t\t\tconst innerUpdate = update.substring(breakpoint + 1) as SetUpdate\n\t\t\tthis.undoStep(innerUpdate)\n\t\t\tthis.mode = `record`\n\t\t\tthis.cacheUpdateNumber--\n\t\t\treturn null\n\t\t}\n\t\treturn this.cacheUpdateNumber\n\t}\n}\n"],"mappings":"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"}
|
package/dist/web/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/web/storage-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 storageSync =\n\t<T>(\n\t\tstorage: Storage | undefined,\n\t\t{ stringify, parse }: StringInterface<T>,\n\t\tkey: string,\n\t): AtomEffect<T> =>\n\t({ setSelf, onSet }) => {\n\t\tif (!storage) {\n\t\t\treturn\n\t\t}\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;AACvB,KAAI,CAAC,QACJ;CAED,MAAM,aAAa,QAAQ,QAAQ;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/web/storage-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 storageSync =\n\t<T>(\n\t\tstorage: Storage | undefined,\n\t\t{ stringify, parse }: StringInterface<T>,\n\t\tkey: string,\n\t): AtomEffect<T> =>\n\t({ setSelf, onSet }) => {\n\t\tif (!storage) {\n\t\t\treturn\n\t\t}\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;AACvB,KAAI,CAAC,QACJ;CAED,MAAM,aAAa,QAAQ,QAAQ,IAAI;AACvC,KAAI,cAAc,KAAM,SAAQ,MAAM,WAAW,CAAC;AAElD,QAAO,EAAE,eAAe;AACvB,MAAI,YAAY,MAAM;AACrB,WAAQ,WAAW,IAAI;AACvB;;AAED,UAAQ,QAAQ,KAAK,UAAU,SAAS,CAAC;GACxC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atom.io",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.8",
|
|
4
4
|
"description": "Composable and testable reactive data library.",
|
|
5
5
|
"homepage": "https://atom.io.fyi",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@eslint/core": "0.15.2",
|
|
64
|
-
"@storybook/addon-docs": "9.1.
|
|
65
|
-
"@storybook/addon-onboarding": "9.1.
|
|
66
|
-
"@storybook/react-vite": "9.1.
|
|
64
|
+
"@storybook/addon-docs": "9.1.5",
|
|
65
|
+
"@storybook/addon-onboarding": "9.1.5",
|
|
66
|
+
"@storybook/react-vite": "9.1.5",
|
|
67
67
|
"@testing-library/react": "16.3.0",
|
|
68
68
|
"@types/bun": "npm:bun-types@1.2.21",
|
|
69
69
|
"@types/eslint": "9.6.1",
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
"@types/npmlog": "7.0.0",
|
|
73
73
|
"@types/react": "19.1.12",
|
|
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.43.0",
|
|
76
|
+
"@typescript-eslint/rule-tester": "8.43.0",
|
|
77
|
+
"@typescript-eslint/utils": "8.43.0",
|
|
78
78
|
"@vitest/coverage-v8": "3.2.4",
|
|
79
79
|
"@vitest/ui": "3.2.4",
|
|
80
80
|
"concurrently": "9.2.1",
|
|
@@ -94,15 +94,15 @@
|
|
|
94
94
|
"recoverage": "0.1.11",
|
|
95
95
|
"socket.io": "4.8.1",
|
|
96
96
|
"socket.io-client": "4.8.1",
|
|
97
|
-
"storybook": "9.1.
|
|
97
|
+
"storybook": "9.1.5",
|
|
98
98
|
"tmp": "0.2.5",
|
|
99
|
-
"tsdown": "0.
|
|
99
|
+
"tsdown": "0.15.0",
|
|
100
100
|
"typescript": "5.9.2",
|
|
101
|
-
"vite": "7.1.
|
|
101
|
+
"vite": "7.1.5",
|
|
102
102
|
"vite-tsconfig-paths": "5.1.4",
|
|
103
103
|
"vitest": "3.2.4",
|
|
104
104
|
"zod": "3.25.76",
|
|
105
|
-
"break-check": "0.6.
|
|
105
|
+
"break-check": "0.6.16"
|
|
106
106
|
},
|
|
107
107
|
"main": "./dist/main/index.js",
|
|
108
108
|
"files": [
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
FamilyMetadata,
|
|
3
|
+
MutableAtomToken,
|
|
4
|
+
RegularAtomToken,
|
|
5
|
+
StateUpdate,
|
|
6
|
+
} from "atom.io"
|
|
2
7
|
|
|
3
8
|
import { createRegularAtom } from "../atom"
|
|
4
9
|
import { getFromStore } from "../get-state"
|
|
5
10
|
import { newest } from "../lineage"
|
|
6
11
|
import { setIntoStore } from "../set-state"
|
|
12
|
+
import { JOIN_OP, operateOnStore } from "../set-state/operate-on-store"
|
|
7
13
|
import type { Store } from "../store"
|
|
8
14
|
import { subscribeToState, subscribeToTimeline } from "../subscribe"
|
|
9
15
|
import { isChildStore } from "../transaction/is-root-store"
|
|
@@ -61,9 +67,9 @@ export class Tracker<T extends Transceiver<any, any, any>> {
|
|
|
61
67
|
const storeStatus = isChildStore(target)
|
|
62
68
|
? target.transactionMeta.update.token.key
|
|
63
69
|
: `main`
|
|
64
|
-
const subscriptionKey = `tracker:${storeName}:${storeStatus}:${stateKey}`
|
|
70
|
+
const subscriptionKey = `tracker-from-core:${storeName}:${storeStatus}:${stateKey}`
|
|
65
71
|
const trackerCapturesOutboundSignal = (update: SignalFrom<T>) => {
|
|
66
|
-
|
|
72
|
+
operateOnStore(JOIN_OP, target, latestSignalState, update)
|
|
67
73
|
}
|
|
68
74
|
const originalInnerValue = getFromStore(target, mutableState)
|
|
69
75
|
this.unsubscribeFromInnerValue = originalInnerValue.subscribe(
|
|
@@ -91,60 +97,69 @@ export class Tracker<T extends Transceiver<any, any, any>> {
|
|
|
91
97
|
latestSignalState: RegularAtomToken<SignalFrom<T> | null>,
|
|
92
98
|
target: Store,
|
|
93
99
|
): void {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
const stateKey = mutableState.key
|
|
101
|
+
const storeName = target.config.name
|
|
102
|
+
const storeStatus = isChildStore(target)
|
|
103
|
+
? target.transactionMeta.update.token.key
|
|
104
|
+
: `main`
|
|
105
|
+
const subscriptionKey = `tracker-to-core:${storeName}:${storeStatus}:${stateKey}`
|
|
97
106
|
subscribeToState(
|
|
98
107
|
target,
|
|
99
108
|
latestSignalState,
|
|
100
109
|
subscriptionKey,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
target,
|
|
109
|
-
{ key: timelineId, type: `timeline` },
|
|
110
|
-
subscriptionKey,
|
|
111
|
-
function trackerWaitsForTimeTravelToFinish(update) {
|
|
112
|
-
unsubscribe()
|
|
113
|
-
setIntoStore(target, mutableState, (transceiver) => {
|
|
114
|
-
if (update === `redo` && newValue) {
|
|
115
|
-
transceiver.do(newValue)
|
|
116
|
-
} else if (update === `undo` && oldValue) {
|
|
117
|
-
transceiver.undo(oldValue)
|
|
118
|
-
}
|
|
119
|
-
return transceiver
|
|
120
|
-
})
|
|
121
|
-
},
|
|
110
|
+
Object.assign(
|
|
111
|
+
function trackerCapturesInboundSignal({
|
|
112
|
+
newValue,
|
|
113
|
+
oldValue,
|
|
114
|
+
}: StateUpdate<SignalFrom<T> | null>) {
|
|
115
|
+
const timelineId = target.timelineTopics.getRelatedKey(
|
|
116
|
+
latestSignalState.key,
|
|
122
117
|
)
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
118
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
119
|
+
if (timelineId && target.timelines.get(timelineId)?.timeTraveling) {
|
|
120
|
+
const unsubscribe = subscribeToTimeline(
|
|
121
|
+
target,
|
|
122
|
+
{ key: timelineId, type: `timeline` },
|
|
123
|
+
subscriptionKey,
|
|
124
|
+
function trackerWaitsForTimeTravelToFinish(update) {
|
|
125
|
+
unsubscribe()
|
|
126
|
+
setIntoStore(target, mutableState, (transceiver) => {
|
|
127
|
+
if (update === `redo` && newValue) {
|
|
128
|
+
transceiver.do(newValue)
|
|
129
|
+
} else if (update === `undo` && oldValue) {
|
|
130
|
+
transceiver.undo(oldValue)
|
|
131
|
+
}
|
|
132
|
+
return transceiver
|
|
133
|
+
})
|
|
134
|
+
},
|
|
135
|
+
)
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const mutable = getFromStore(target, mutableState)
|
|
140
|
+
const updateNumber = mutable.getUpdateNumber(newValue)
|
|
141
|
+
const eventOffset = updateNumber - mutable.cacheUpdateNumber
|
|
142
|
+
if (newValue && eventOffset === 1) {
|
|
143
|
+
setIntoStore(
|
|
144
|
+
target,
|
|
145
|
+
mutableState,
|
|
146
|
+
(transceiver) => (transceiver.do(newValue), transceiver),
|
|
147
|
+
)
|
|
148
|
+
} else {
|
|
149
|
+
const expected = mutable.cacheUpdateNumber + 1
|
|
150
|
+
target.logger.info(
|
|
151
|
+
`❌`,
|
|
152
|
+
`mutable_atom`,
|
|
153
|
+
mutableState.key,
|
|
154
|
+
`could not be updated. Expected update number`,
|
|
155
|
+
expected,
|
|
156
|
+
`but got`,
|
|
157
|
+
updateNumber,
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{ inboundTracker: true },
|
|
162
|
+
),
|
|
148
163
|
)
|
|
149
164
|
}
|
|
150
165
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReadableToken, StateUpdate, UpdateHandler } from "atom.io"
|
|
2
2
|
|
|
3
|
+
import { hasRole } from "../atom"
|
|
3
4
|
import { readOrComputeValue } from "../get-state"
|
|
4
5
|
import { reduceReference } from "../get-state/reduce-reference"
|
|
5
6
|
import { traceRootSelectorAtoms } from "../selector"
|
|
@@ -15,6 +16,14 @@ export function subscribeToState<T, E>(
|
|
|
15
16
|
): () => void {
|
|
16
17
|
function safelyHandleUpdate(update: StateUpdate<any>): void {
|
|
17
18
|
if (store.operation.open) {
|
|
19
|
+
if (
|
|
20
|
+
state?.type === `atom` &&
|
|
21
|
+
hasRole(state, `tracker:signal`) &&
|
|
22
|
+
`*` + store.operation.token.key === token.key &&
|
|
23
|
+
`inboundTracker` in handleUpdate
|
|
24
|
+
) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
18
27
|
const unsubscribe = store.on.operationClose.subscribe(
|
|
19
28
|
`state subscription ${key}`,
|
|
20
29
|
() => {
|
|
@@ -7,6 +7,5 @@ export * from "./use-pull-selector"
|
|
|
7
7
|
export * from "./use-pull-selector-family-member"
|
|
8
8
|
export * from "./use-push"
|
|
9
9
|
export * from "./use-realtime-service"
|
|
10
|
-
export * from "./use-server-action"
|
|
11
10
|
export * from "./use-single-effect"
|
|
12
11
|
export * from "./use-sync-continuity"
|
|
@@ -7,14 +7,6 @@ import { atomFamily } from "atom.io"
|
|
|
7
7
|
|
|
8
8
|
import type { UserKey } from "../realtime-server-stores/server-user-store"
|
|
9
9
|
|
|
10
|
-
// export const completeUpdateAtoms = atomFamily<
|
|
11
|
-
// TransactionUpdate<any> | null,
|
|
12
|
-
// string
|
|
13
|
-
// >({
|
|
14
|
-
// key: `completeUpdate`,
|
|
15
|
-
// default: null,
|
|
16
|
-
// })
|
|
17
|
-
|
|
18
10
|
export function redactTransactionUpdateContent(
|
|
19
11
|
visibleStateKeys: string[],
|
|
20
12
|
updates: TransactionSubEvent[],
|
|
@@ -67,29 +59,12 @@ export const redactorAtoms: RegularAtomFamilyToken<
|
|
|
67
59
|
key: `redactor`,
|
|
68
60
|
default: { occlude: (updates) => updates },
|
|
69
61
|
})
|
|
70
|
-
// export const redactedUpdateSelectors = selectorFamily<
|
|
71
|
-
// TransactionUpdate<any> | null,
|
|
72
|
-
// [transactionKey: string, updateId: string]
|
|
73
|
-
// >({
|
|
74
|
-
// key: `redactedUpdate`,
|
|
75
|
-
// get:
|
|
76
|
-
// ([transactionKey, updateId]) =>
|
|
77
|
-
// ({ get, find }) => {
|
|
78
|
-
// const update = get(find(completeUpdateAtoms, updateId))
|
|
79
|
-
// const { filter } = get(find(transactionRedactorAtoms, transactionKey))
|
|
80
|
-
|
|
81
|
-
// if (update && filter) {
|
|
82
|
-
// return { ...update, updates: filter(update.updates) }
|
|
83
|
-
// }
|
|
84
|
-
// return null
|
|
85
|
-
// },
|
|
86
|
-
// })
|
|
87
62
|
|
|
88
63
|
export type ContinuitySyncTransactionUpdate = Pick<
|
|
89
64
|
TransactionOutcomeEvent<any>,
|
|
90
65
|
`epoch` | `id` | `output` | `subEvents` | `token`
|
|
91
66
|
>
|
|
92
|
-
export const
|
|
67
|
+
export const unacknowledgedUpdatesAtoms: RegularAtomFamilyToken<
|
|
93
68
|
ContinuitySyncTransactionUpdate[],
|
|
94
69
|
UserKey
|
|
95
70
|
> = atomFamily<ContinuitySyncTransactionUpdate[], UserKey>({
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getFromStore, IMPLICIT } from "atom.io/internal"
|
|
2
|
+
import type { Json } from "atom.io/json"
|
|
3
|
+
import type { ContinuityToken } from "atom.io/realtime"
|
|
4
|
+
|
|
5
|
+
import type { ServerConfig, UserKey } from ".."
|
|
6
|
+
import { unacknowledgedUpdatesAtoms } from "./continuity-store"
|
|
7
|
+
import { provideOutcomes } from "./provide-outcomes"
|
|
8
|
+
import { providePerspectives } from "./provide-perspectives"
|
|
9
|
+
import { provideStartupPayloads } from "./provide-startup-payloads"
|
|
10
|
+
import { receiveActionRequests } from "./receive-action-requests"
|
|
11
|
+
import { trackAcknowledgements } from "./track-acknowledgements"
|
|
12
|
+
|
|
13
|
+
export type ProvideContinuity = (
|
|
14
|
+
continuity: ContinuityToken,
|
|
15
|
+
userKey: UserKey,
|
|
16
|
+
) => () => void
|
|
17
|
+
export function prepareToProvideContinuity({
|
|
18
|
+
socket,
|
|
19
|
+
store = IMPLICIT.STORE,
|
|
20
|
+
}: ServerConfig): ProvideContinuity {
|
|
21
|
+
return function syncRealtimeContinuity(continuity, userKey) {
|
|
22
|
+
const continuityKey = continuity.key
|
|
23
|
+
|
|
24
|
+
const unacknowledgedUpdates = getFromStore(
|
|
25
|
+
store,
|
|
26
|
+
unacknowledgedUpdatesAtoms,
|
|
27
|
+
userKey,
|
|
28
|
+
)
|
|
29
|
+
for (const unacknowledgedUpdate of unacknowledgedUpdates) {
|
|
30
|
+
socket.emit(
|
|
31
|
+
`tx-new:${continuityKey}`,
|
|
32
|
+
unacknowledgedUpdate as Json.Serializable,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const subscriptions = new Set<() => void>()
|
|
37
|
+
const clearSubscriptions = () => {
|
|
38
|
+
for (const unsubscribe of subscriptions) unsubscribe()
|
|
39
|
+
subscriptions.clear()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
subscriptions.add(providePerspectives(store, socket, continuity, userKey))
|
|
43
|
+
subscriptions.add(provideOutcomes(store, socket, continuity, userKey))
|
|
44
|
+
subscriptions.add(provideStartupPayloads(store, socket, continuity, userKey))
|
|
45
|
+
subscriptions.add(receiveActionRequests(store, socket, continuity, userKey))
|
|
46
|
+
subscriptions.add(trackAcknowledgements(store, socket, continuity, userKey))
|
|
47
|
+
|
|
48
|
+
return clearSubscriptions
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/realtime-server/continuity/{subscribe-to-continuity-actions.ts → provide-outcomes.ts}
RENAMED
|
@@ -12,24 +12,24 @@ import type { ContinuityToken } from "atom.io/realtime"
|
|
|
12
12
|
import type { Socket, UserKey } from ".."
|
|
13
13
|
import {
|
|
14
14
|
redactTransactionUpdateContent,
|
|
15
|
-
|
|
15
|
+
unacknowledgedUpdatesAtoms,
|
|
16
16
|
} from "./continuity-store"
|
|
17
17
|
|
|
18
|
-
export function
|
|
18
|
+
export function provideOutcomes(
|
|
19
19
|
store: Store,
|
|
20
|
+
socket: Socket,
|
|
20
21
|
continuity: ContinuityToken,
|
|
21
22
|
userKey: UserKey,
|
|
22
|
-
|
|
23
|
-
): (() => void)[] {
|
|
23
|
+
): () => void {
|
|
24
24
|
const continuityKey = continuity.key
|
|
25
|
-
const unsubscribeFunctions
|
|
25
|
+
const unsubscribeFunctions = new Set<() => void>()
|
|
26
26
|
|
|
27
27
|
for (const transaction of continuity.actions) {
|
|
28
28
|
const unsubscribeFromTransaction = subscribeToTransaction(
|
|
29
29
|
store,
|
|
30
30
|
transaction,
|
|
31
31
|
`sync-continuity:${continuityKey}:${userKey}`,
|
|
32
|
-
(
|
|
32
|
+
(outcomes) => {
|
|
33
33
|
try {
|
|
34
34
|
const visibleKeys = continuity.globals
|
|
35
35
|
.map((atom) => {
|
|
@@ -59,13 +59,13 @@ export function subscribeToContinuityActions(
|
|
|
59
59
|
)
|
|
60
60
|
const redactedUpdates = redactTransactionUpdateContent(
|
|
61
61
|
visibleKeys,
|
|
62
|
-
|
|
62
|
+
outcomes.subEvents,
|
|
63
63
|
)
|
|
64
64
|
const redactedUpdate = {
|
|
65
|
-
...
|
|
65
|
+
...outcomes,
|
|
66
66
|
updates: redactedUpdates,
|
|
67
67
|
}
|
|
68
|
-
setIntoStore(store,
|
|
68
|
+
setIntoStore(store, unacknowledgedUpdatesAtoms, userKey, (updates) => {
|
|
69
69
|
if (redactedUpdate) {
|
|
70
70
|
updates.push(redactedUpdate)
|
|
71
71
|
updates.sort((a, b) => a.epoch - b.epoch)
|
|
@@ -81,7 +81,7 @@ export function subscribeToContinuityActions(
|
|
|
81
81
|
return updates
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
socket
|
|
84
|
+
socket.emit(
|
|
85
85
|
`tx-new:${continuityKey}`,
|
|
86
86
|
redactedUpdate as Json.Serializable,
|
|
87
87
|
)
|
|
@@ -98,7 +98,9 @@ export function subscribeToContinuityActions(
|
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
100
|
)
|
|
101
|
-
unsubscribeFunctions.
|
|
101
|
+
unsubscribeFunctions.add(unsubscribeFromTransaction)
|
|
102
|
+
}
|
|
103
|
+
return () => {
|
|
104
|
+
for (const unsubscribe of unsubscribeFunctions) unsubscribe()
|
|
102
105
|
}
|
|
103
|
-
return unsubscribeFunctions
|
|
104
106
|
}
|
|
@@ -10,14 +10,14 @@ import type { ContinuityToken } from "atom.io/realtime"
|
|
|
10
10
|
import type { Socket } from ".."
|
|
11
11
|
import type { UserKey } from "../realtime-server-stores"
|
|
12
12
|
|
|
13
|
-
export function
|
|
13
|
+
export function providePerspectives(
|
|
14
14
|
store: Store,
|
|
15
|
+
socket: Socket,
|
|
15
16
|
continuity: ContinuityToken,
|
|
16
17
|
userKey: UserKey,
|
|
17
|
-
|
|
18
|
-
): (() => void)[] {
|
|
18
|
+
): () => void {
|
|
19
19
|
const continuityKey = continuity.key
|
|
20
|
-
const unsubFns
|
|
20
|
+
const unsubFns = new Set<() => void>()
|
|
21
21
|
for (const perspective of continuity.perspectives) {
|
|
22
22
|
const { viewAtoms } = perspective
|
|
23
23
|
const userViewState = findInStore(store, viewAtoms, userKey)
|
|
@@ -47,14 +47,16 @@ export function subscribeToContinuityPerspectives(
|
|
|
47
47
|
{ oldKeys, newKeys, revealed, concealed },
|
|
48
48
|
)
|
|
49
49
|
if (revealed.length > 0) {
|
|
50
|
-
socket
|
|
50
|
+
socket.emit(`reveal:${continuityKey}`, revealed)
|
|
51
51
|
}
|
|
52
52
|
if (concealed && concealed.length > 0) {
|
|
53
|
-
socket
|
|
53
|
+
socket.emit(`conceal:${continuityKey}`, concealed)
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
)
|
|
57
|
-
unsubFns.
|
|
57
|
+
unsubFns.add(unsubscribeFromUserView)
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
for (const unsubscribe of unsubFns) unsubscribe()
|
|
58
61
|
}
|
|
59
|
-
return unsubFns
|
|
60
62
|
}
|
|
@@ -9,15 +9,16 @@ import type { Json } from "atom.io/json"
|
|
|
9
9
|
import type { ContinuityToken } from "atom.io/realtime"
|
|
10
10
|
|
|
11
11
|
import type { Socket, UserKey } from ".."
|
|
12
|
+
import { employSocket } from "../employ-socket"
|
|
12
13
|
|
|
13
|
-
export function
|
|
14
|
+
export function provideStartupPayloads(
|
|
14
15
|
store: Store,
|
|
16
|
+
socket: Socket,
|
|
15
17
|
continuity: ContinuityToken,
|
|
16
18
|
userKey: UserKey,
|
|
17
|
-
socket: Socket | null,
|
|
18
19
|
): () => void {
|
|
19
20
|
const continuityKey = continuity.key
|
|
20
|
-
|
|
21
|
+
function sendInitialPayload(): void {
|
|
21
22
|
const initialPayload: Json.Serializable[] = []
|
|
22
23
|
for (const atom of continuity.globals) {
|
|
23
24
|
const resourceToken =
|
|
@@ -49,6 +50,7 @@ export function prepareToSendInitialPayload(
|
|
|
49
50
|
? (store.transactionMeta.epoch.get(continuityKey) ?? null)
|
|
50
51
|
: null
|
|
51
52
|
|
|
52
|
-
socket
|
|
53
|
+
socket.emit(`continuity-init:${continuityKey}`, epoch, initialPayload)
|
|
53
54
|
}
|
|
55
|
+
return employSocket(socket, `get:${continuityKey}`, sendInitialPayload)
|
|
54
56
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { TransactionOutcomeEvent, TransactionToken } from "atom.io"
|
|
2
|
+
import type { Store } from "atom.io/internal"
|
|
3
|
+
import { actUponStore } from "atom.io/internal"
|
|
4
|
+
import type { Json, JsonIO } from "atom.io/json"
|
|
5
|
+
import type { ContinuityToken } from "atom.io/realtime"
|
|
6
|
+
|
|
7
|
+
import { employSocket } from "../employ-socket"
|
|
8
|
+
import type { Socket } from "../socket-interface"
|
|
9
|
+
|
|
10
|
+
export function receiveActionRequests(
|
|
11
|
+
store: Store,
|
|
12
|
+
socket: Socket,
|
|
13
|
+
continuity: ContinuityToken,
|
|
14
|
+
userKey: string,
|
|
15
|
+
): () => void {
|
|
16
|
+
const continuityKey = continuity.key
|
|
17
|
+
|
|
18
|
+
return employSocket(
|
|
19
|
+
socket,
|
|
20
|
+
`tx-run:${continuityKey}`,
|
|
21
|
+
function serveTransactionRequest(
|
|
22
|
+
txOutcome: Json.Serializable &
|
|
23
|
+
Pick<
|
|
24
|
+
TransactionOutcomeEvent<TransactionToken<JsonIO>>,
|
|
25
|
+
`id` | `params` | `token`
|
|
26
|
+
>,
|
|
27
|
+
) {
|
|
28
|
+
store.logger.info(`🛎️`, `continuity`, continuityKey, `received`, txOutcome)
|
|
29
|
+
const transactionKey = txOutcome.token.key
|
|
30
|
+
const updateId = txOutcome.id
|
|
31
|
+
const performanceKey = `tx-run:${transactionKey}:${updateId}`
|
|
32
|
+
const performanceKeyStart = `${performanceKey}:start`
|
|
33
|
+
const performanceKeyEnd = `${performanceKey}:end`
|
|
34
|
+
performance.mark(performanceKeyStart)
|
|
35
|
+
try {
|
|
36
|
+
actUponStore(
|
|
37
|
+
store,
|
|
38
|
+
{ type: `transaction`, key: transactionKey },
|
|
39
|
+
updateId,
|
|
40
|
+
)(...txOutcome.params)
|
|
41
|
+
} catch (thrown) {
|
|
42
|
+
if (thrown instanceof Error) {
|
|
43
|
+
store.logger.error(
|
|
44
|
+
`❌`,
|
|
45
|
+
`continuity`,
|
|
46
|
+
continuityKey,
|
|
47
|
+
`failed to run transaction ${transactionKey} from ${userKey} with update ${updateId}`,
|
|
48
|
+
thrown.message,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
performance.mark(performanceKeyEnd)
|
|
53
|
+
const metric = performance.measure(
|
|
54
|
+
performanceKey,
|
|
55
|
+
performanceKeyStart,
|
|
56
|
+
performanceKeyEnd,
|
|
57
|
+
)
|
|
58
|
+
store.logger.info(
|
|
59
|
+
`🚀`,
|
|
60
|
+
`transaction`,
|
|
61
|
+
transactionKey,
|
|
62
|
+
updateId,
|
|
63
|
+
userKey,
|
|
64
|
+
metric.duration,
|
|
65
|
+
)
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import type { Store } from "atom.io/internal"
|
|
2
|
-
import { setIntoStore } from "atom.io/internal"
|
|
2
|
+
import { getFromStore, setIntoStore } from "atom.io/internal"
|
|
3
3
|
import type { ContinuityToken } from "atom.io/realtime"
|
|
4
4
|
|
|
5
|
+
import { employSocket } from "../employ-socket"
|
|
5
6
|
import type { UserKey } from "../realtime-server-stores"
|
|
6
|
-
import type {
|
|
7
|
-
import {
|
|
7
|
+
import type { Socket } from "../socket-interface"
|
|
8
|
+
import { unacknowledgedUpdatesAtoms } from "./continuity-store"
|
|
8
9
|
|
|
9
|
-
export function
|
|
10
|
+
export function trackAcknowledgements(
|
|
10
11
|
store: Store,
|
|
12
|
+
socket: Socket,
|
|
11
13
|
continuity: ContinuityToken,
|
|
12
14
|
userKey: UserKey,
|
|
13
|
-
|
|
14
|
-
): (epoch: number) => void {
|
|
15
|
+
): () => void {
|
|
15
16
|
const continuityKey = continuity.key
|
|
16
|
-
|
|
17
|
+
const userUnacknowledgedUpdates = getFromStore(
|
|
18
|
+
store,
|
|
19
|
+
unacknowledgedUpdatesAtoms,
|
|
20
|
+
userKey,
|
|
21
|
+
)
|
|
22
|
+
function trackClientAcknowledgement(epoch: number): void {
|
|
17
23
|
store.logger.info(
|
|
18
24
|
`👍`,
|
|
19
25
|
`continuity`,
|
|
@@ -22,7 +28,7 @@ export function prepareToTrackClientAcknowledgement(
|
|
|
22
28
|
)
|
|
23
29
|
const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch
|
|
24
30
|
if (isUnacknowledged) {
|
|
25
|
-
setIntoStore(store,
|
|
31
|
+
setIntoStore(store, unacknowledgedUpdatesAtoms, userKey, (updates) => {
|
|
26
32
|
updates.shift()
|
|
27
33
|
store.logger.info(
|
|
28
34
|
`👍`,
|
|
@@ -36,4 +42,5 @@ export function prepareToTrackClientAcknowledgement(
|
|
|
36
42
|
})
|
|
37
43
|
}
|
|
38
44
|
}
|
|
45
|
+
return employSocket(socket, `ack:${continuityKey}`, trackClientAcknowledgement)
|
|
39
46
|
}
|