lib0
Version:
> Monorepo of isomorphic utility functions
1 lines • 137 kB
Source Map (JSON)
{"version":3,"file":"delta.cjs","sources":["../delta/delta.js"],"sourcesContent":["/**\n * @beta this API is about to change\n *\n * ## Mutability\n *\n * Deltas are mutable by default. But references are often shared, by marking a Delta as \"done\". You\n * may only modify deltas by applying other deltas to them. Casting a Delta to a DeltaBuilder\n * manually, will likely modify \"shared\" state.\n */\n\nimport * as list from '../list.js'\nimport * as object from '../object.js'\nimport * as equalityTrait from '../trait/equality.js'\nimport * as fingerprintTrait from '../trait/fingerprint.js'\nimport * as arr from '../array.js'\nimport * as fun from '../function.js'\nimport * as s from '../schema.js'\nimport * as error from '../error.js'\nimport * as math from '../math.js'\nimport * as rabin from '../hash/rabin.js'\nimport * as encoding from '../encoding.js'\nimport * as buffer from '../buffer.js'\nimport * as patience from '../diff/patience.js'\nimport * as prng from '../prng.js'\n\n/**\n * @typedef {{\n * insert?: string[]\n * insertAt?: number\n * delete?: string[]\n * deleteAt?: number\n * format?: Record<string,string[]>\n * formatAt?: number\n * }} Attribution\n */\n\n/**\n * @type {s.Schema<Attribution>}\n */\nexport const $attribution = s.$object({\n insert: s.$array(s.$string).optional,\n insertAt: s.$number.optional,\n delete: s.$array(s.$string).optional,\n deleteAt: s.$number.optional,\n format: s.$record(s.$string, s.$array(s.$string)).optional,\n formatAt: s.$number.optional\n})\n\n/**\n * @typedef {s.Unwrap<$anyOp>} DeltaOps\n */\n\n/**\n * @typedef {{ [key: string]: any }} FormattingAttributes\n */\n\n/**\n * @typedef {{\n * type: 'delta',\n * name?: string,\n * attrs?: { [Key in string|number]: DeltaAttrOpJSON },\n * children?: Array<DeltaListOpJSON>\n * }} DeltaJSON\n */\n\n/**\n * @typedef {{ type: 'insert', insert: string|Array<any>, format?: { [key: string]: any }, attribution?: Attribution } | { delete: number } | { type: 'retain', retain: number, format?: { [key:string]: any }, attribution?: Attribution } | { type: 'modify', value: object }} DeltaListOpJSON\n */\n\n/**\n * @typedef {{ type: 'insert', value: any, prevValue?: any, attribution?: Attribution } | { type: 'delete', prevValue?: any, attribution?: Attribution } | { type: 'modify', value: DeltaJSON }} DeltaAttrOpJSON\n */\n\n/**\n * @typedef {TextOp|InsertOp<any>|DeleteOp|RetainOp|ModifyOp<any>} ChildrenOpAny\n */\n\n/**\n * @typedef {AttrInsertOp<any>|AttrDeleteOp<any>|AttrModifyOp} AttrOpAny\n */\n\n/**\n * @typedef {ChildrenOpAny|AttrOpAny} _OpAny\n */\n\n/**\n * @type {s.Schema<DeltaAttrOpJSON>}\n */\nexport const $deltaMapChangeJson = s.$union(\n s.$object({ type: s.$literal('insert'), value: s.$any, prevValue: s.$any.optional, attribution: $attribution.optional }),\n s.$object({ type: s.$literal('modify'), value: s.$any }),\n s.$object({ type: s.$literal('delete'), prevValue: s.$any.optional, attribution: $attribution.optional })\n)\n\n/**\n * @template {{[key:string]: any} | null} Attrs\n * @param {Attrs} attrs\n * @return {Attrs}\n */\nconst _cloneAttrs = attrs => attrs == null ? attrs : { ...attrs }\n/**\n * @template {any} MaybeDelta\n * @param {MaybeDelta} maybeDelta\n * @return {MaybeDelta}\n */\nconst _markMaybeDeltaAsDone = maybeDelta => $deltaAny.check(maybeDelta) ? /** @type {MaybeDelta} */ (maybeDelta.done()) : maybeDelta\n\nexport class TextOp extends list.ListNode {\n /**\n * @param {string} insert\n * @param {FormattingAttributes|null} format\n * @param {Attribution?} attribution\n */\n constructor (insert, format, attribution) {\n super()\n // Whenever this is modified, make sure to clear _fingerprint\n /**\n * @readonly\n * @type {string}\n */\n this.insert = insert\n /**\n * @readonly\n * @type {FormattingAttributes|null}\n */\n this.format = format\n this.attribution = attribution\n /**\n * @type {string?}\n */\n this._fingerprint = null\n }\n\n /**\n * @param {string} newVal\n */\n _updateInsert (newVal) {\n // @ts-ignore\n this.insert = newVal\n this._fingerprint = null\n }\n\n /**\n * @return {'insert'}\n */\n get type () {\n return 'insert'\n }\n\n get length () {\n return this.insert.length\n }\n\n get fingerprint () {\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeVarUint(encoder, 0) // textOp type: 0\n encoding.writeVarString(encoder, this.insert)\n encoding.writeAny(encoder, this.format)\n })))\n }\n\n /**\n * Remove a part of the operation (similar to Array.splice)\n *\n * @param {number} offset\n * @param {number} len\n */\n _splice (offset, len) {\n this._fingerprint = null\n // @ts-ignore\n this.insert = this.insert.slice(0, offset) + this.insert.slice(offset + len)\n return this\n }\n\n /**\n * @return {DeltaListOpJSON}\n */\n toJSON () {\n const { insert, format, attribution } = this\n return object.assign(/** @type {{type: 'insert', insert: string}} */ ({ type: 'insert', insert }), format != null ? { format } : ({}), attribution != null ? { attribution } : ({}))\n }\n\n /**\n * @param {TextOp} other\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.format, other.format) && fun.equalityDeep(this.attribution, other.attribution)\n }\n\n /**\n * @return {TextOp}\n */\n clone (start = 0, end = this.length) {\n return new TextOp(this.insert.slice(start, end), _cloneAttrs(this.format), _cloneAttrs(this.attribution))\n }\n}\n\n/**\n * @template {fingerprintTrait.Fingerprintable} ArrayContent\n */\nexport class InsertOp extends list.ListNode {\n /**\n * @param {Array<ArrayContent>} insert\n * @param {FormattingAttributes|null} format\n * @param {Attribution?} attribution\n */\n constructor (insert, format, attribution) {\n super()\n /**\n * @readonly\n * @type {Array<ArrayContent>}\n */\n this.insert = insert\n /**\n * @readonly\n * @type {FormattingAttributes?}\n */\n this.format = format\n /**\n * @readonly\n * @type {Attribution?}\n */\n this.attribution = attribution\n /**\n * @type {string?}\n */\n this._fingerprint = null\n }\n\n /**\n * @param {ArrayContent} newVal\n */\n _updateInsert (newVal) {\n // @ts-ignore\n this.insert = newVal\n this._fingerprint = null\n }\n\n /**\n * @return {'insert'}\n */\n get type () {\n return 'insert'\n }\n\n get length () {\n return this.insert.length\n }\n\n /**\n * @param {number} i\n * @return {Extract<ArrayContent,DeltaAny>}\n */\n _modValue (i) {\n /**\n * @type {any}\n */\n let d = this.insert[i]\n this._fingerprint = null\n $deltaAny.expect(d)\n if (d.isDone) {\n // @ts-ignore\n this.insert[i] = (d = clone(d))\n return d\n }\n return d\n }\n\n get fingerprint () {\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeVarUint(encoder, 1) // insertOp type: 1\n encoding.writeVarUint(encoder, this.insert.length)\n this.insert.forEach(ins => {\n encoding.writeVarString(encoder, fingerprintTrait.fingerprint(ins))\n })\n encoding.writeAny(encoder, this.format)\n })))\n }\n\n /**\n * Remove a part of the operation (similar to Array.splice)\n *\n * @param {number} offset\n * @param {number} len\n */\n _splice (offset, len) {\n this._fingerprint = null\n this.insert.splice(offset, len)\n return this\n }\n\n /**\n * @return {DeltaListOpJSON}\n */\n toJSON () {\n const { insert, format, attribution } = this\n return object.assign({ type: /** @type {'insert'} */ ('insert'), insert: insert.map(ins => $deltaAny.check(ins) ? ins.toJSON() : ins) }, format ? { format } : ({}), attribution != null ? { attribution } : ({}))\n }\n\n /**\n * @param {InsertOp<ArrayContent>} other\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.format, other.format) && fun.equalityDeep(this.attribution, other.attribution)\n }\n\n /**\n * @return {InsertOp<ArrayContent>}\n */\n clone (start = 0, end = this.length) {\n return new InsertOp(this.insert.slice(start, end).map(_markMaybeDeltaAsDone), _cloneAttrs(this.format), _cloneAttrs(this.attribution))\n }\n}\n\n/**\n * @template {fingerprintTrait.Fingerprintable} [Children=never]\n * @template {string} [Text=never]\n */\nexport class DeleteOp extends list.ListNode {\n /**\n * @param {number} len\n */\n constructor (len) {\n super()\n this.delete = len\n /**\n * @type {(Children|Text) extends never ? null : (Delta<any,{},Children,Text>?)}\n */\n this.prevValue = null\n /**\n * @type {string|null}\n */\n this._fingerprint = null\n }\n\n /**\n * @return {'delete'}\n */\n get type () {\n return 'delete'\n }\n\n get length () {\n return 0\n }\n\n get fingerprint () {\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeVarUint(encoder, 2) // deleteOp type: 2\n encoding.writeVarUint(encoder, this.delete)\n })))\n }\n\n /**\n * Remove a part of the operation (similar to Array.splice)\n *\n * @param {number} _offset\n * @param {number} len\n */\n _splice (_offset, len) {\n this.prevValue = /** @type {any} */ (this.prevValue?.slice(_offset, len) || null)\n this._fingerprint = null\n this.delete -= len\n return this\n }\n\n /**\n * @return {DeltaListOpJSON}\n */\n toJSON () {\n return { delete: this.delete }\n }\n\n /**\n * @param {DeleteOp} other\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n return this.delete === other.delete\n }\n\n clone (start = 0, end = this.delete) {\n return new DeleteOp(end - start)\n }\n}\n\nexport class RetainOp extends list.ListNode {\n /**\n * @param {number} retain\n * @param {FormattingAttributes|null} format\n * @param {Attribution?} attribution\n */\n constructor (retain, format, attribution) {\n super()\n /**\n * @readonly\n * @type {number}\n */\n this.retain = retain\n /**\n * @readonly\n * @type {FormattingAttributes?}\n */\n this.format = format\n /**\n * @readonly\n * @type {Attribution?}\n */\n this.attribution = attribution\n /**\n * @type {string|null}\n */\n this._fingerprint = null\n }\n\n /**\n * @return {'retain'}\n */\n get type () {\n return 'retain'\n }\n\n get length () {\n return this.retain\n }\n\n get fingerprint () {\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeVarUint(encoder, 3) // retainOp type: 3\n encoding.writeVarUint(encoder, this.retain)\n encoding.writeAny(encoder, this.format)\n })))\n }\n\n /**\n * Remove a part of the operation (similar to Array.splice)\n *\n * @param {number} _offset\n * @param {number} len\n */\n _splice (_offset, len) {\n // @ts-ignore\n this.retain -= len\n this._fingerprint = null\n return this\n }\n\n /**\n * @return {DeltaListOpJSON}\n */\n toJSON () {\n const { retain, format, attribution } = this\n return object.assign({ type: /** @type {'retain'} */ ('retain'), retain }, format ? { format } : {}, attribution != null ? { attribution } : {})\n }\n\n /**\n * @param {RetainOp} other\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n return this.retain === other.retain && fun.equalityDeep(this.format, other.format) && fun.equalityDeep(this.attribution, other.attribution)\n }\n\n clone (start = 0, end = this.retain) {\n return new RetainOp(end - start, _cloneAttrs(this.format), _cloneAttrs(this.attribution))\n }\n}\n\n/**\n * Delta that can be applied on a YType Embed\n *\n * @template {DeltaAny} [DTypes=DeltaAny]\n */\nexport class ModifyOp extends list.ListNode {\n /**\n * @param {DTypes} delta\n * @param {FormattingAttributes|null} format\n * @param {Attribution?} attribution\n */\n constructor (delta, format, attribution) {\n super()\n /**\n * @readonly\n * @type {DTypes}\n */\n this.value = delta\n /**\n * @readonly\n * @type {FormattingAttributes?}\n */\n this.format = format\n /**\n * @readonly\n * @type {Attribution?}\n */\n this.attribution = attribution\n /**\n * @type {string|null}\n */\n this._fingerprint = null\n }\n\n /**\n * @return {'modify'}\n */\n get type () {\n return 'modify'\n }\n\n get length () {\n return 1\n }\n\n /**\n * @type {DeltaBuilderAny}\n */\n get _modValue () {\n /**\n * @type {any}\n */\n const d = this.value\n this._fingerprint = null\n if (d.isDone) {\n // @ts-ignore\n return (this.value = clone(d))\n }\n return d\n }\n\n get fingerprint () {\n // don't cache fingerprint because we don't know when delta changes\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeVarUint(encoder, 4) // modifyOp type: 4\n encoding.writeVarString(encoder, this.value.fingerprint)\n encoding.writeAny(encoder, this.format)\n })))\n }\n\n /**\n * Remove a part of the operation (similar to Array.splice)\n *\n * @param {number} _offset\n * @param {number} _len\n */\n _splice (_offset, _len) {\n return this\n }\n\n /**\n * @return {DeltaListOpJSON}\n */\n toJSON () {\n const { value, attribution, format } = this\n return object.assign({ type: /** @type {'modify'} */ ('modify'), value: value.toJSON() }, format ? { format } : {}, attribution != null ? { attribution } : {})\n }\n\n /**\n * @param {ModifyOp<any>} other\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n return this.value[equalityTrait.EqualityTraitSymbol](other.value) && fun.equalityDeep(this.format, other.format) && fun.equalityDeep(this.attribution, other.attribution)\n }\n\n /**\n * @return {ModifyOp<DTypes>}\n */\n clone () {\n return new ModifyOp(/** @type {DTypes} */ (this.value.done()), _cloneAttrs(this.format), _cloneAttrs(this.attribution))\n }\n}\n\n/**\n * @template {fingerprintTrait.Fingerprintable} V\n * @template {string|number} [K=any]\n */\nexport class AttrInsertOp {\n /**\n * @param {K} key\n * @param {V} value\n * @param {V|undefined} prevValue\n * @param {Attribution?} attribution\n */\n constructor (key, value, prevValue, attribution) {\n /**\n * @readonly\n * @type {K}\n */\n this.key = key\n /**\n * @readonly\n * @type {V}\n */\n this.value = value\n /**\n * @readonly\n * @type {V|undefined}\n */\n this.prevValue = prevValue\n /**\n * @readonly\n * @type {Attribution?}\n */\n this.attribution = attribution\n /**\n * @type {string|null}\n */\n this._fingerprint = null\n }\n\n /**\n * @return {'insert'}\n */\n get type () { return 'insert' }\n\n /**\n * @type {DeltaBuilderAny}\n */\n get _modValue () {\n /**\n * @type {any}\n */\n const v = this.value\n this._fingerprint = null\n if ($deltaAny.check(v) && v.isDone) {\n // @ts-ignore\n return (this.value = clone(v))\n }\n return v\n }\n\n get fingerprint () {\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeVarUint(encoder, 5) // map insert type: 5\n encoding.writeAny(encoder, this.key)\n if ($deltaAny.check(this.value)) {\n encoding.writeUint8(encoder, 0)\n encoding.writeVarString(encoder, this.value.fingerprint)\n } else {\n encoding.writeUint8(encoder, 1)\n encoding.writeAny(encoder, this.value)\n }\n })))\n }\n\n toJSON () {\n const v = this.value\n const prevValue = this.prevValue\n const attribution = this.attribution\n return object.assign({\n type: this.type,\n value: $deltaAny.check(v) ? v.toJSON() : v\n }, attribution != null ? { attribution } : {}, prevValue !== undefined ? { prevValue } : {})\n }\n\n /**\n * @param {AttrInsertOp<V>} other\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n return this.key === other.key && fun.equalityDeep(this.value, other.value) && fun.equalityDeep(this.attribution, other.attribution)\n }\n\n /**\n * @return {AttrInsertOp<V,K>}\n */\n clone () {\n return new AttrInsertOp(this.key, _markMaybeDeltaAsDone(this.value), _markMaybeDeltaAsDone(this.prevValue), _cloneAttrs(this.attribution))\n }\n}\n\n/**\n * @template V\n * @template {string|number} [K=string]\n */\nexport class AttrDeleteOp {\n /**\n * @param {K} key\n * @param {V|undefined} prevValue\n * @param {Attribution?} attribution\n */\n constructor (key, prevValue, attribution) {\n /**\n * @type {K}\n */\n this.key = key\n /**\n * @type {V|undefined}\n */\n this.prevValue = prevValue\n this.attribution = attribution\n /**\n * @type {string|null}\n */\n this._fingerprint = null\n }\n\n get value () { return undefined }\n\n /**\n * @type {'delete'}\n */\n get type () { return 'delete' }\n\n get fingerprint () {\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeVarUint(encoder, 6) // map delete type: 6\n encoding.writeAny(encoder, this.key)\n })))\n }\n\n /**\n * @return {DeltaAttrOpJSON}\n */\n toJSON () {\n const {\n type, attribution, prevValue\n } = this\n return object.assign({ type }, attribution != null ? { attribution } : {}, prevValue !== undefined ? { prevValue } : {})\n }\n\n /**\n * @param {AttrDeleteOp<V>} other\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n return this.key === other.key && fun.equalityDeep(this.attribution, other.attribution)\n }\n\n clone () {\n return new AttrDeleteOp(this.key, _markMaybeDeltaAsDone(this.prevValue), _cloneAttrs(this.attribution))\n }\n}\n\n/**\n * @template {DeltaAny} [Modifier=DeltaAny]\n * @template {string|number} [K=string]\n */\nexport class AttrModifyOp {\n /**\n * @param {K} key\n * @param {Modifier} delta\n */\n constructor (key, delta) {\n /**\n * @readonly\n * @type {K}\n */\n this.key = key\n /**\n * @readonly\n * @type {Modifier}\n */\n this.value = delta\n /**\n * @type {string|null}\n */\n this._fingerprint = null\n }\n\n /**\n * @type {'modify'}\n */\n get type () { return 'modify' }\n\n get fingerprint () {\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeVarUint(encoder, 7) // map modify type: 7\n encoding.writeAny(encoder, this.key)\n encoding.writeVarString(encoder, this.value.fingerprint)\n })))\n }\n\n /**\n * @return {DeltaBuilder}\n */\n get _modValue () {\n this._fingerprint = null\n if (this.value.isDone) {\n // @ts-ignore\n this.value = /** @type {any} */ (clone(this.value))\n }\n // @ts-ignore\n return this.value\n }\n\n /**\n * @return {DeltaAttrOpJSON}\n */\n toJSON () {\n return {\n type: this.type,\n value: this.value.toJSON()\n }\n }\n\n /**\n * @param {AttrModifyOp<Modifier>} other\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n return this.key === other.key && this.value[equalityTrait.EqualityTraitSymbol](other.value)\n }\n\n /**\n * @return {AttrModifyOp<Modifier,K>}\n */\n clone () {\n return new AttrModifyOp(this.key, /** @type {Modifier} */ (this.value.done()))\n }\n}\n\n/**\n * @type {s.Schema<AttrDeleteOp<any> | DeleteOp>}\n */\nexport const $deleteOp = s.$custom(o => o != null && (o.constructor === DeleteOp || o.constructor === AttrDeleteOp))\n\n/**\n * @type {s.Schema<AttrInsertOp<any> | InsertOp<any>>}\n */\nexport const $insertOp = s.$custom(o => o != null && (o.constructor === AttrInsertOp || o.constructor === InsertOp))\n\n/**\n * @template {fingerprintTrait.Fingerprintable} Content\n * @param {s.Schema<Content>} $content\n * @return {s.Schema<AttrInsertOp<Content> | InsertOp<Content>>}\n */\nexport const $insertOpWith = $content => s.$custom(o =>\n o != null && (\n (o.constructor === AttrInsertOp && $content.check(/** @type {AttrInsertOp<Content>} */ (o).value)) ||\n (o.constructor === InsertOp && /** @type {InsertOp<Content>} */ (o).insert.every(ins => $content.check(ins)))\n )\n)\n\n/**\n * @type {s.Schema<TextOp>}\n */\nexport const $textOp = s.$constructedBy(TextOp)\n\n/**\n * @type {s.Schema<RetainOp>}\n */\nexport const $retainOp = s.$constructedBy(RetainOp)\n\n/**\n * @type {s.Schema<AttrModifyOp | ModifyOp>}\n */\nexport const $modifyOp = s.$custom(o => o != null && (o.constructor === AttrModifyOp || o.constructor === ModifyOp))\n\n/**\n * @template {DeltaAny} Modify\n * @param {s.Schema<Modify>} $content\n * @return {s.Schema<AttrModifyOp<Modify> | ModifyOp<Modify>>}\n */\nexport const $modifyOpWith = $content => s.$custom(o =>\n o != null && (\n (o.constructor === AttrModifyOp && $content.check(/** @type {AttrModifyOp<Modify>} */ (o).value)) ||\n (o.constructor === ModifyOp && $content.check(/** @type {ModifyOp<Modify>} */ (o).value))\n )\n)\n\nexport const $anyOp = s.$union($insertOp, $deleteOp, $textOp, $modifyOp)\n\n/**\n * @template {Array<any>|string} C1\n * @template {Array<any>|string} C2\n * @typedef {Extract<C1 | C2, Array<any>> extends never\n * ? never\n * : (Array<(Extract<C1 | C2,Array<any>> extends Array<infer AC1> ? (unknown extends AC1 ? never : AC1) : never)>)} MergeListArrays\n */\n\n/**\n * @template {{[Key in string|number]: any}} Attrs\n * @template {string|number} Key\n * @template {any} Val\n * @typedef {{ [K in (Key | keyof Attrs)]: (unknown extends Attrs[K] ? never : Attrs[K]) | (Key extends K ? Val : never) }} AddToAttrs\n */\n\n/**\n * @template {{[Key in string|number|symbol]: any}} Attrs\n * @template {{[Key in string|number|symbol]: any}} NewAttrs\n * @typedef {{ [K in (keyof NewAttrs | keyof Attrs)]: (unknown extends Attrs[K] ? never : Attrs[K]) | (unknown extends NewAttrs[K] ? never : NewAttrs[K]) }} MergeAttrs\n */\n\n/**\n * @template X\n * @typedef {0 extends (1 & X) ? null : X} _AnyToNull\n */\n\n/**\n * @template {s.Schema<Delta<any,any,any,any,any>>|null} Schema\n * @typedef {_AnyToNull<Schema> extends null ? Delta<any,{[key:string|number]:any},any,string> : (Schema extends s.Schema<infer D> ? D : never)} AllowedDeltaFromSchema\n */\n\n/**\n * @typedef {Delta<any,{ [k:string|number]: any },any,any,any>} DeltaAny\n */\n/**\n * @typedef {DeltaBuilder<any,{ [k:string|number]: any },any,any,any>} DeltaBuilderAny\n */\n\n/**\n * @template {string} [NodeName=any]\n * @template {{[k:string|number]:any}} [Attrs={}]\n * @template {fingerprintTrait.Fingerprintable} [Children=never]\n * @template {string} [Text=never]\n * @template {s.Schema<Delta<any,any,any,any,any>>|null} [Schema=any]\n */\nexport class Delta {\n /**\n * @param {NodeName} [name]\n * @param {Schema} [$schema]\n */\n constructor (name, $schema) {\n this.name = name || null\n this.$schema = $schema || null\n /**\n * @type {{ [K in keyof Attrs]?: K extends string|number ? (AttrInsertOp<Attrs[K],K>|AttrDeleteOp<Attrs[K],K>|(Delta extends Attrs[K] ? AttrModifyOp<Extract<Attrs[K],DeltaAny>,K> : never)) : never }\n * & { [Symbol.iterator]: () => Iterator<{ [K in keyof Attrs]: K extends string|number ? (AttrInsertOp<Attrs[K],K>|AttrDeleteOp<Attrs[K],K>|(Delta extends Attrs[K] ? AttrModifyOp<Extract<Attrs[K],DeltaAny>,K> : never)) : never }[keyof Attrs]> }\n * }\n */\n this.attrs = /** @type {any} */ ({\n * [Symbol.iterator] () {\n for (const k in this) {\n yield this[k]\n }\n }\n })\n\n /**\n * @type {list.List<\n * RetainOp\n * | DeleteOp\n * | (Text extends never ? never : TextOp)\n * | (Children extends never ? never : InsertOp<Children>)\n * | (Delta extends Children ? ModifyOp<Extract<Children,Delta<any,any,any,any,any>>> : never)\n * >}\n */\n this.children = /** @type {any} */ (list.create())\n this.childCnt = 0\n /**\n * @type {any}\n */\n this.origin = null\n /**\n * @type {string|null}\n */\n this._fingerprint = null\n this.isDone = false\n }\n\n /**\n * @type {string}\n */\n get fingerprint () {\n return this._fingerprint || (this._fingerprint = buffer.toBase64(encoding.encode(encoder => {\n encoding.writeUint32(encoder, 0xf2ae5680) // \"magic number\" that ensures that different types of content don't yield the same fingerprint\n encoding.writeAny(encoder, this.name)\n /**\n * @type {Array<number|string>}\n */\n const keys = []\n for (const attr of this.attrs) {\n keys.push(attr.key)\n }\n keys.sort((a, b) => {\n const aIsString = s.$string.check(a)\n const bIsString = s.$string.check(b)\n // numbers first\n // in ascending order\n return (aIsString && bIsString)\n ? a.localeCompare(b)\n : (aIsString ? 1 : (bIsString ? -1 : (a - b)))\n })\n encoding.writeVarUint(encoder, keys.length)\n for (const key of keys) {\n encoding.writeVarString(encoder, /** @type {any} */ (this.attrs[/** @type {keyof Attrs} */ (key)]).fingerprint)\n }\n encoding.writeVarUint(encoder, this.children.len)\n for (const child of this.children) {\n encoding.writeVarString(encoder, child.fingerprint)\n }\n return buffer.toBase64(rabin.fingerprint(rabin.StandardIrreducible128, encoding.toUint8Array(encoder)))\n })))\n }\n\n [fingerprintTrait.FingerprintTraitSymbol] () {\n return this.fingerprint\n }\n\n isEmpty () {\n return object.isEmpty(this.attrs) && list.isEmpty(this.children)\n }\n\n /**\n * @return {DeltaJSON}\n */\n toJSON () {\n const name = this.name\n /**\n * @type {any}\n */\n const attrs = {}\n /**\n * @type {any}\n */\n const children = []\n for (const attr of this.attrs) {\n attrs[attr.key] = attr.toJSON()\n }\n this.children.forEach(val => {\n children.push(val.toJSON())\n })\n return object.assign(\n { type: /** @type {'delta'} */ ('delta') },\n (name != null ? { name } : {}),\n (object.isEmpty(attrs) ? {} : { attrs }),\n (children.length > 0 ? { children } : {})\n )\n }\n\n /**\n * @param {Delta<any,any,any,any,any>} other\n * @return {boolean}\n */\n equals (other) {\n return this[equalityTrait.EqualityTraitSymbol](other)\n }\n\n /**\n * @param {any} other\n * @return {boolean}\n */\n [equalityTrait.EqualityTraitSymbol] (other) {\n // @todo it is only necessary to compare finrerprints OR do a deep equality check (remove\n // childCnt as well)\n return this.name === other.name && fun.equalityDeep(this.attrs, other.attrs) && fun.equalityDeep(this.children, other.children) && this.childCnt === other.childCnt\n }\n\n /**\n * @return {DeltaBuilder<NodeName,Attrs,Children,Text,Schema>}\n */\n clone () {\n return this.slice(0, this.childCnt)\n }\n\n /**\n * @param {number} start\n * @param {number} end\n * @return {DeltaBuilder<NodeName,Attrs,Children,Text,Schema>}\n */\n slice (start = 0, end = this.childCnt) {\n const cpy = /** @type {DeltaAny} */ (new DeltaBuilder(/** @type {any} */ (this.name), this.$schema))\n cpy.origin = this.origin\n // copy attrs\n for (const op of this.attrs) {\n cpy.attrs[op.key] = /** @type {any} */ (op.clone())\n }\n // copy children\n const slicedLen = end - start\n let remainingLen = slicedLen\n /**\n * @type {ChildrenOpAny?}\n */\n let currNode = this.children.start\n let currNodeOffset = 0\n while (start > 0 && currNode != null) {\n if (currNode.length <= start) {\n start -= currNode.length\n currNode = currNode.next\n } else {\n currNodeOffset = start\n start = 0\n }\n }\n if (currNodeOffset > 0 && currNode) {\n const ncpy = currNode.clone(currNodeOffset, currNodeOffset + math.min(remainingLen, currNode.length - currNodeOffset))\n list.pushEnd(cpy.children, ncpy)\n remainingLen -= ncpy.length\n currNode = currNode.next\n }\n while (currNode != null && currNode.length <= remainingLen) {\n list.pushEnd(cpy.children, currNode.clone())\n remainingLen -= currNode.length\n currNode = currNode.next\n }\n if (currNode != null && remainingLen > 0) {\n list.pushEnd(cpy.children, currNode.clone(0, remainingLen))\n remainingLen -= math.min(currNode.length, remainingLen)\n }\n cpy.childCnt = slicedLen - remainingLen\n // @ts-ignore\n return cpy\n }\n\n /**\n * Mark this delta as done and perform some cleanup (e.g. remove appended retains without\n * formats&attributions). In the future, there might be additional merge operations that can be\n * performed to result in smaller deltas. Set `markAsDone=false` to only perform the cleanup.\n *\n * @return {Delta<NodeName,Attrs,Children,Text,Schema>}\n */\n done (markAsDone = true) {\n if (!this.isDone) {\n this.isDone = markAsDone\n const cs = this.children\n for (let end = cs.end; end !== null && $retainOp.check(end) && end.format == null && end.attribution == null; end = cs.end) {\n this.childCnt -= end.length\n list.popEnd(cs)\n }\n }\n return this\n }\n}\n\n/**\n * @template {DeltaAny} D\n * @param {D} d\n * @return {D extends DeltaBuilder<infer NodeName,infer Attrs,infer Children,infer Text,infer Schema> ? DeltaBuilder<NodeName,Attrs,Children,Text,Schema> : never}\n */\nexport const clone = d => /** @type {any} */ (d.slice(0, d.childCnt))\n\n/**\n * Try merging this op with the previous op\n * @param {list.List<any>} parent\n * @param {InsertOp<any>|RetainOp|DeleteOp|TextOp|ModifyOp<any>} op\n */\nconst tryMergeWithPrev = (parent, op) => {\n const prevOp = op.prev\n if (\n prevOp?.constructor !== op.constructor ||\n (\n (!$deleteOp.check(op) && !$modifyOp.check(op)) && (!fun.equalityDeep(op.format, /** @type {InsertOp<any>} */ (prevOp).format) || !fun.equalityDeep(op.attribution, /** @type {InsertOp<any>} */ (prevOp).attribution))\n )\n ) {\n // constructor mismatch or format/attribution mismatch\n return\n }\n // can be merged\n if ($insertOp.check(op)) {\n /** @type {InsertOp<any>} */ (prevOp).insert.push(...op.insert)\n } else if ($retainOp.check(op)) {\n // @ts-ignore\n /** @type {RetainOp} */ (prevOp).retain += op.retain\n } else if ($deleteOp.check(op)) {\n /** @type {DeleteOp} */ (prevOp).delete += op.delete\n } else if ($textOp.check(op)) {\n /** @type {TextOp} */ (prevOp)._updateInsert(/** @type {TextOp} */ (prevOp).insert + op.insert)\n } else {\n error.unexpectedCase()\n }\n list.remove(parent, op)\n}\n\n/**\n * Ensures that the delta can be edited. clears _fingerprint cache.\n *\n * @param {any} d\n */\nconst modDeltaCheck = d => {\n if (d.isDone) {\n /**\n * You tried to modify a delta after it has been marked as \"done\".\n */\n throw error.create(\"Readonly Delta can't be modified\")\n }\n d._fingerprint = null\n}\n\n/**\n * @template {string} [NodeName=any]\n * @template {{[key:string|number]:any}} [Attrs={}]\n * @template {fingerprintTrait.Fingerprintable} [Children=never]\n * @template {string} [Text=never]\n * @template {s.Schema<Delta<any,any,any,any,any>>|null} [Schema=any]\n * @extends {Delta<NodeName,Attrs,Children,Text,Schema>}\n */\nexport class DeltaBuilder extends Delta {\n /**\n * @param {NodeName} [name]\n * @param {Schema} [$schema]\n */\n constructor (name, $schema) {\n super(name, $schema)\n /**\n * @type {FormattingAttributes?}\n */\n this.usedAttributes = null\n /**\n * @type {Attribution?}\n */\n this.usedAttribution = null\n }\n\n /**\n * @param {Attribution?} attribution\n */\n useAttribution (attribution) {\n modDeltaCheck(this)\n this.usedAttribution = attribution\n return this\n }\n\n /**\n * @param {FormattingAttributes?} attributes\n * @return {this}\n */\n useAttributes (attributes) {\n modDeltaCheck(this)\n this.usedAttributes = attributes\n return this\n }\n\n /**\n * @param {string} name\n * @param {any} value\n */\n updateUsedAttributes (name, value) {\n modDeltaCheck(this)\n if (value == null) {\n this.usedAttributes = object.assign({}, this.usedAttributes)\n delete this.usedAttributes?.[name]\n if (object.isEmpty(this.usedAttributes)) {\n this.usedAttributes = null\n }\n } else if (!fun.equalityDeep(this.usedAttributes?.[name], value)) {\n this.usedAttributes = object.assign({}, this.usedAttributes)\n this.usedAttributes[name] = value\n }\n return this\n }\n\n /**\n * @template {keyof Attribution} NAME\n * @param {NAME} name\n * @param {Attribution[NAME]?} value\n */\n updateUsedAttribution (name, value) {\n modDeltaCheck(this)\n if (value == null) {\n this.usedAttribution = object.assign({}, this.usedAttribution)\n delete this.usedAttribution?.[name]\n if (object.isEmpty(this.usedAttribution)) {\n this.usedAttribution = null\n }\n } else if (!fun.equalityDeep(this.usedAttribution?.[name], value)) {\n this.usedAttribution = object.assign({}, this.usedAttribution)\n this.usedAttribution[name] = value\n }\n return this\n }\n\n /**\n * @template {AllowedDeltaFromSchema<Schema> extends Delta<any,any,infer Children,infer Text,infer Schema> ? ((Children extends never ? never : Array<Children>) | Text) : never} NewContent\n * @param {NewContent} insert\n * @param {FormattingAttributes?} [formatting]\n * @param {Attribution?} [attribution]\n * @return {DeltaBuilder<\n * NodeName,\n * Attrs,\n * Exclude<NewContent,string>[number]|Children,\n * (Extract<NewContent,string>|Text) extends never ? never : string,\n * Schema\n * >}\n */\n insert (insert, formatting = null, attribution = null) {\n modDeltaCheck(this)\n const mergedAttributes = mergeAttrs(this.usedAttributes, formatting)\n const mergedAttribution = mergeAttrs(this.usedAttribution, attribution)\n /**\n * @param {TextOp | InsertOp<any>} lastOp\n */\n const checkMergedEquals = lastOp => (mergedAttributes === lastOp.format || fun.equalityDeep(mergedAttributes, lastOp.format)) && (mergedAttribution === lastOp.attribution || fun.equalityDeep(mergedAttribution, lastOp.attribution))\n const end = this.children.end\n if (s.$string.check(insert)) {\n if ($textOp.check(end) && checkMergedEquals(end)) {\n end._updateInsert(end.insert + insert)\n } else if (insert.length > 0) {\n list.pushEnd(this.children, new TextOp(insert, object.isEmpty(mergedAttributes) ? null : mergedAttributes, object.isEmpty(mergedAttribution) ? null : mergedAttribution))\n }\n this.childCnt += insert.length\n } else if (arr.isArray(insert)) {\n if ($insertOp.check(end) && checkMergedEquals(end)) {\n // @ts-ignore\n end.insert.push(...insert)\n end._fingerprint = null\n } else if (insert.length > 0) {\n list.pushEnd(this.children, new InsertOp(insert, object.isEmpty(mergedAttributes) ? null : mergedAttributes, object.isEmpty(mergedAttribution) ? null : mergedAttribution))\n }\n this.childCnt += insert.length\n }\n return /** @type {any} */ (this)\n }\n\n /**\n * @template {AllowedDeltaFromSchema<Schema> extends Delta<any,any,infer Children,any,any> ? Extract<Children,Delta<any,any,any,any,any>> : never} NewContent\n * @param {NewContent} modify\n * @param {FormattingAttributes?} formatting\n * @param {Attribution?} attribution\n * @return {DeltaBuilder<\n * NodeName,\n * Attrs,\n * Exclude<NewContent,string>[number]|Children,\n * (Extract<NewContent,string>|Text) extends string ? string : never,\n * Schema\n * >}\n */\n modify (modify, formatting = null, attribution = null) {\n modDeltaCheck(this)\n const mergedAttributes = mergeAttrs(this.usedAttributes, formatting)\n const mergedAttribution = mergeAttrs(this.usedAttribution, attribution)\n list.pushEnd(this.children, new ModifyOp(modify, object.isEmpty(mergedAttributes) ? null : mergedAttributes, object.isEmpty(mergedAttribution) ? null : mergedAttribution))\n this.childCnt += 1\n return /** @type {any} */ (this)\n }\n\n /**\n * @param {number} len\n * @param {FormattingAttributes?} [format]\n * @param {Attribution?} [attribution]\n */\n retain (len, format = null, attribution = null) {\n modDeltaCheck(this)\n const mergedFormats = mergeAttrs(this.usedAttributes, format)\n const mergedAttribution = mergeAttrs(this.usedAttribution, attribution)\n const lastOp = /** @type {RetainOp|InsertOp<any>} */ (this.children.end)\n if (lastOp instanceof RetainOp && fun.equalityDeep(mergedFormats, lastOp.format) && fun.equalityDeep(mergedAttribution, lastOp.attribution)) {\n // @ts-ignore\n lastOp.retain += len\n } else if (len > 0) {\n list.pushEnd(this.children, new RetainOp(len, mergedFormats, mergedAttribution))\n }\n this.childCnt += len\n return this\n }\n\n /**\n * @param {number} len\n */\n delete (len) {\n modDeltaCheck(this)\n const lastOp = /** @type {DeleteOp|InsertOp<any>} */ (this.children.end)\n if (lastOp instanceof DeleteOp) {\n lastOp.delete += len\n } else if (len > 0) {\n list.pushEnd(this.children, new DeleteOp(len))\n }\n this.childCnt += len\n return this\n }\n\n /**\n * @template {AllowedDeltaFromSchema<Schema> extends Delta<any,infer Attrs,any,any,any> ? (keyof Attrs) : never} Key\n * @template {AllowedDeltaFromSchema<Schema> extends Delta<any,infer Attrs,any,any,any> ? (Attrs[Key]) : never} Val\n * @param {Key} key\n * @param {Val} val\n * @param {Attribution?} attribution\n * @param {Val|undefined} [prevValue]\n * @return {DeltaBuilder<\n * NodeName,\n * { [K in keyof AddToAttrs<Attrs,Key,Val>]: AddToAttrs<Attrs,Key,Val>[K] },\n * Children,\n * Text,\n * Schema\n * >}\n */\n set (key, val, attribution = null, prevValue) {\n modDeltaCheck(this)\n this.attrs[key] = /** @type {any} */ (new AttrInsertOp(key, val, prevValue, mergeAttrs(this.usedAttribution, attribution)))\n return /** @type {any} */ (this)\n }\n\n /**\n * @template {AllowedDeltaFromSchema<Schema> extends Delta<any,infer Attrs,any,any,any> ? Attrs : never} NewAttrs\n * @param {NewAttrs} attrs\n * @param {Attribution?} attribution\n * @return {DeltaBuilder<\n * NodeName,\n * { [K in keyof MergeAttrs<Attrs,NewAttrs>]: MergeAttrs<Attrs,NewAttrs>[K] },\n * Children,\n * Text,\n * Schema\n * >}\n */\n setMany (attrs, attribution = null) {\n modDeltaCheck(this)\n for (const k in attrs) {\n this.set(/** @type {any} */ (k), attrs[k], attribution)\n }\n return /** @type {any} */ (this)\n }\n\n /**\n * @template {AllowedDeltaFromSchema<Schema> extends Delta<any,infer As,any,any,any> ? keyof As : never} Key\n * @param {Key} key\n * @param {Attribution?} attribution\n * @param {any} [prevValue]\n * @return {DeltaBuilder<\n * NodeName,\n * { [K in keyof AddToAttrs<Attrs,Key,never>]: AddToAttrs<Attrs,Key,never>[K] },\n * Children,\n * Text,\n * Schema\n * >}\n */\n unset (key, attribution = null, prevValue) {\n modDeltaCheck(this)\n this.attrs[key] = /** @type {any} */ (new AttrDeleteOp(key, prevValue, mergeAttrs(this.usedAttribution, attribution)))\n return /** @type {any} */ (this)\n }\n\n /**\n * @template {AllowedDeltaFromSchema<Schema> extends Delta<any,infer As,any,any,any> ? { [K in keyof As]: Extract<As[K],Delta<any,any,any,any,any>> extends never ? never : K }[keyof As] : never} Key\n * @template {AllowedDeltaFromSchema<Schema> extends Delta<any,infer As,any,any,any> ? Extract<As[Key],Delta<any,any,any,any,any>> : never} D\n * @param {Key} key\n * @param {D} modify\n * @return {DeltaBuilder<\n * NodeName,\n * { [K in keyof AddToAttrs<Attrs,Key,D>]: AddToAttrs<Attrs,Key,D>[K] },\n * Children,\n * Text,\n * Schema\n * >}\n */\n update (key, modify) {\n modDeltaCheck(this)\n this.attrs[key] = /** @type {any} */ (new AttrModifyOp(key, modify))\n return /** @type {any} */ (this)\n }\n\n /**\n * @param {Delta<NodeName,Attrs,Children,Text,any>} other\n */\n apply (other) {\n modDeltaCheck(this)\n this.$schema?.expect(other)\n // apply attrs\n for (const op of other.attrs) {\n const c = /** @type {AttrInsertOp<any,any>|AttrDeleteOp<any>|AttrModifyOp<any,any>} */ (this.attrs[op.key])\n if ($modifyOp.check(op)) {\n if ($deltaAny.check(c?.value)) {\n c._modValue.apply(op.value)\n } else {\n // then this is a simple modify\n // @ts-ignore\n this.attrs[op.key] = op.clone()\n }\n } else if ($insertOp.check(op)) {\n // @ts-ignore\n op.prevValue = c?.value\n // @ts-ignore\n this.attrs[op.key] = op.clone()\n } else if ($deleteOp.check(op)) {\n op.prevValue = c?.value\n delete this.attrs[op.key]\n }\n }\n // apply children\n /**\n * @type {ChildrenOpAny?}\n */\n let opsI = this.children.start\n let offset = 0\n /**\n * At the end, we will try to merge this op, and op.next op with their respective previous op.\n *\n * Hence, anytime an op is cloned, deleted, or inserted (anytime list.* api is used) we must add\n * an op to maybeMergeable.\n *\n * @type {Array<InsertOp<any>|RetainOp|DeleteOp|TextOp|ModifyOp<any>>}\n */\n const maybeMergeable = []\n /**\n * @template {InsertOp<any>|RetainOp|DeleteOp|TextOp|ModifyOp<any>|null} OP\n * @param {OP} op\n * @return {OP}\n */\n const scheduleForMerge = op => {\n op && maybeMergeable.push(op)\n return op\n }\n other.children.forEach(op => {\n if ($textOp.check(op) || $insertOp.check(op)) {\n if (offset === 0) {\n list.insertBetween(this.children, opsI == null ? this.children.end : opsI.prev, opsI, scheduleForMerge(op.clone()))\n } else {\n // @todo inmplement \"splitHelper\" and \"insertHelper\" - I'm splitting all the time and\n // forget to update opsI\n if (opsI == null) error.unexpectedCase()\n const cpy = scheduleForMerge(opsI.clone(offset))\n opsI._splice(offset, opsI.length - offset)\n list.insertBetween(this.children, opsI, opsI.next || null, cpy)\n list.insertBetween(this.children, opsI, cpy || null, scheduleForMerge(op.clone()))\n opsI = cpy\n offset = 0\n }\n this.childCnt += op.insert.length\n } else if ($retainOp.check(op)) {\n let retainLen = op.length\n\n if (offset > 0 && opsI != null && op.format != null && !$deleteOp.check(opsI) && !object.every(op.format, (v, k) => fun.equalityDeep(v, /** @type {InsertOp<any>|RetainOp|ModifyOp} */ (opsI).format?.[k] || null))) {\n // need to split current op\n const cpy = scheduleForMerge(opsI.clone(offset))\n opsI._splice(offset, opsI.length - offset)\n list.insertBetween(this.children, opsI, opsI.next || null, cpy)\n opsI = cpy\n offset = 0\n }\n\n while (opsI != null && opsI.length - offset <= retainLen) {\n op.format != null && updateOpFormat(opsI, op.format)\n retainLen -= opsI.length - offset\n opsI = opsI?.next || null\n offset = 0\n }\n\n if (opsI != null) {\n if (op.format != null && retainLen > 0) {\n // split current op and apply format\n const cpy = scheduleForMerge(opsI.clone(retainLen))\n opsI._splice(retainLen, opsI.length - retainLen)\n list.insertBetween(this.children, opsI, opsI.next || null, cpy)\n updateOpFormat(opsI, op.format)\n opsI = cpy\n } else {\n offset += retainLen\n }\n } else if (retainLen > 0) {\n list.pushEnd(this.children, scheduleForMerge(new RetainOp(retainLen, op.format, op.attribution)))\n this.childCnt += retainLen\n }\n } else if ($deleteOp.check(op)) {\n let remainingLen = op.delete\n while (remainingLen > 0) {\n if (opsI == null) {\n list.pushEnd(this.children, scheduleForMerge(new DeleteOp(remainingLen)))\n this.childCnt += remainingLen\n break\n } else if (opsI instanceof DeleteOp) {\n const delLen = opsI.length - offset\n // the same content can't be deleted twice, remove duplicated deletes\n if (delLen >= remainingLen) {\n offset = 0\n opsI = opsI.next\n } else {\n offset += remainingLen\n }\n remainingLen -= delLen\n } else { // insert / embed / retain / modify ⇒ replace\n // case1: delete o fully\n // case2: delete some part of beginning\n // case3: delete some part of end\n // case4: delete some part of center\n const delLen = math.min(opsI.length - offset, remainingLen)\n this.childCnt -= delLen\n if (opsI.length === delLen) {\n // case 1\n offset = 0\n scheduleForMerge(opsI.next)\n list.remove(this.children, opsI)\n opsI = opsI.next\n } else if (offset === 0) {\n // case 2\n offset = 0\n opsI._splice(0, delLen)\n } else if (offset + delLen === opsI.length) {\n // case 3\n opsI._splice(offset, delLen)\n offset = 0\n opsI = opsI.next\n } else {\n // case 4\n opsI._splice(offset, delLen)\n }\n remainingLen -= delLen\n }\n }\n } else if ($modifyOp.check(op)) {\n if (opsI == null) {\n list.pushEnd(this.children, op.clone())\n this.childCnt += 1\n return\n }\n if ($modifyOp.check(opsI)) {\n opsI._modValue.apply(op.value)\n } else if ($insertOp.check(opsI)) {\n opsI._modValue(offset).apply(op.value)\n } else if ($retainOp.check(opsI)) {\n if (offset > 0) {\n const cpy = scheduleForMerge(opsI.clone(0, offset)) // skipped len\n opsI._splice(0, offset) // new remainder\n list.insertBetween(this.children, opsI.prev, opsI, cpy) // insert skipped len\n offset = 0\n }\n list.insertBetween(this.children, opsI.prev, opsI, scheduleForMerge(op.clone())) // insert skipped len\n if (opsI.length === 1) {\n list.remove(this.children, opsI)\n } else {\n opsI._splice(0, 1)\n scheduleForMerge(opsI)\n }\n } else if ($deleteOp.check(opsI)) {\n // nop\n } else {\n error.unexpectedCase()\n }\n } else {\n error.unexpectedCase()\n }\n })\n maybeMergeable.forEach(op => {\n // check if this is still integrated\n if (op.prev?.next === op) {\n tryMergeWithPrev(this.children, op)\n op.next && tryMergeWithPrev(this.children, op.next)\n }\n })\n return this\n }\n\n /**\n * @param {DeltaAny} other\n * @param {boolean} priority\n */\n rebase (other, priority) {\n modDeltaCheck(this)\n /**\n * Rebase attributes\n *\n * - insert vs delete ⇒ insert takes precedence\n * - insert vs modify ⇒ insert takes precedence\n * - insert vs insert ⇒ priority decides\n * - delete vs modify ⇒ delete takes precedence\n * - delete vs delete ⇒ current delete op is removed because item has already been deleted\n * - modify vs modify ⇒ rebase using priority\n */\n for (const op of this.attrs) {\n if ($insertOp.check(op)) {\n if ($insertOp.check(other.attrs[op.key]) && !priority) {\n delete this.attrs[op.key]\n }\n } else if ($deleteOp.check(op)) {\n const otherOp = other.attrs[/** @type {any} */ (op.key)]\n if ($insertOp.check(otherOp)) {\n delete this.attrs[otherOp.key]\n }\n } else if ($modifyOp.check(op)) {\n const otherOp = other.attrs[/** @type {any} */ (op.key)]\n if (otherOp == null) {\n // nop\n } else if ($modifyOp.check(otherOp)) {\n op._modValue.rebase(otherOp.value, priority)\n } else {\n delete this.attrs[otherOp.key]\n }\n }\n }\n /**\n * Rebase children.\n *\n * Precedence: insert with higher priority comes first. Op with less priority is transformed to\n * be inserted later.\n *\n * @todo always check if inser OR text\n */\n /**\n * @type {ChildrenOpAny?}\n */\n let currChild = this.children.start\n let currOffset = 0\n /**\n * @type {ChildrenOpAny?}\n */\n let otherChild = other.children.start\n let otherOffset = 0\n while (currChild != null && otherChild != null) {\n if ($insertOp.check(currChild) || $tex