From 46f15f34c431ed8caa5ca6844ea5ff435ac21adf Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 25 Nov 2023 16:14:51 +0100 Subject: [PATCH 1/3] =?UTF-8?q?feat(json-crdt):=20=F0=9F=8E=B8=20inform=20?= =?UTF-8?q?node=20event=20system=20on=20deletion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt/model/Model.ts | 3 +++ src/json-crdt/model/api/events/NodeEvents.ts | 25 +++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/json-crdt/model/Model.ts b/src/json-crdt/model/Model.ts index 034f013f91..1ff6c4ab15 100644 --- a/src/json-crdt/model/Model.ts +++ b/src/json-crdt/model/Model.ts @@ -21,6 +21,7 @@ import {AvlMap} from '../../util/trees/avl/AvlMap'; import type {JsonNode, JsonNodeView} from '../nodes/types'; import type {Printable} from '../../util/print/types'; import type {NodeBuilder} from '../../json-crdt-patch'; +import type {NodeApi} from './api/nodes'; export const UNDEFINED = new ConNode(ORIGIN, undefined); @@ -291,6 +292,8 @@ export class Model implements Printable { if (isSystemNode) return; const node = this.index.get(value); if (!node) return; + const api = node.api; + if (api) (api as NodeApi).events.onDelete(); node.children((child) => this.deleteNodeTree(child.id)); this.index.del(value); } diff --git a/src/json-crdt/model/api/events/NodeEvents.ts b/src/json-crdt/model/api/events/NodeEvents.ts index 190161c4e1..c9b26e02e4 100644 --- a/src/json-crdt/model/api/events/NodeEvents.ts +++ b/src/json-crdt/model/api/events/NodeEvents.ts @@ -21,9 +21,12 @@ export interface NodeEventMap { } class ChangesFanOut extends FanOut> { + /** @ignore */ private _v: JsonNodeView | undefined = undefined; + /** @ignore */ private _u: FanOutUnsubscribe | undefined = undefined; + /** @ignore */ constructor(private readonly api: NodeApi) { super(); } @@ -45,10 +48,20 @@ class ChangesFanOut extends FanOut @@ -101,6 +114,16 @@ export class NodeEvents super.off(type, listener, options); } + /** + * Called when this node is deleted. + * + * @internal + * @ignore + */ + public onDelete() { + this.changes.dispose(); + } + // ---------------------------------------------------------------- SyncStore public readonly subscribe = (callback: () => void): SyncStoreUnsubscribe => { From a220c2ad14628b4014309140fb8ce88435e95659 Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 25 Nov 2023 16:29:44 +0100 Subject: [PATCH 2/3] =?UTF-8?q?test(json-crdt):=20=F0=9F=92=8D=20check=20e?= =?UTF-8?q?vents=20stop=20flowing=20for=20deleted=20nodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/api/__tests__/NodeEvents.spec.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/json-crdt/model/api/__tests__/NodeEvents.spec.ts diff --git a/src/json-crdt/model/api/__tests__/NodeEvents.spec.ts b/src/json-crdt/model/api/__tests__/NodeEvents.spec.ts new file mode 100644 index 0000000000..e35dc4bd5e --- /dev/null +++ b/src/json-crdt/model/api/__tests__/NodeEvents.spec.ts @@ -0,0 +1,25 @@ +import {Model} from "../.."; + +test('does not fire events after node is deleted', () => { + const model = Model.withLogicalClock(); + model.api.root({ + foo: { + bar: { + baz: 'asdf', + }, + }, + }); + const bar = model.api.obj(['foo', 'bar']); + let cnt = 0; + bar.events.changes.listen(() => { + cnt++; + }); + expect(cnt).toBe(0); + bar.set({ + gg: 'wp', + }); + expect(cnt).toBe(1); + model.api.obj(['foo']).del(['bar']); + model.api.obj(['foo']).set({'gl': 'hf'}); + expect(cnt).toBe(1); +}); From 46e75f817ef9ed862160046dd29cf960f02b62dc Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 25 Nov 2023 16:30:15 +0100 Subject: [PATCH 3/3] =?UTF-8?q?style(json-crdt):=20=F0=9F=92=84=20run=20Pr?= =?UTF-8?q?ettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-crdt/model/api/__tests__/NodeEvents.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/json-crdt/model/api/__tests__/NodeEvents.spec.ts b/src/json-crdt/model/api/__tests__/NodeEvents.spec.ts index e35dc4bd5e..2ce7fc3d19 100644 --- a/src/json-crdt/model/api/__tests__/NodeEvents.spec.ts +++ b/src/json-crdt/model/api/__tests__/NodeEvents.spec.ts @@ -1,4 +1,4 @@ -import {Model} from "../.."; +import {Model} from '../..'; test('does not fire events after node is deleted', () => { const model = Model.withLogicalClock(); @@ -20,6 +20,6 @@ test('does not fire events after node is deleted', () => { }); expect(cnt).toBe(1); model.api.obj(['foo']).del(['bar']); - model.api.obj(['foo']).set({'gl': 'hf'}); + model.api.obj(['foo']).set({gl: 'hf'}); expect(cnt).toBe(1); });