11import { deepEqual } from '@jsonjoy.com/util/lib/json-equal/deepEqual' ;
22import { cmpUint8Array } from '@jsonjoy.com/buffers/lib/cmpUint8Array' ;
3- import { type ITimespanStruct , type ITimestampStruct , type Patch , PatchBuilder } from '../json-crdt-patch' ;
3+ import {
4+ type ITimespanStruct ,
5+ type ITimestampStruct ,
6+ NodeBuilder ,
7+ nodes ,
8+ type Patch ,
9+ PatchBuilder ,
10+ Timestamp ,
11+ tss ,
12+ } from '../json-crdt-patch' ;
413import { ArrNode , BinNode , ConNode , ObjNode , StrNode , ValNode , VecNode , type JsonNode } from '../json-crdt/nodes' ;
514import * as str from '../util/diff/str' ;
615import * as bin from '../util/diff/bin' ;
716import * as line from '../util/diff/line' ;
817import { structHashCrdt } from '../json-hash/structHashCrdt' ;
9- import { structHash } from '../json-hash' ;
18+ import { structHashSchema } from '../json-hash/structHashSchema ' ;
1019import type { Model } from '../json-crdt/model' ;
1120
1221export class DiffError extends Error {
@@ -16,7 +25,7 @@ export class DiffError extends Error {
1625}
1726
1827export class JsonCrdtDiff {
19- protected builder : PatchBuilder ;
28+ public builder : PatchBuilder ;
2029
2130 public constructor ( protected readonly model : Model < any > ) {
2231 this . builder = new PatchBuilder ( model . clock . clone ( ) ) ;
@@ -47,60 +56,67 @@ export class JsonCrdtDiff {
4756 }
4857
4958 protected diffArr ( src : ArrNode , dst : unknown [ ] ) : void {
59+ if ( src . size ( ) === 0 ) {
60+ const length = dst . length ;
61+ if ( length === 0 ) return ;
62+ let after : ITimestampStruct = src . id ;
63+ for ( let i = 0 ; i < length ; i ++ ) after = this . builder . insArr ( src . id , after , [ this . buildView ( dst [ i ] ) ] ) ;
64+ return ;
65+ } else if ( dst . length === 0 ) {
66+ const spans : ITimespanStruct [ ] = [ ] ;
67+ for ( const chunk of src . chunks ( ) ) {
68+ if ( chunk . del ) continue ;
69+ const id = chunk . id ;
70+ spans . push ( tss ( id . sid , id . time , chunk . span ) ) ;
71+ }
72+ if ( spans . length ) this . builder . del ( src . id , spans ) ;
73+ return ;
74+ }
5075 const srcLines : string [ ] = [ ] ;
51- src . children ( ( node ) => {
52- srcLines . push ( structHashCrdt ( node ) ) ;
53- } ) ;
76+ src . children ( ( node ) => srcLines . push ( structHashCrdt ( node ) ) ) ;
5477 const dstLines : string [ ] = [ ] ;
5578 const dstLength = dst . length ;
56- for ( let i = 0 ; i < dstLength ; i ++ ) dstLines . push ( structHash ( dst [ i ] ) ) ;
79+ for ( let i = 0 ; i < dstLength ; i ++ ) dstLines . push ( structHashSchema ( dst [ i ] ) ) ;
5780 const linePatch = line . diff ( srcLines , dstLines ) ;
5881 if ( ! linePatch . length ) return ;
5982 const inserts : [ after : ITimestampStruct , views : unknown [ ] ] [ ] = [ ] ;
6083 const deletes : ITimespanStruct [ ] = [ ] ;
61- const patchLength = linePatch . length ;
62- for ( let i = patchLength - 1 ; i >= 0 ; i -- ) {
63- const [ type , posSrc , posDst ] = linePatch [ i ] ;
64- switch ( type ) {
65- case line . LINE_PATCH_OP_TYPE . EQL :
66- break ;
67- case line . LINE_PATCH_OP_TYPE . INS : {
68- const view = dst [ posDst ] ;
69- const after = posSrc >= 0 ? src . find ( posSrc ) : src . id ;
70- if ( ! after ) throw new DiffError ( ) ;
71- inserts . push ( [ after , [ view ] ] ) ;
72- break ;
73- }
74- case line . LINE_PATCH_OP_TYPE . DEL : {
75- const span = src . findInterval ( posSrc , 1 ) ;
76- if ( ! span || ! span . length ) throw new DiffError ( ) ;
77- deletes . push ( ...span ) ;
78- break ;
79- }
80- case line . LINE_PATCH_OP_TYPE . MIX : {
81- const view = dst [ posDst ] ;
82- try {
83- this . diffAny ( src . getNode ( posSrc ) ! , view ) ;
84- } catch ( error ) {
85- if ( error instanceof DiffError ) {
86- const span = src . findInterval ( posSrc , 1 ) ! ;
87- deletes . push ( ...span ) ;
88- const after = posSrc ? src . find ( posSrc - 1 ) : src . id ;
89- if ( ! after ) throw new DiffError ( ) ;
90- inserts . push ( [ after , [ view ] ] ) ;
91- } else throw error ;
92- }
84+ line . apply (
85+ linePatch ,
86+ ( posSrc ) => {
87+ const span = src . findInterval ( posSrc , 1 ) ;
88+ if ( ! span || ! span . length ) throw new DiffError ( ) ;
89+ deletes . push ( ...span ) ;
90+ } ,
91+ ( posSrc , posDst ) => {
92+ const view = dst [ posDst ] ;
93+ const after = posSrc >= 0 ? src . find ( posSrc ) : src . id ;
94+ if ( ! after ) throw new DiffError ( ) ;
95+ inserts . push ( [ after , [ view ] ] ) ;
96+ } ,
97+ ( posSrc , posDst ) => {
98+ const view = dst [ posDst ] ;
99+ try {
100+ this . diffAny ( src . getNode ( posSrc ) ! , view ) ;
101+ } catch ( error ) {
102+ if ( error instanceof DiffError ) {
103+ const span = src . findInterval ( posSrc , 1 ) ! ;
104+ deletes . push ( ...span ) ;
105+ const after = posSrc ? src . find ( posSrc - 1 ) : src . id ;
106+ if ( ! after ) throw new DiffError ( ) ;
107+ inserts . push ( [ after , [ view ] ] ) ;
108+ } else throw error ;
93109 }
94- }
95- }
110+ } ,
111+ ) ;
96112 const builder = this . builder ;
97113 const length = inserts . length ;
98114 for ( let i = 0 ; i < length ; i ++ ) {
99115 const [ after , views ] = inserts [ i ] ;
100116 builder . insArr (
101117 src . id ,
102118 after ,
103- views . map ( ( view ) => builder . json ( view ) ) ,
119+ views . map ( ( view ) => this . buildView ( view ) ) ,
104120 ) ;
105121 }
106122 if ( deletes . length ) builder . del ( src . id , deletes ) ;
@@ -131,7 +147,8 @@ export class JsonCrdtDiff {
131147 }
132148 }
133149 }
134- inserts . push ( [ key , src . get ( key ) instanceof ConNode ? builder . con ( dstValue ) : builder . constOrJson ( dstValue ) ] ) ;
150+ inserts . push ( [ key , this . buildConView ( dstValue ) ] ) ;
151+ // inserts.push([key, src.get(key) instanceof ConNode ? builder.con(dstValue) : this.buildConView(dstValue)]);
135152 }
136153 if ( inserts . length ) builder . insObj ( src . id , inserts ) ;
137154 }
@@ -149,11 +166,11 @@ export class JsonCrdtDiff {
149166 if ( id ) {
150167 const child = index . get ( id ) ;
151168 const isDeleted = ! child || ( child instanceof ConNode && child . val === void 0 ) ;
152- if ( isDeleted ) return ;
169+ if ( isDeleted ) continue ;
153170 edits . push ( [ i , builder . con ( void 0 ) ] ) ;
154171 }
155172 }
156- for ( let i = 0 ; i < min ; i ++ ) {
173+ CHILDREN: for ( let i = 0 ; i < min ; i ++ ) {
157174 const value = dst [ i ] ;
158175 const child = src . get ( i ) ;
159176 if ( child ) {
@@ -163,10 +180,15 @@ export class JsonCrdtDiff {
163180 } catch ( error ) {
164181 if ( ! ( error instanceof DiffError ) ) throw error ;
165182 }
183+ if ( child instanceof ConNode && typeof value !== 'object' ) {
184+ const valueId = builder . con ( value ) ;
185+ edits . push ( [ i , valueId ] ) ;
186+ continue CHILDREN;
187+ }
166188 }
167- edits . push ( [ i , builder . constOrJson ( value ) ] ) ;
189+ edits . push ( [ i , this . buildConView ( value ) ] ) ;
168190 }
169- for ( let i = srcLength ; i < dstLength ; i ++ ) edits . push ( [ i , builder . constOrJson ( dst [ i ] ) ] ) ;
191+ for ( let i = srcLength ; i < dstLength ; i ++ ) edits . push ( [ i , this . buildConView ( dst [ i ] ) ] ) ;
170192 if ( edits . length ) builder . insVec ( src . id , edits ) ;
171193 }
172194
@@ -176,30 +198,44 @@ export class JsonCrdtDiff {
176198 } catch ( error ) {
177199 if ( error instanceof DiffError ) {
178200 const builder = this . builder ;
179- builder . setVal ( src . id , builder . constOrJson ( dst ) ) ;
201+ builder . setVal ( src . id , this . buildConView ( dst ) ) ;
180202 } else throw error ;
181203 }
182204 }
183205
184206 protected diffAny ( src : JsonNode , dst : unknown ) : void {
185207 if ( src instanceof ConNode ) {
208+ if ( dst instanceof nodes . con ) dst = dst . raw ;
186209 const val = src . val ;
187- if ( val !== dst && ! deepEqual ( src . val , dst ) ) throw new DiffError ( ) ;
210+ if (
211+ val !== dst &&
212+ ( ( val instanceof Timestamp && ! ( dst instanceof Timestamp ) ) ||
213+ ( ! ( val instanceof Timestamp ) && dst instanceof Timestamp ) ||
214+ ! deepEqual ( src . val , dst ) )
215+ )
216+ throw new DiffError ( ) ;
188217 } else if ( src instanceof StrNode ) {
218+ if ( dst instanceof nodes . str ) dst = dst . raw ;
189219 if ( typeof dst !== 'string' ) throw new DiffError ( ) ;
190220 this . diffStr ( src , dst ) ;
191221 } else if ( src instanceof ObjNode ) {
222+ if ( dst instanceof nodes . obj ) dst = dst . opt ? { ...dst . obj , ...dst . opt } : dst . obj ;
223+ if ( dst instanceof NodeBuilder ) throw new DiffError ( ) ;
192224 if ( ! dst || typeof dst !== 'object' || Array . isArray ( dst ) ) throw new DiffError ( ) ;
193225 this . diffObj ( src , dst as Record < string , unknown > ) ;
194226 } else if ( src instanceof ValNode ) {
227+ if ( dst instanceof nodes . val ) dst = dst . value ;
195228 this . diffVal ( src , dst ) ;
196229 } else if ( src instanceof ArrNode ) {
230+ if ( dst instanceof nodes . arr ) dst = dst . arr ;
197231 if ( ! Array . isArray ( dst ) ) throw new DiffError ( ) ;
198232 this . diffArr ( src , dst as unknown [ ] ) ;
199233 } else if ( src instanceof VecNode ) {
234+ if ( dst instanceof nodes . vec ) dst = dst . value ;
200235 if ( ! Array . isArray ( dst ) ) throw new DiffError ( ) ;
201236 this . diffVec ( src , dst as unknown [ ] ) ;
202237 } else if ( src instanceof BinNode ) {
238+ if ( dst instanceof nodes . bin ) dst = dst . raw ;
203239 if ( ! ( dst instanceof Uint8Array ) ) throw new DiffError ( ) ;
204240 this . diffBin ( src , dst ) ;
205241 } else {
@@ -211,4 +247,18 @@ export class JsonCrdtDiff {
211247 this . diffAny ( src , dst ) ;
212248 return this . builder . flush ( ) ;
213249 }
250+
251+ protected buildView ( dst : unknown ) : ITimestampStruct {
252+ const builder = this . builder ;
253+ if ( dst instanceof Timestamp ) return builder . con ( dst ) ;
254+ if ( dst instanceof nodes . con ) return builder . con ( dst . raw ) ;
255+ return builder . json ( dst ) ;
256+ }
257+
258+ protected buildConView ( dst : unknown ) : ITimestampStruct {
259+ const builder = this . builder ;
260+ if ( dst instanceof Timestamp ) return builder . con ( dst ) ;
261+ if ( dst instanceof nodes . con ) return builder . con ( dst . raw ) ;
262+ return builder . constOrJson ( dst ) ;
263+ }
214264}
0 commit comments