From 9330a8baef2e0269f2aa3f411bf20752d25a4e7b Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Thu, 3 Oct 2024 00:07:10 +0200 Subject: [PATCH 1/9] =?UTF-8?q?test:=20=F0=9F=92=8D=20setup=20text=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/__demos__/ui-json/main.tsx | 2 +- src/__demos__/ui-text/main.tsx | 60 +++++++++++++++++++++++++ src/__demos__/ui-text/webpack.config.js | 35 +++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/__demos__/ui-text/main.tsx create mode 100644 src/__demos__/ui-text/webpack.config.js diff --git a/package.json b/package.json index e1e77637..c04fc067 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "demo:e2e:json-crdt-server:http1": "ts-node src/__demos__/json-crdt-server/main-http1.ts", "demo:e2e:json-crdt-server:uws": "ts-node src/__demos__/json-crdt-server/main-uws.ts", "demo:ui:json": "webpack serve --config ./src/__demos__/ui-json/webpack.config.js", + "demo:ui:text": "webpack serve --config ./src/__demos__/ui-text/webpack.config.js", "start:json-crdt-server:http1": "NODE_ENV=production PORT=80 JSON_CRDT_STORE=level pm2 start lib/__demos__/json-crdt-server/main-http1.js", "start": "NODE_ENV=production PORT=80 JSON_CRDT_STORE=level pm2 start lib/__demos__/json-crdt-server/main-http1.js --exp-backoff-restart-delay=100", "coverage": "yarn test --collectCoverage", diff --git a/src/__demos__/ui-json/main.tsx b/src/__demos__/ui-json/main.tsx index 370448bc..26488a7a 100644 --- a/src/__demos__/ui-json/main.tsx +++ b/src/__demos__/ui-json/main.tsx @@ -9,7 +9,7 @@ import {Model, Patch} from 'json-joy/lib/json-crdt'; const repo = new JsonCrdtRepo({ wsUrl: 'wss://demo-iasd8921ondk0.jsonjoy.com/rpc', }); -const id = 'block-sync-ui-demo-id'; +const id = 'block-sync-ui-demo-json'; const session = repo.make(id); const model = session.model; diff --git a/src/__demos__/ui-text/main.tsx b/src/__demos__/ui-text/main.tsx new file mode 100644 index 00000000..9496d6ed --- /dev/null +++ b/src/__demos__/ui-text/main.tsx @@ -0,0 +1,60 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import {JsonCrdtRepo} from '../../json-crdt-repo/JsonCrdtRepo'; +import {bind} from 'collaborative-input'; +import {Model, Patch} from 'json-joy/lib/json-crdt'; + +/* tslint:disable no-console */ + +const main = async () => { + const repo = new JsonCrdtRepo({ + wsUrl: 'wss://demo-iasd8921ondk0.jsonjoy.com/rpc', + }); + const id = 'block-sync-ui-demo-text'; + const session = await repo.sessions.load({id: [id], remote: {}}); + const model = session.model; + const view = model.view(); + if (typeof view !== 'string') model.api.root(''); + + + const Demo: React.FC = () => { + const [remote, setRemote] = React.useState(null); + const ref = React.useRef(null); + React.useLayoutEffect(() => { + if (!ref.current) return; + const unbind = bind(() => model.api.str([]), ref.current); + return () => unbind(); + }, []); + + return ( +
+ +
+ +
+ {!!remote && ( + +
{remote.toString()}
+
+ )} +
+ ); + }; + + const div = document.createElement('div'); + document.body.appendChild(div); + const root = ReactDOM.createRoot(div); + root.render(); +}; + +main().catch(() => {}); diff --git a/src/__demos__/ui-text/webpack.config.js b/src/__demos__/ui-text/webpack.config.js new file mode 100644 index 00000000..202a1346 --- /dev/null +++ b/src/__demos__/ui-text/webpack.config.js @@ -0,0 +1,35 @@ +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + +module.exports = { + mode: 'development', + devtool: 'inline-source-map', + entry: { + bundle: __dirname + '/main', + }, + plugins: [ + new HtmlWebpackPlugin({ + title: 'Development', + }), + ], + module: { + rules: [ + { + test: /\.tsx?$/, + exclude: /node_modules/, + loader: 'ts-loader', + }, + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, + output: { + filename: '[name].js', + path: path.resolve(__dirname, '../../dist'), + }, + devServer: { + port: 9949, + hot: false, + }, +}; From 0ce937410d53f9558249e12e9d24b3e17d517bf1 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 3 Oct 2024 09:38:00 +0200 Subject: [PATCH 2/9] =?UTF-8?q?perf:=20=E2=9A=A1=EF=B8=8F=20emit=20operati?= =?UTF-8?q?on=20to=20pubsub=20ASAP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-repo/local/level/LevelLocalRepo.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/json-crdt-repo/local/level/LevelLocalRepo.ts b/src/json-crdt-repo/local/level/LevelLocalRepo.ts index f590136c..18e89c16 100644 --- a/src/json-crdt-repo/local/level/LevelLocalRepo.ts +++ b/src/json-crdt-repo/local/level/LevelLocalRepo.ts @@ -775,15 +775,15 @@ export class LevelLocalRepo implements LocalRepo { const op: BinStrLevelOperation = {type: 'put', key: patchKey, value: uint8}; ops.push(op); } + if (writtenPatches.length) { + this.pubsub.pub({type: 'rebase', id, patches: writtenPatches, session: req.session}); + } if (ops.length) { await this.kv.batch(ops); return true; } return false; }); - if (writtenPatches.length) { - this.pubsub.pub({type: 'rebase', id, patches: writtenPatches, session: req.session}); - } if (!didPush && !needsReset) { const merge = await this.readFrontier0(keyBase); return {cursor, merge}; From 335739e43ceb0affeb67078e48ce6e7d098e9a24 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 3 Oct 2024 10:39:49 +0200 Subject: [PATCH 3/9] =?UTF-8?q?fix:=20=F0=9F=90=9B=20harden=20error=20intr?= =?UTF-8?q?ospection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-repo/session/EditSessionFactory.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/json-crdt-repo/session/EditSessionFactory.ts b/src/json-crdt-repo/session/EditSessionFactory.ts index 6a411aef..39b73b7b 100644 --- a/src/json-crdt-repo/session/EditSessionFactory.ts +++ b/src/json-crdt-repo/session/EditSessionFactory.ts @@ -61,14 +61,14 @@ export class EditSessionFactory { const {model, cursor} = await (typeof timeoutMs === 'number' ? timeout(timeoutMs, repo.pull(id)) : repo.pull(id)); - if (remote.throwIf === 'exists') throw new Error('EXISTS'); - const session = new EditSession(repo, id, model, cursor, opts.session); - session.log.end.api.autoFlush(); - return session; - } catch (error) { - if (error instanceof Error && error.message === 'TIMEOUT') { + if (remote.throwIf === 'exists') throw new Error('EXISTS'); + const session = new EditSession(repo, id, model, cursor, opts.session); + session.log.end.api.autoFlush(); + return session; + } catch (error) { + if (!!error && typeof error === 'object' && (error as Record).message === 'TIMEOUT') { if (!opts.make) throw error; - } else if (error instanceof Error && error.message === 'NOT_FOUND') { + } else if (!!error && typeof error === 'object' && ((error as Record).message === 'NOT_FOUND' || (error as Record).code === 'NOT_FOUND')) { if (remote.throwIf === 'missing') throw error; } else throw error; } From 0cde405dfcacc856b985369746dda231306b58ec Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 3 Oct 2024 11:47:52 +0200 Subject: [PATCH 4/9] =?UTF-8?q?fix:=20=F0=9F=90=9B=20do=20not=20check=20fo?= =?UTF-8?q?r=20sync=20on=20local=20sync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__demos__/ui-text/main.tsx | 4 ++-- src/json-crdt-repo/local/level/LevelLocalRepo.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/__demos__/ui-text/main.tsx b/src/__demos__/ui-text/main.tsx index 9496d6ed..5ad6d2c1 100644 --- a/src/__demos__/ui-text/main.tsx +++ b/src/__demos__/ui-text/main.tsx @@ -10,8 +10,8 @@ const main = async () => { const repo = new JsonCrdtRepo({ wsUrl: 'wss://demo-iasd8921ondk0.jsonjoy.com/rpc', }); - const id = 'block-sync-ui-demo-text'; - const session = await repo.sessions.load({id: [id], remote: {}}); + const id = 'block-sync-ui-demo-text-3'; + const session = await repo.sessions.load({id: [id], make: {}, remote: {timeout: 1000}}); const model = session.model; const view = model.view(); if (typeof view !== 'string') model.api.root(''); diff --git a/src/json-crdt-repo/local/level/LevelLocalRepo.ts b/src/json-crdt-repo/local/level/LevelLocalRepo.ts index 18e89c16..a99e18ba 100644 --- a/src/json-crdt-repo/local/level/LevelLocalRepo.ts +++ b/src/json-crdt-repo/local/level/LevelLocalRepo.ts @@ -742,7 +742,6 @@ export class LevelLocalRepo implements LocalRepo { let needsReset = false; const didPush = await this.lockBlock(keyBase, async () => { const [tip, meta] = await Promise.all([this.readFrontierTip(keyBase), this.readMeta(keyBase)]); - if (meta.seq > -1 && (typeof req.cursor !== 'number' || req.cursor < meta.seq)) needsReset = true; let nextTick = meta.time + 1; cursor = meta.seq; if (tip) { From a49fb92312742cd70a49272b1c3516d9a7d230b5 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 3 Oct 2024 14:58:28 +0200 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20=F0=9F=90=9B=20pass=20concurrent=20c?= =?UTF-8?q?onstruction=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../local/level/LevelLocalRepo.ts | 21 +++++++-- src/json-crdt-repo/local/types.ts | 13 +++++ src/json-crdt-repo/session/EditSession.ts | 36 ++++++++++++-- .../__tests__/EditSession.sync.spec.ts | 47 +++++++++++++++++++ 4 files changed, 111 insertions(+), 6 deletions(-) diff --git a/src/json-crdt-repo/local/level/LevelLocalRepo.ts b/src/json-crdt-repo/local/level/LevelLocalRepo.ts index a99e18ba..89ec424c 100644 --- a/src/json-crdt-repo/local/level/LevelLocalRepo.ts +++ b/src/json-crdt-repo/local/level/LevelLocalRepo.ts @@ -715,6 +715,7 @@ export class LevelLocalRepo implements LocalRepo { } catch (error) { if (error instanceof Error && error.message === 'EXISTS') // TODO: make sure reset does not happen, if models are the same. + // TODO: Check for `req.time` in `_syncRead`. return await this._syncRead(id); throw error; } @@ -732,6 +733,17 @@ export class LevelLocalRepo implements LocalRepo { private async _syncMerge(req: LocalRepoSyncRequest): Promise { const {id, patches} = req; + let lastKnownTime: number = 0; + const reqTime = req.time + if (typeof reqTime === 'number') { + lastKnownTime = reqTime; + const firstPatch = patches?.[0]; + if (firstPatch?.getId()?.sid === SESSION.GLOBAL) + lastKnownTime = firstPatch.getId()!.time + firstPatch.span() - 1; + } else if (patches?.length) { + const firstPatchTime = patches?.[0]?.getId()?.time; + if (typeof firstPatchTime === 'number') lastKnownTime = firstPatchTime - 1; + } const keyBase = await this.blockKeyBase(id); if (!patches || !patches.length) throw new Error('EMPTY_BATCH'); const writtenPatches: Uint8Array[] = []; @@ -740,9 +752,12 @@ export class LevelLocalRepo implements LocalRepo { // TODO: Return correct response. // TODO: Check that remote state is in sync, too. let needsReset = false; + let cursorBehind = false; const didPush = await this.lockBlock(keyBase, async () => { const [tip, meta] = await Promise.all([this.readFrontierTip(keyBase), this.readMeta(keyBase)]); + if (meta.seq > -1 && (typeof req.cursor !== 'number' || req.cursor < meta.seq)) cursorBehind = true; let nextTick = meta.time + 1; + if (lastKnownTime < meta.time) needsReset = true; cursor = meta.seq; if (tip) { const tipTime = tip.getId()?.time ?? 0; @@ -785,7 +800,7 @@ export class LevelLocalRepo implements LocalRepo { }); if (!didPush && !needsReset) { const merge = await this.readFrontier0(keyBase); - return {cursor, merge}; + return {cursor, merge, cursorBehind}; } const remote = this.markDirtyAndSync(keyBase, id) .then(() => {}) @@ -795,9 +810,9 @@ export class LevelLocalRepo implements LocalRepo { }); if (needsReset) { const {cursor, model} = await this._syncRead0(keyBase); - return {cursor, model, remote}; + return {cursor, model, remote, cursorBehind}; } - return {cursor, remote}; + return {cursor, remote, cursorBehind}; } protected async readLocal0(keyBase: string): Promise<[model: Model, cursor: LevelLocalRepoCursor]> { diff --git a/src/json-crdt-repo/local/types.ts b/src/json-crdt-repo/local/types.ts index 45992f50..86327d92 100644 --- a/src/json-crdt-repo/local/types.ts +++ b/src/json-crdt-repo/local/types.ts @@ -87,6 +87,12 @@ export interface LocalRepoSyncRequest { */ id: BlockId; + /** + * Logical clock time of the local operations which the client has caught up + * to. + */ + time?: number; + /** * The last known cursor returned in the `.sync()` call response. The cursor * should be omitted in the first `.sync()` call, and then set to the value @@ -115,6 +121,13 @@ export interface LocalRepoSyncResponse { */ cursor: undefined | unknown; + /** + * Set to true if the client is behind the remote. When true, the client + * should call `.getIf()` after a short wait period to check if the remote + * is indeed ahead. + */ + cursorBehind?: boolean; + /** * Model snapshot that the client should reset its "start" state to. The * `Model` is sent when the *sync* call detects that the client is behind the diff --git a/src/json-crdt-repo/session/EditSession.ts b/src/json-crdt-repo/session/EditSession.ts index 5d9fbece..fedb10cc 100644 --- a/src/json-crdt-repo/session/EditSession.ts +++ b/src/json-crdt-repo/session/EditSession.ts @@ -87,7 +87,14 @@ export class EditSession> { const length = patches.length; // TODO: After async call check that sync state is still valid. New patches, might have been added. if (length || this.cursor === undefined) { - const res = await this.repo.sync({id: this.id, patches, cursor: this.cursor, session: this.session}); + let time = this.start.clock.time - 1; + const res = await this.repo.sync({ + id: this.id, + patches, + time, + cursor: this.cursor, + session: this.session + }); if (this._stopped) return null; // TODO: After sync call succeeds, remove the patches from the log. if (length) { @@ -96,16 +103,33 @@ export class EditSession> { if (lastId) this.log.advanceTo(lastId); this.start.applyBatch(patches); } - if (typeof res.cursor !== undefined) this.cursor = res.cursor; + // "cursor" shall not be returned from .sync() call. The cursor shall update + // only when remote model changes are received, during local .sync() write + // only the local model is updated. + if (typeof res.cursor !== undefined) { + this.cursor = res.cursor; + } if (res.model) { this._syncRace(() => { this.reset(res.model!); }); - } else if (res.merge) { + } else if (res.merge && res.merge) { this._syncRace(() => { this.merge(res.merge!); }); } + // if (res.cursorBehind) { + // setTimeout(async () => { + // if (!this._stopped) return; + // const get = await this.repo.getIf({ + // id: this.id, + // cursor: this.cursor + // }); + // if (!this._stopped) return; + // if (!get) return; + // this.reset(get.model); + // }, 50); + // } return {remote: res.remote}; } else { const res = await this.repo.getIf({id: this.id, time: this.model.clock.time - 1, cursor: this.cursor}); @@ -209,6 +233,12 @@ export class EditSession> { private onEvent = (event: LocalRepoEvent): void => { if (this._stopped) return; + if ((event as LocalRepoMergeEvent).merge) { + const cursor = (event as LocalRepoMergeEvent).cursor; + if (cursor !== undefined) { + this.cursor = cursor; + } + } if ((event as LocalRepoRebaseEvent).rebase) { if ((event as LocalRepoRebaseEvent).session === this.session) return; } diff --git a/src/json-crdt-repo/session/__tests__/EditSession.sync.spec.ts b/src/json-crdt-repo/session/__tests__/EditSession.sync.spec.ts index 974a9c6c..efad9e73 100644 --- a/src/json-crdt-repo/session/__tests__/EditSession.sync.spec.ts +++ b/src/json-crdt-repo/session/__tests__/EditSession.sync.spec.ts @@ -118,6 +118,38 @@ describe('sync', () => { await kit.stop(); }); + test('sessions created just after the first one, converges in state', async () => { + const kit = await setup(); + const schema = s.obj({a: s.con('a')}); + const {session: session1} = kit.sessions.make({id: kit.blockId, schema, session: 1}); + const {session: session2} = kit.sessions.make({id: kit.blockId, schema, session: 2}); + await session1.sync(); + await session2.sync(); + expect(session1.log.patches.size()).toBe(0); + session1.model.api.obj([]).set({b: 'b'}); + session1.model.api.obj([]).set({c: 'c'}); + await tick(5); + session1.model.api.obj([]).set({d: 'd'}); + const {session: session3} = kit.sessions.make({id: kit.blockId, pull: true, schema, session: 3}); + await tick(5); + session1.model.api.obj([]).set({e: 'e'}); + await tick(5); + session1.model.api.obj([]).set({f: 'f'}); + await session1.sync(); + await until(async () => { + try { + expect(session1.model.view()).toEqual(session3.model.view()); + return true; + } catch { + return false; + } + }); + await session1.dispose(); + await session2.dispose(); + await session3.dispose(); + await kit.stop(); + }); + test('sessions converge to the same view', async () => { const kit = await setup(); const schema = s.obj({a: s.con('a')}); @@ -141,7 +173,21 @@ describe('sync', () => { await until(async () => { try { expect(session1.model.view()).toEqual(session2.model.view()); + return true; + } catch { + return false; + } + }); + await until(async () => { + try { expect(session1.model.view()).toEqual(session3.model.view()); + return true; + } catch { + return false; + } + }); + await until(async () => { + try { expect(session1.model.view()).toEqual(session4.model.view()); return true; } catch { @@ -155,6 +201,7 @@ describe('sync', () => { await session1.dispose(); await session2.dispose(); await session3.dispose(); + await session4.dispose(); await kit.stop(); }); }); From cfe3d22c384e64ccbabe42022629757171ca9e7c Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 3 Oct 2024 15:02:02 +0200 Subject: [PATCH 6/9] =?UTF-8?q?fix:=20=F0=9F=90=9B=20capture=20async=20err?= =?UTF-8?q?or?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-repo/session/EditSession.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/json-crdt-repo/session/EditSession.ts b/src/json-crdt-repo/session/EditSession.ts index fedb10cc..61e62682 100644 --- a/src/json-crdt-repo/session/EditSession.ts +++ b/src/json-crdt-repo/session/EditSession.ts @@ -151,6 +151,8 @@ export class EditSession> { this._syncRace(() => { this.sync().then((error) => { this.onsyncerror?.(error); + }).catch(error => { + this.onsyncerror?.(error); }); }); } From 9a342d8e5f01139585ec46c743a3b5b095f9b169 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 3 Oct 2024 15:20:28 +0200 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20enable=20remote=20st?= =?UTF-8?q?ate=20out=20of=20sync=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-repo/session/EditSession.ts | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/json-crdt-repo/session/EditSession.ts b/src/json-crdt-repo/session/EditSession.ts index 61e62682..fe2f0f38 100644 --- a/src/json-crdt-repo/session/EditSession.ts +++ b/src/json-crdt-repo/session/EditSession.ts @@ -118,18 +118,18 @@ export class EditSession> { this.merge(res.merge!); }); } - // if (res.cursorBehind) { - // setTimeout(async () => { - // if (!this._stopped) return; - // const get = await this.repo.getIf({ - // id: this.id, - // cursor: this.cursor - // }); - // if (!this._stopped) return; - // if (!get) return; - // this.reset(get.model); - // }, 50); - // } + if (res.cursorBehind) { + setTimeout(async () => { + if (this._stopped) return; + const get = await this.repo.getIf({ + id: this.id, + cursor: this.cursor + }); + if (this._stopped) return; + if (!get) return; + this.reset(get.model); + }, 50); + } return {remote: res.remote}; } else { const res = await this.repo.getIf({id: this.id, time: this.model.clock.time - 1, cursor: this.cursor}); From be63cd3b7efc135d36e17f017f3d99e2130a32a0 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 3 Oct 2024 15:20:55 +0200 Subject: [PATCH 8/9] =?UTF-8?q?style:=20=F0=9F=92=84=20run=20Prettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__demos__/ui-text/main.tsx | 1 - .../local/level/LevelLocalRepo.ts | 5 ++--- src/json-crdt-repo/session/EditSession.ts | 16 +++++++++------- .../session/EditSessionFactory.ts | 17 +++++++++++------ 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/__demos__/ui-text/main.tsx b/src/__demos__/ui-text/main.tsx index 5ad6d2c1..ec88240e 100644 --- a/src/__demos__/ui-text/main.tsx +++ b/src/__demos__/ui-text/main.tsx @@ -16,7 +16,6 @@ const main = async () => { const view = model.view(); if (typeof view !== 'string') model.api.root(''); - const Demo: React.FC = () => { const [remote, setRemote] = React.useState(null); const ref = React.useRef(null); diff --git a/src/json-crdt-repo/local/level/LevelLocalRepo.ts b/src/json-crdt-repo/local/level/LevelLocalRepo.ts index 89ec424c..609c817e 100644 --- a/src/json-crdt-repo/local/level/LevelLocalRepo.ts +++ b/src/json-crdt-repo/local/level/LevelLocalRepo.ts @@ -734,12 +734,11 @@ export class LevelLocalRepo implements LocalRepo { private async _syncMerge(req: LocalRepoSyncRequest): Promise { const {id, patches} = req; let lastKnownTime: number = 0; - const reqTime = req.time + const reqTime = req.time; if (typeof reqTime === 'number') { lastKnownTime = reqTime; const firstPatch = patches?.[0]; - if (firstPatch?.getId()?.sid === SESSION.GLOBAL) - lastKnownTime = firstPatch.getId()!.time + firstPatch.span() - 1; + if (firstPatch?.getId()?.sid === SESSION.GLOBAL) lastKnownTime = firstPatch.getId()!.time + firstPatch.span() - 1; } else if (patches?.length) { const firstPatchTime = patches?.[0]?.getId()?.time; if (typeof firstPatchTime === 'number') lastKnownTime = firstPatchTime - 1; diff --git a/src/json-crdt-repo/session/EditSession.ts b/src/json-crdt-repo/session/EditSession.ts index fe2f0f38..5e10e127 100644 --- a/src/json-crdt-repo/session/EditSession.ts +++ b/src/json-crdt-repo/session/EditSession.ts @@ -93,7 +93,7 @@ export class EditSession> { patches, time, cursor: this.cursor, - session: this.session + session: this.session, }); if (this._stopped) return null; // TODO: After sync call succeeds, remove the patches from the log. @@ -123,7 +123,7 @@ export class EditSession> { if (this._stopped) return; const get = await this.repo.getIf({ id: this.id, - cursor: this.cursor + cursor: this.cursor, }); if (this._stopped) return; if (!get) return; @@ -149,11 +149,13 @@ export class EditSession> { public syncLog(): void { if (!this.log.patches.size()) return; this._syncRace(() => { - this.sync().then((error) => { - this.onsyncerror?.(error); - }).catch(error => { - this.onsyncerror?.(error); - }); + this.sync() + .then((error) => { + this.onsyncerror?.(error); + }) + .catch((error) => { + this.onsyncerror?.(error); + }); }); } diff --git a/src/json-crdt-repo/session/EditSessionFactory.ts b/src/json-crdt-repo/session/EditSessionFactory.ts index 39b73b7b..11af3d96 100644 --- a/src/json-crdt-repo/session/EditSessionFactory.ts +++ b/src/json-crdt-repo/session/EditSessionFactory.ts @@ -61,14 +61,19 @@ export class EditSessionFactory { const {model, cursor} = await (typeof timeoutMs === 'number' ? timeout(timeoutMs, repo.pull(id)) : repo.pull(id)); - if (remote.throwIf === 'exists') throw new Error('EXISTS'); - const session = new EditSession(repo, id, model, cursor, opts.session); - session.log.end.api.autoFlush(); - return session; - } catch (error) { + if (remote.throwIf === 'exists') throw new Error('EXISTS'); + const session = new EditSession(repo, id, model, cursor, opts.session); + session.log.end.api.autoFlush(); + return session; + } catch (error) { if (!!error && typeof error === 'object' && (error as Record).message === 'TIMEOUT') { if (!opts.make) throw error; - } else if (!!error && typeof error === 'object' && ((error as Record).message === 'NOT_FOUND' || (error as Record).code === 'NOT_FOUND')) { + } else if ( + !!error && + typeof error === 'object' && + ((error as Record).message === 'NOT_FOUND' || + (error as Record).code === 'NOT_FOUND') + ) { if (remote.throwIf === 'missing') throw error; } else throw error; } From cc43257e8fb168a675c5fb5c94acebe2624fd506 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 3 Oct 2024 15:27:24 +0200 Subject: [PATCH 9/9] =?UTF-8?q?style:=20=F0=9F=92=84=20fix=20linter=20warn?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt-repo/session/EditSession.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/json-crdt-repo/session/EditSession.ts b/src/json-crdt-repo/session/EditSession.ts index 5e10e127..eb4b161c 100644 --- a/src/json-crdt-repo/session/EditSession.ts +++ b/src/json-crdt-repo/session/EditSession.ts @@ -87,7 +87,7 @@ export class EditSession> { const length = patches.length; // TODO: After async call check that sync state is still valid. New patches, might have been added. if (length || this.cursor === undefined) { - let time = this.start.clock.time - 1; + const time = this.start.clock.time - 1; const res = await this.repo.sync({ id: this.id, patches,