edytor 3.0.0 → 5.0.0
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/README.md +2 -2
- package/dist/block/block.svelte.js +6 -21
- package/dist/block/inlineBlock.svelte.js +4 -6
- package/dist/clipboard/moveText.js +6 -8
- package/dist/cloudflare/DocumentRoom.d.ts +23 -12
- package/dist/cloudflare/DocumentRoom.js +10 -6
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/room/access.d.ts +8 -3
- package/dist/cloudflare/room/access.js +44 -20
- package/dist/cloudflare/room/admission.js +28 -5
- package/dist/cloudflare/room/comments.js +28 -1
- package/dist/cloudflare/room/context.d.ts +139 -129
- package/dist/cloudflare/room/context.js +11 -4
- package/dist/cloudflare/room/forged.d.ts +14 -0
- package/dist/cloudflare/room/forged.js +30 -2
- package/dist/cloudflare/room/frames.d.ts +4 -0
- package/dist/cloudflare/room/frames.js +5 -1
- package/dist/cloudflare/room/purge.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +45 -19
- package/dist/cloudflare/room/replicas.d.ts +4 -2
- package/dist/cloudflare/room/replicas.js +8 -3
- package/dist/cloudflare/room/suggest.d.ts +7 -0
- package/dist/cloudflare/room/suggest.js +143 -0
- package/dist/cloudflare/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- package/dist/cloudflare/routeDocumentSocket.d.ts +10 -1
- package/dist/cloudflare/routeDocumentSocket.js +5 -3
- package/dist/components/Content.svelte +9 -1
- package/dist/crdt/anchors.js +3 -2
- package/dist/crdt/attribution/attribution.d.ts +34 -12
- package/dist/crdt/attribution/attribution.js +59 -21
- package/dist/crdt/attribution/block.d.ts +3 -3
- package/dist/crdt/attribution/block.js +7 -8
- package/dist/crdt/attribution/index.d.ts +1 -1
- package/dist/crdt/attribution/index.js +1 -1
- package/dist/crdt/data.d.ts +62 -31
- package/dist/crdt/data.js +211 -103
- package/dist/crdt/doc/capability.js +9 -13
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.d.ts +1 -1
- package/dist/crdt/doc/funnel.js +34 -26
- package/dist/crdt/doc/gate.d.ts +3 -3
- package/dist/crdt/doc/history.d.ts +31 -9
- package/dist/crdt/doc/history.js +204 -73
- package/dist/crdt/doc/layout.js +16 -27
- package/dist/crdt/doc/meta.js +17 -23
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.d.ts +2 -2
- package/dist/crdt/doc/reads.js +20 -30
- package/dist/crdt/doc/seed.js +2 -2
- package/dist/crdt/doc/split-merge.js +12 -15
- package/dist/crdt/doc/steps.d.ts +25 -13
- package/dist/crdt/doc/steps.js +55 -125
- package/dist/crdt/doc/suggest.d.ts +167 -0
- package/dist/crdt/doc/suggest.js +579 -0
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +26 -6
- package/dist/crdt/document.js +40 -15
- package/dist/crdt/edytor-doc.d.ts +410 -390
- package/dist/crdt/edytor-doc.js +143 -95
- package/dist/crdt/engine-api.d.ts +39 -1
- package/dist/crdt/engine.d.ts +2 -2
- package/dist/crdt/engine.js +4 -5
- package/dist/crdt/flow.d.ts +2 -2
- package/dist/crdt/flow.js +29 -37
- package/dist/crdt/index.d.ts +134 -127
- package/dist/crdt/index.js +4 -1
- package/dist/crdt/operations.d.ts +12 -3
- package/dist/crdt/parts.d.ts +43 -0
- package/dist/crdt/parts.js +44 -0
- package/dist/crdt/placement/display.d.ts +6 -31
- package/dist/crdt/placement/display.js +41 -146
- package/dist/crdt/placement/keys.d.ts +63 -0
- package/dist/crdt/placement/keys.js +95 -0
- package/dist/crdt/placement/model.d.ts +119 -118
- package/dist/crdt/placement/model.js +242 -142
- package/dist/crdt/placement/resolve.d.ts +123 -35
- package/dist/crdt/placement/resolve.js +188 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -0
- package/dist/crdt/protocol.d.ts +1 -1
- package/dist/crdt/protocol.js +1 -1
- package/dist/crdt/protocols/auth.d.ts +11 -1
- package/dist/crdt/protocols/auth.js +17 -1
- package/dist/crdt/protocols/comments.d.ts +16 -1
- package/dist/crdt/protocols/comments.js +9 -1
- package/dist/crdt/providers/index.d.ts +6 -4
- package/dist/crdt/providers/index.js +3 -1
- package/dist/crdt/providers/room.d.ts +16 -2
- package/dist/crdt/providers/room.js +64 -17
- package/dist/crdt/providers/websocket.d.ts +6 -4
- package/dist/crdt/providers/websocket.js +14 -7
- package/dist/crdt/purge.d.ts +24 -9
- package/dist/crdt/purge.js +266 -126
- package/dist/crdt/restore.d.ts +4 -3
- package/dist/crdt/restore.js +27 -34
- package/dist/crdt/schema.d.ts +30 -21
- package/dist/crdt/schema.js +39 -23
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +17 -15
- package/dist/crdt/structs.js +10 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +13 -23
- package/dist/crdt/text/index/fold.d.ts +2 -3
- package/dist/crdt/text/index/fold.js +194 -44
- package/dist/crdt/text/index/layout.d.ts +2 -4
- package/dist/crdt/text/index/layout.js +14 -30
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +420 -157
- package/dist/crdt/text/index/records.d.ts +3 -3
- package/dist/crdt/text/index/records.js +24 -31
- package/dist/crdt/text/index/shared.d.ts +11 -3
- package/dist/crdt/text/index/shared.js +29 -17
- package/dist/crdt/text/index/state.d.ts +21 -17
- package/dist/crdt/text/index/state.js +15 -17
- package/dist/crdt/text/index/streams.d.ts +10 -5
- package/dist/crdt/text/index/streams.js +78 -27
- package/dist/crdt/text/items.d.ts +10 -3
- package/dist/crdt/text/items.js +7 -0
- package/dist/crdt/text/model.d.ts +87 -62
- package/dist/crdt/text/model.js +91 -169
- package/dist/crdt/text/ranges.d.ts +1 -1
- package/dist/crdt/text/ranges.js +3 -3
- package/dist/crdt/text/rows.d.ts +13 -7
- package/dist/crdt/text/rows.js +24 -7
- package/dist/crdt/text/runs.d.ts +51 -42
- package/dist/crdt/text/runs.js +24 -7
- package/dist/crdt/text/streams.d.ts +26 -13
- package/dist/crdt/text/streams.js +41 -14
- package/dist/crdt/vendor/yjs/UPSTREAM.md +651 -21
- package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +197 -10
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
- package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
- package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
- package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
- package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +54 -7
- package/dist/crdt/vendor/yjs/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
- package/dist/crdt/vendor/yjs/src/structs/Item.js +649 -51
- package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
- package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
- package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
- package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
- package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
- package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
- package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
- package/dist/crdt/vendor/yjs/src/ynode.js +199 -12
- package/dist/edytor.svelte.d.ts +1 -1
- package/dist/edytor.svelte.js +103 -75
- package/dist/events/beforeInputCommands.d.ts +2 -2
- package/dist/events/beforeInputCommands.js +2 -2
- package/dist/events/nativeInteractiveControl.d.ts +11 -1
- package/dist/events/nativeInteractiveControl.js +41 -1
- package/dist/events/onBeforeInput.js +3 -3
- package/dist/events/onFocus.js +9 -11
- package/dist/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- package/dist/export/forms.d.ts +50 -50
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/labels.d.ts +8 -0
- package/dist/labels.js +6 -1
- package/dist/plugins/comments/CommentsController.svelte.d.ts +5 -1
- package/dist/plugins/comments/CommentsController.svelte.js +14 -4
- package/dist/plugins/find/search.js +2 -2
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/suggestions/SuggestionBars.svelte +40 -23
- package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +3 -1
- package/dist/plugins/suggestions/SuggestionRuns.svelte +77 -0
- package/dist/plugins/suggestions/SuggestionRuns.svelte.d.ts +9 -0
- package/dist/plugins/suggestions/shared.svelte.d.ts +58 -0
- package/dist/plugins/suggestions/shared.svelte.js +421 -0
- package/dist/plugins/suggestions/suggestionsPlugin.d.ts +15 -4
- package/dist/selection/domSelection.d.ts +2 -0
- package/dist/selection/domSelection.js +6 -2
- package/dist/selection/pointer.svelte.js +75 -48
- package/dist/selection/replaceSelection.js +2 -6
- package/dist/selection/selection.svelte.d.ts +3 -3
- package/dist/selection/selection.svelte.js +39 -32
- package/dist/selection/selection.utils.d.ts +14 -0
- package/dist/selection/selection.utils.js +28 -3
- package/dist/session/commands.js +21 -11
- package/dist/session/composition.svelte.d.ts +5 -3
- package/dist/session/composition.svelte.js +21 -17
- package/dist/session/handles.d.ts +4 -17
- package/dist/session/handles.js +11 -37
- package/dist/session/navigation.js +6 -9
- package/dist/session/selection.d.ts +11 -5
- package/dist/session/selection.js +14 -48
- package/dist/session/suggestions.svelte.d.ts +38 -9
- package/dist/session/suggestions.svelte.js +37 -18
- package/dist/session/virtual.d.ts +1 -1
- package/dist/surface/attributes.d.ts +5 -0
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/cells.d.ts +2 -26
- package/dist/surface/cells.js +2 -33
- package/dist/surface/observer.svelte.d.ts +6 -1
- package/dist/surface/observer.svelte.js +130 -83
- package/dist/surface/pin.svelte.d.ts +9 -9
- package/dist/surface/pin.svelte.js +15 -22
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +67 -22
- package/dist/text/text.svelte.js +14 -19
- package/dist/themes/notion.css +29 -0
- package/package.json +4 -2
- package/dist/crdt/placement/rank.d.ts +0 -87
- package/dist/crdt/placement/rank.js +0 -332
- package/dist/crdt/placement/source.d.ts +0 -43
- package/dist/crdt/placement/source.js +0 -66
- package/dist/crdt/text/delete-index.d.ts +0 -129
- package/dist/crdt/text/delete-index.js +0 -365
- package/dist/crdt/text/deletes.d.ts +0 -76
- package/dist/crdt/text/deletes.js +0 -362
- package/dist/crdt/text/index/anchored.d.ts +0 -17
- package/dist/crdt/text/index/anchored.js +0 -197
|
@@ -37,7 +37,7 @@ export const readBlockSet = (decoder) => {
|
|
|
37
37
|
clientRefs.clients.set(client, new BlockRange(refs))
|
|
38
38
|
for (let i = 0; i < numberOfBlocks; i++) {
|
|
39
39
|
const info = decoder.readInfo()
|
|
40
|
-
switch (binary.
|
|
40
|
+
switch (binary.BITS4 & info) { // YP27: content refs fit four bits
|
|
41
41
|
case 0: { // GC
|
|
42
42
|
const len = decoder.readLen()
|
|
43
43
|
refs[i] = new GC(createID(client, clock), len)
|
|
@@ -68,9 +68,9 @@ export const readBlockSet = (decoder) => {
|
|
|
68
68
|
(info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null, // origin
|
|
69
69
|
null, // right
|
|
70
70
|
(info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null, // right origin
|
|
71
|
-
cantCopyParentInfo ? (
|
|
72
|
-
cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.
|
|
73
|
-
readItemContent(decoder, info) // item content
|
|
71
|
+
cantCopyParentInfo ? ((info & binary.BIT5) === binary.BIT5 ? decoder.readKey() : decoder.readLeftID()) : null, // parent (YP27: bit 5 a root's name, YP24: a key)
|
|
72
|
+
cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readKey() : null, // parentSub (YP24: a key)
|
|
73
|
+
readItemContent(decoder, info, createID(client, clock)) // item content (YP25: with its id)
|
|
74
74
|
)
|
|
75
75
|
refs[i] = block
|
|
76
76
|
clock += block.length
|
|
@@ -147,19 +147,28 @@ export class BlockSet {
|
|
|
147
147
|
for (const client of clientids) {
|
|
148
148
|
const range = exclude.clients.get(client)
|
|
149
149
|
const structs = this.clients.get(client)?.refs
|
|
150
|
-
if (range == null || structs == null)
|
|
151
|
-
const firstStruct = structs[0]
|
|
152
|
-
const lastStruct = structs[structs.length - 1]
|
|
150
|
+
if (range == null || structs == null) continue // YP23: the other clients are excluded too
|
|
153
151
|
const idranges = range.getIds()
|
|
154
152
|
for (let i = 0; i < idranges.length; i++) {
|
|
155
153
|
const range = idranges[i]
|
|
156
|
-
|
|
154
|
+
// YP23: read the bounds per range, and the last struct again after
|
|
155
|
+
// the start's split — `findIndexCleanStart` splits a struct in place
|
|
156
|
+
// (the struct shrinks, a new one holds the rest), so bounds read
|
|
157
|
+
// before a split are stale: the next range was skipped (an arriving
|
|
158
|
+
// GC spanning a skip, known ids and the state's end integrated whole
|
|
159
|
+
// over the known middle, and its delete-set entry outran the store),
|
|
160
|
+
// or the end's split was skipped and the range's skip replaced the
|
|
161
|
+
// struct's tail too.
|
|
162
|
+
const firstStruct = structs[0]
|
|
163
|
+
let lastStruct = structs[structs.length - 1]
|
|
157
164
|
if (range.clock >= lastStruct.id.clock + lastStruct.length) continue
|
|
165
|
+
if (range.clock + range.len <= firstStruct.id.clock) continue
|
|
166
|
+
let startIndex = 0
|
|
158
167
|
if (range.clock > firstStruct.id.clock) {
|
|
159
168
|
startIndex = findIndexCleanStart(null, structs, range.clock)
|
|
160
169
|
}
|
|
170
|
+
lastStruct = structs[structs.length - 1]
|
|
161
171
|
let endIndex = structs.length // must be set here, after structs is modified
|
|
162
|
-
if (range.clock + range.len <= firstStruct.id.clock) continue
|
|
163
172
|
if (range.clock + range.len < lastStruct.id.clock + lastStruct.length) {
|
|
164
173
|
endIndex = findIndexCleanStart(null, structs, range.clock + range.len)
|
|
165
174
|
}
|
|
@@ -21,6 +21,7 @@ import { YNode } from '../ynode.js'
|
|
|
21
21
|
* @property {function(Uint8Array<ArrayBuffer>, any, Doc, Transaction):void} DocEvents.update
|
|
22
22
|
* @property {function(Doc):void} DocEvents.beforeAllTransactions
|
|
23
23
|
* @property {function(Transaction, Doc):void} DocEvents.beforeTransaction
|
|
24
|
+
* @property {function(Transaction, Doc):void} DocEvents.beforeCommit
|
|
24
25
|
* @property {function(Transaction, Doc):void} DocEvents.commit
|
|
25
26
|
* @property {function(Transaction, Doc):void} DocEvents.afterTransaction
|
|
26
27
|
* @property {function(Doc, Array<Transaction>):void} DocEvents.afterAllTransactions
|
|
@@ -56,11 +57,12 @@ export class Doc extends ObservableV2 {
|
|
|
56
57
|
this.keepReplaced = null
|
|
57
58
|
this.clientID = generateNewClientId()
|
|
58
59
|
/**
|
|
59
|
-
* YP13: the greatest Lamport timestamp of a paired mark
|
|
60
|
-
* integrated (see `utils/marks.js
|
|
60
|
+
* YP13, YP22: the greatest Lamport timestamp of a paired mark or a move
|
|
61
|
+
* record this document integrated (see `utils/marks.js`,
|
|
62
|
+
* `structs/Item.js` `ContentMove`); the next one it writes takes `_clock + 1`.
|
|
61
63
|
* @type {number}
|
|
62
64
|
*/
|
|
63
|
-
this.
|
|
65
|
+
this._clock = 0
|
|
64
66
|
/**
|
|
65
67
|
* @type {Map<string, YNode>}
|
|
66
68
|
*/
|
|
@@ -18,6 +18,11 @@ export class StructStore {
|
|
|
18
18
|
* @type {null | Uint8Array<ArrayBuffer>}
|
|
19
19
|
*/
|
|
20
20
|
this.pendingDs = null
|
|
21
|
+
/**
|
|
22
|
+
* YP20: holds whose items have not arrived, `client → [clock, len, delta, …]`.
|
|
23
|
+
* @type {Map<number, Array<number>>|null}
|
|
24
|
+
*/
|
|
25
|
+
this.pendingHolds = null
|
|
21
26
|
this.skips = createIdSet()
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -15,7 +15,8 @@ export const generateNewClientId = random.uint53
|
|
|
15
15
|
* minimize the number of messages sent.
|
|
16
16
|
*
|
|
17
17
|
* YP19: a commit is told through the document's events (`commit`, then
|
|
18
|
-
* `afterTransaction`); nodes have no observers.
|
|
18
|
+
* `afterTransaction`); nodes have no observers. YP30: `beforeCommit` runs
|
|
19
|
+
* before them, inside the transaction.
|
|
19
20
|
*
|
|
20
21
|
* @public
|
|
21
22
|
*/
|
|
@@ -39,6 +40,19 @@ export class Transaction {
|
|
|
39
40
|
* Describes the set of inserted items by ids
|
|
40
41
|
*/
|
|
41
42
|
this.insertSet = createIdSet()
|
|
43
|
+
/**
|
|
44
|
+
* YP20: the visibility changes hold records made, in order: per item,
|
|
45
|
+
* `client, clock, len` (`len > 0`: shown, `< 0`: hidden; an item removed
|
|
46
|
+
* while held is logged shown, as the delete set counts it hidden).
|
|
47
|
+
* @type {Array<number>}
|
|
48
|
+
*/
|
|
49
|
+
this.holds = []
|
|
50
|
+
/**
|
|
51
|
+
* YP22: the units whose mover changed (a move record integrated or
|
|
52
|
+
* deleted, a moved unit removed), in order: per item `client, clock, len`.
|
|
53
|
+
* @type {Array<number>}
|
|
54
|
+
*/
|
|
55
|
+
this.moves = []
|
|
42
56
|
/**
|
|
43
57
|
* All types that were directly modified (property added or child
|
|
44
58
|
* inserted/deleted). New types are not included in this Set.
|
|
@@ -174,18 +188,27 @@ export const transact = (doc, f, origin = null, local = true) => {
|
|
|
174
188
|
result = f(doc._transaction)
|
|
175
189
|
} finally {
|
|
176
190
|
if (initialCall) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
191
|
+
try {
|
|
192
|
+
// YP30 begin: the outermost function returned (or threw), the
|
|
193
|
+
// transaction still open: a `beforeCommit` listener's writes join it.
|
|
194
|
+
if (doc._observers.has('beforeCommit')) {
|
|
195
|
+
doc.emit('beforeCommit', [doc._transaction, doc])
|
|
196
|
+
}
|
|
197
|
+
// YP30 end
|
|
198
|
+
} finally {
|
|
199
|
+
const finishCleanup = doc._transaction === transactionCleanups[0]
|
|
200
|
+
doc._transaction = null
|
|
201
|
+
if (finishCleanup) {
|
|
202
|
+
// The first transaction ended, now process observer calls.
|
|
203
|
+
// Observer call may create new transactions for which we need to call the observers and do cleanup.
|
|
204
|
+
// We don't want to nest these calls, so we execute these calls one after
|
|
205
|
+
// another.
|
|
206
|
+
// Also we need to ensure that all cleanups are called, even if the
|
|
207
|
+
// observes throw errors.
|
|
208
|
+
// This file is full of hacky try {} finally {} blocks to ensure that an
|
|
209
|
+
// event can throw errors and also that the cleanup is called.
|
|
210
|
+
cleanupTransactions(transactionCleanups, 0)
|
|
211
|
+
}
|
|
189
212
|
}
|
|
190
213
|
}
|
|
191
214
|
}
|
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
import * as decoding from 'lib0-v14/decoding'
|
|
2
|
+
import * as error from 'lib0-v14/error' // YP24
|
|
2
3
|
|
|
3
4
|
import { ID } from './ID.js'
|
|
5
|
+
import { UPDATE_ENCODING } from './UpdateEncoder.js' // YP24
|
|
6
|
+
|
|
7
|
+
// YP24 begin (edytor fork: one string table per update — see UPSTREAM.md YP24)
|
|
8
|
+
/**
|
|
9
|
+
* Read a content value written by `writeValue` (lib0's `readAny` layout, its
|
|
10
|
+
* strings and object keys from the update's string table).
|
|
11
|
+
*
|
|
12
|
+
* @param {UpdateDecoder} decoder
|
|
13
|
+
* @return {any}
|
|
14
|
+
*/
|
|
15
|
+
const readValue = decoder => {
|
|
16
|
+
const rest = decoder.restDecoder
|
|
17
|
+
switch (rest.arr[rest.pos]) {
|
|
18
|
+
case 115: // YP28: a large integer
|
|
19
|
+
rest.pos++
|
|
20
|
+
return decoding.readVarUint(rest)
|
|
21
|
+
case 114: // YP28: a large integer, through the client table, and a clock
|
|
22
|
+
rest.pos++
|
|
23
|
+
return [decoder.readClient(), decoding.readVarUint(rest)]
|
|
24
|
+
case 119:
|
|
25
|
+
rest.pos++
|
|
26
|
+
return decoder.readKey()
|
|
27
|
+
case 118: {
|
|
28
|
+
rest.pos++
|
|
29
|
+
/** @type {Object<string,any>} */
|
|
30
|
+
const obj = {}
|
|
31
|
+
for (let n = decoding.readVarUint(rest); n > 0; n--) {
|
|
32
|
+
const key = decoder.readKey()
|
|
33
|
+
obj[key] = readValue(decoder)
|
|
34
|
+
}
|
|
35
|
+
return obj
|
|
36
|
+
}
|
|
37
|
+
case 117: {
|
|
38
|
+
rest.pos++
|
|
39
|
+
const arr = []
|
|
40
|
+
for (let n = decoding.readVarUint(rest); n > 0; n--) arr.push(readValue(decoder))
|
|
41
|
+
return arr
|
|
42
|
+
}
|
|
43
|
+
default:
|
|
44
|
+
return decoding.readAny(rest)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// YP24 end
|
|
4
48
|
|
|
5
49
|
export class IdSetDecoder {
|
|
6
50
|
/**
|
|
@@ -18,6 +62,15 @@ export class IdSetDecoder {
|
|
|
18
62
|
this.dsCurrVal = 0
|
|
19
63
|
}
|
|
20
64
|
|
|
65
|
+
/**
|
|
66
|
+
* YP26: the client of a delete set's ranges.
|
|
67
|
+
*
|
|
68
|
+
* @return {number}
|
|
69
|
+
*/
|
|
70
|
+
readDsClient () {
|
|
71
|
+
return decoding.readVarUint(this.restDecoder)
|
|
72
|
+
}
|
|
73
|
+
|
|
21
74
|
/**
|
|
22
75
|
* @return {number}
|
|
23
76
|
*/
|
|
@@ -49,38 +102,60 @@ export class UpdateDecoder extends IdSetDecoder {
|
|
|
49
102
|
* @type {Array<string>}
|
|
50
103
|
*/
|
|
51
104
|
this.keys = []
|
|
52
|
-
|
|
53
|
-
|
|
105
|
+
// YP24: the feature flag names the encoding; another one (upstream's `0`) is refused
|
|
106
|
+
if (decoding.readVarUint(decoder) !== UPDATE_ENCODING) throw error.create('an update of another encoding')
|
|
107
|
+
this.keyClockDecoder = new decoding.UintOptRleDecoder(decoding.readVarUint8Array(decoder)) // YP24
|
|
54
108
|
this.clientDecoder = new decoding.UintOptRleDecoder(decoding.readVarUint8Array(decoder))
|
|
55
109
|
this.leftClockDecoder = new decoding.IntDiffOptRleDecoder(decoding.readVarUint8Array(decoder))
|
|
56
110
|
this.rightClockDecoder = new decoding.IntDiffOptRleDecoder(decoding.readVarUint8Array(decoder))
|
|
57
111
|
this.infoDecoder = new decoding.RleDecoder(decoding.readVarUint8Array(decoder), decoding.readUint8)
|
|
58
112
|
this.stringDecoder = new decoding.StringDecoder(decoding.readVarUint8Array(decoder))
|
|
59
|
-
this.parentInfoDecoder = new decoding.RleDecoder(decoding.readVarUint8Array(decoder), decoding.readUint8)
|
|
60
113
|
this.typeRefDecoder = new decoding.UintOptRleDecoder(decoding.readVarUint8Array(decoder))
|
|
61
114
|
this.lenDecoder = new decoding.UintOptRleDecoder(decoding.readVarUint8Array(decoder))
|
|
115
|
+
// YP26 begin: the update's client table, read as its indices are
|
|
116
|
+
/**
|
|
117
|
+
* @type {Array<number>}
|
|
118
|
+
*/
|
|
119
|
+
this.clients = []
|
|
120
|
+
// YP26 end
|
|
62
121
|
}
|
|
63
122
|
|
|
64
123
|
/**
|
|
65
124
|
* @return {ID}
|
|
66
125
|
*/
|
|
67
126
|
readLeftID () {
|
|
68
|
-
return new ID(this.
|
|
127
|
+
return new ID(this.readClient(), this.leftClockDecoder.read())
|
|
69
128
|
}
|
|
70
129
|
|
|
71
130
|
/**
|
|
72
131
|
* @return {ID}
|
|
73
132
|
*/
|
|
74
133
|
readRightID () {
|
|
75
|
-
return new ID(this.
|
|
134
|
+
return new ID(this.readClient(), this.rightClockDecoder.read())
|
|
76
135
|
}
|
|
77
136
|
|
|
78
137
|
/**
|
|
79
138
|
* Read the next client id.
|
|
80
139
|
* Use this in favor of readID whenever possible to reduce the number of objects created.
|
|
140
|
+
* YP26: an index in the update's client table (a new one's id follows it).
|
|
141
|
+
*
|
|
142
|
+
* @return {number}
|
|
81
143
|
*/
|
|
82
144
|
readClient () {
|
|
83
|
-
|
|
145
|
+
const index = this.clientDecoder.read()
|
|
146
|
+
const clients = this.clients
|
|
147
|
+
if (index === clients.length) clients.push(this.clientDecoder.read())
|
|
148
|
+
else if (index > clients.length) throw error.create('an unknown client')
|
|
149
|
+
return clients[index]
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* YP26: a delete set's client, from the client table.
|
|
154
|
+
*
|
|
155
|
+
* @return {number}
|
|
156
|
+
*/
|
|
157
|
+
readDsClient () {
|
|
158
|
+
return this.readClient()
|
|
84
159
|
}
|
|
85
160
|
|
|
86
161
|
/**
|
|
@@ -97,13 +172,6 @@ export class UpdateDecoder extends IdSetDecoder {
|
|
|
97
172
|
return this.stringDecoder.read()
|
|
98
173
|
}
|
|
99
174
|
|
|
100
|
-
/**
|
|
101
|
-
* @return {boolean}
|
|
102
|
-
*/
|
|
103
|
-
readParentInfo () {
|
|
104
|
-
return this.parentInfoDecoder.read() === 1
|
|
105
|
-
}
|
|
106
|
-
|
|
107
175
|
/**
|
|
108
176
|
* @return {number} An unsigned 8-bit integer
|
|
109
177
|
*/
|
|
@@ -124,7 +192,7 @@ export class UpdateDecoder extends IdSetDecoder {
|
|
|
124
192
|
* @return {any}
|
|
125
193
|
*/
|
|
126
194
|
readAny () {
|
|
127
|
-
return
|
|
195
|
+
return readValue(this) // YP24
|
|
128
196
|
}
|
|
129
197
|
|
|
130
198
|
/**
|
|
@@ -138,20 +206,22 @@ export class UpdateDecoder extends IdSetDecoder {
|
|
|
138
206
|
* @return {any}
|
|
139
207
|
*/
|
|
140
208
|
readJSON () {
|
|
141
|
-
return
|
|
209
|
+
return readValue(this) // YP24
|
|
142
210
|
}
|
|
143
211
|
|
|
144
212
|
/**
|
|
145
213
|
* @return {string}
|
|
146
214
|
*/
|
|
147
215
|
readKey () {
|
|
216
|
+
// YP24: `0` a new string, else the index of a known one plus one
|
|
148
217
|
const keyClock = this.keyClockDecoder.read()
|
|
149
|
-
if (keyClock
|
|
150
|
-
return this.keys[keyClock]
|
|
151
|
-
} else {
|
|
218
|
+
if (keyClock === 0) {
|
|
152
219
|
const key = this.stringDecoder.read()
|
|
153
220
|
this.keys.push(key)
|
|
154
221
|
return key
|
|
155
222
|
}
|
|
223
|
+
if (keyClock > this.keys.length) throw error.create('an unknown key')
|
|
224
|
+
return this.keys[keyClock - 1]
|
|
156
225
|
}
|
|
226
|
+
|
|
157
227
|
}
|
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
import * as error from 'lib0-v14/error'
|
|
2
2
|
import * as encoding from 'lib0-v14/encoding'
|
|
3
3
|
|
|
4
|
+
// YP24 begin (edytor fork: one string table per update — see UPSTREAM.md YP24)
|
|
5
|
+
/**
|
|
6
|
+
* The update encoding this fork writes and reads, in upstream's reserved
|
|
7
|
+
* feature-flag varuint (upstream writes `0`): `1`, a string table per update.
|
|
8
|
+
*/
|
|
9
|
+
export const UPDATE_ENCODING = 1
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* YP28: whether `x` is an integer past 31 bits a varint holds exactly.
|
|
13
|
+
*
|
|
14
|
+
* @param {any} x
|
|
15
|
+
* @return {boolean}
|
|
16
|
+
*/
|
|
17
|
+
const large = x => Number.isInteger(x) && x > 0x7fffffff && x <= Number.MAX_SAFE_INTEGER
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Write a content value in lib0's `writeAny` layout, its strings and object
|
|
21
|
+
* keys through the update's string table (`writeKey`).
|
|
22
|
+
*
|
|
23
|
+
* @param {UpdateEncoder} encoder
|
|
24
|
+
* @param {any} data
|
|
25
|
+
*/
|
|
26
|
+
const writeValue = (encoder, data) => {
|
|
27
|
+
const rest = encoder.restEncoder
|
|
28
|
+
if (typeof data === 'string') {
|
|
29
|
+
encoding.write(rest, 119)
|
|
30
|
+
encoder.writeKey(data)
|
|
31
|
+
} else if (large(data)) {
|
|
32
|
+
// YP28: an integer past lib0's 31 bits (a float64 there) as a varint
|
|
33
|
+
encoding.write(rest, 115)
|
|
34
|
+
encoding.writeVarUint(rest, data)
|
|
35
|
+
} else if (data !== null && typeof data === 'object' && !(data instanceof Uint8Array)) {
|
|
36
|
+
if (Array.isArray(data) && data.length === 2 && large(data[0]) && Number.isInteger(data[1]) && data[1] >= 0) {
|
|
37
|
+
// YP28: a pair of a large integer and a clock (an item id, a block
|
|
38
|
+
// slot's split key) names its first through the client table
|
|
39
|
+
encoding.write(rest, 114)
|
|
40
|
+
encoder.clientIndex(data[0])
|
|
41
|
+
encoding.writeVarUint(rest, data[1])
|
|
42
|
+
} else if (Array.isArray(data)) {
|
|
43
|
+
encoding.write(rest, 117)
|
|
44
|
+
encoding.writeVarUint(rest, data.length)
|
|
45
|
+
for (let i = 0; i < data.length; i++) writeValue(encoder, data[i])
|
|
46
|
+
} else {
|
|
47
|
+
const keys = Object.keys(data)
|
|
48
|
+
encoding.write(rest, 118)
|
|
49
|
+
encoding.writeVarUint(rest, keys.length)
|
|
50
|
+
for (let i = 0; i < keys.length; i++) {
|
|
51
|
+
encoder.writeKey(keys[i])
|
|
52
|
+
writeValue(encoder, data[keys[i]])
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
encoding.writeAny(rest, data)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// YP24 end
|
|
60
|
+
|
|
4
61
|
export class IdSetEncoder {
|
|
5
62
|
constructor () {
|
|
6
63
|
this.restEncoder = encoding.createEncoder() // encodes all the rest / non-optimized
|
|
@@ -15,6 +72,15 @@ export class IdSetEncoder {
|
|
|
15
72
|
this.dsCurrVal = 0
|
|
16
73
|
}
|
|
17
74
|
|
|
75
|
+
/**
|
|
76
|
+
* YP26: the client of a delete set's ranges.
|
|
77
|
+
*
|
|
78
|
+
* @param {number} client
|
|
79
|
+
*/
|
|
80
|
+
writeDsClient (client) {
|
|
81
|
+
encoding.writeVarUint(this.restEncoder, client)
|
|
82
|
+
}
|
|
83
|
+
|
|
18
84
|
/**
|
|
19
85
|
* @param {number} clock
|
|
20
86
|
*/
|
|
@@ -50,27 +116,52 @@ export class UpdateEncoder extends IdSetEncoder {
|
|
|
50
116
|
* @type {number}
|
|
51
117
|
*/
|
|
52
118
|
this.keyClock = 0
|
|
53
|
-
this.keyClockEncoder = new encoding.
|
|
119
|
+
this.keyClockEncoder = new encoding.UintOptRleEncoder() // YP24: 0 a new string, else its index + 1
|
|
54
120
|
this.clientEncoder = new encoding.UintOptRleEncoder()
|
|
55
121
|
this.leftClockEncoder = new encoding.IntDiffOptRleEncoder()
|
|
56
122
|
this.rightClockEncoder = new encoding.IntDiffOptRleEncoder()
|
|
57
123
|
this.infoEncoder = new encoding.RleEncoder(encoding.writeUint8)
|
|
58
124
|
this.stringEncoder = new encoding.StringEncoder()
|
|
59
|
-
this.parentInfoEncoder = new encoding.RleEncoder(encoding.writeUint8)
|
|
60
125
|
this.typeRefEncoder = new encoding.UintOptRleEncoder()
|
|
61
126
|
this.lenEncoder = new encoding.UintOptRleEncoder()
|
|
127
|
+
// YP26 begin: one client table per update
|
|
128
|
+
/**
|
|
129
|
+
* Each client's index, in first-use order.
|
|
130
|
+
*
|
|
131
|
+
* @type {Map<number,number>}
|
|
132
|
+
*/
|
|
133
|
+
this.clientTable = new Map()
|
|
134
|
+
// YP26 end
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* YP26: write `client` to the client column as its index in the update's
|
|
139
|
+
* client table, in first-use order: a new client's index (the table's
|
|
140
|
+
* size) is followed by its id, once.
|
|
141
|
+
*
|
|
142
|
+
* @param {number} client
|
|
143
|
+
*/
|
|
144
|
+
clientIndex (client) {
|
|
145
|
+
const index = this.clientTable.get(client)
|
|
146
|
+
if (index === undefined) {
|
|
147
|
+
const next = this.clientTable.size
|
|
148
|
+
this.clientTable.set(client, next)
|
|
149
|
+
this.clientEncoder.write(next)
|
|
150
|
+
this.clientEncoder.write(client)
|
|
151
|
+
} else {
|
|
152
|
+
this.clientEncoder.write(index)
|
|
153
|
+
}
|
|
62
154
|
}
|
|
63
155
|
|
|
64
156
|
toUint8Array () {
|
|
65
157
|
const encoder = encoding.createEncoder()
|
|
66
|
-
encoding.writeVarUint(encoder,
|
|
158
|
+
encoding.writeVarUint(encoder, UPDATE_ENCODING) // YP24: the feature flag names the encoding
|
|
67
159
|
encoding.writeVarUint8Array(encoder, this.keyClockEncoder.toUint8Array())
|
|
68
160
|
encoding.writeVarUint8Array(encoder, this.clientEncoder.toUint8Array())
|
|
69
161
|
encoding.writeVarUint8Array(encoder, this.leftClockEncoder.toUint8Array())
|
|
70
162
|
encoding.writeVarUint8Array(encoder, this.rightClockEncoder.toUint8Array())
|
|
71
163
|
encoding.writeVarUint8Array(encoder, encoding.toUint8Array(this.infoEncoder))
|
|
72
164
|
encoding.writeVarUint8Array(encoder, this.stringEncoder.toUint8Array())
|
|
73
|
-
encoding.writeVarUint8Array(encoder, encoding.toUint8Array(this.parentInfoEncoder))
|
|
74
165
|
encoding.writeVarUint8Array(encoder, this.typeRefEncoder.toUint8Array())
|
|
75
166
|
encoding.writeVarUint8Array(encoder, this.lenEncoder.toUint8Array())
|
|
76
167
|
// @note The rest encoder is appended! (note the missing var)
|
|
@@ -82,7 +173,7 @@ export class UpdateEncoder extends IdSetEncoder {
|
|
|
82
173
|
* @param {ID} id
|
|
83
174
|
*/
|
|
84
175
|
writeLeftID (id) {
|
|
85
|
-
this.
|
|
176
|
+
this.clientIndex(id.client) // YP26
|
|
86
177
|
this.leftClockEncoder.write(id.clock)
|
|
87
178
|
}
|
|
88
179
|
|
|
@@ -90,7 +181,7 @@ export class UpdateEncoder extends IdSetEncoder {
|
|
|
90
181
|
* @param {ID} id
|
|
91
182
|
*/
|
|
92
183
|
writeRightID (id) {
|
|
93
|
-
this.
|
|
184
|
+
this.clientIndex(id.client) // YP26
|
|
94
185
|
this.rightClockEncoder.write(id.clock)
|
|
95
186
|
}
|
|
96
187
|
|
|
@@ -98,7 +189,16 @@ export class UpdateEncoder extends IdSetEncoder {
|
|
|
98
189
|
* @param {number} client
|
|
99
190
|
*/
|
|
100
191
|
writeClient (client) {
|
|
101
|
-
this.
|
|
192
|
+
this.clientIndex(client) // YP26
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* YP26: a delete set's client goes through the client table too.
|
|
197
|
+
*
|
|
198
|
+
* @param {number} client
|
|
199
|
+
*/
|
|
200
|
+
writeDsClient (client) {
|
|
201
|
+
this.writeClient(client)
|
|
102
202
|
}
|
|
103
203
|
|
|
104
204
|
/**
|
|
@@ -115,13 +215,6 @@ export class UpdateEncoder extends IdSetEncoder {
|
|
|
115
215
|
this.stringEncoder.write(s)
|
|
116
216
|
}
|
|
117
217
|
|
|
118
|
-
/**
|
|
119
|
-
* @param {boolean} isYKey
|
|
120
|
-
*/
|
|
121
|
-
writeParentInfo (isYKey) {
|
|
122
|
-
this.parentInfoEncoder.write(isYKey ? 1 : 0)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
218
|
/**
|
|
126
219
|
* @param {number} info An unsigned 8-bit integer
|
|
127
220
|
*/
|
|
@@ -142,7 +235,7 @@ export class UpdateEncoder extends IdSetEncoder {
|
|
|
142
235
|
* @param {any} any
|
|
143
236
|
*/
|
|
144
237
|
writeAny (any) {
|
|
145
|
-
|
|
238
|
+
writeValue(this, any) // YP24
|
|
146
239
|
}
|
|
147
240
|
|
|
148
241
|
/**
|
|
@@ -156,7 +249,7 @@ export class UpdateEncoder extends IdSetEncoder {
|
|
|
156
249
|
* @param {any} embed
|
|
157
250
|
*/
|
|
158
251
|
writeJSON (embed) {
|
|
159
|
-
|
|
252
|
+
writeValue(this, embed) // YP24
|
|
160
253
|
}
|
|
161
254
|
|
|
162
255
|
/**
|
|
@@ -165,29 +258,23 @@ export class UpdateEncoder extends IdSetEncoder {
|
|
|
165
258
|
*
|
|
166
259
|
* We cache these keys in a Map and refer to them via a unique number.
|
|
167
260
|
*
|
|
261
|
+
* YP24: the cache is on (upstream left it off for decoders older than its
|
|
262
|
+
* own), and every map key, root name and string of a content value goes
|
|
263
|
+
* through it: an update writes each distinct string once. The key column
|
|
264
|
+
* says `0` for a new string (the next in the string column) and the index
|
|
265
|
+
* of a known one plus one, so a key repeated block after block is the same
|
|
266
|
+
* small number every time.
|
|
267
|
+
*
|
|
168
268
|
* @param {string} key
|
|
169
269
|
*/
|
|
170
270
|
writeKey (key) {
|
|
171
271
|
const clock = this.keyMap.get(key)
|
|
172
272
|
if (clock === undefined) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
*
|
|
176
|
-
* Background. The ContentFormat object was always encoded using writeKey, but the decoder used to use readString.
|
|
177
|
-
* Furthermore, I forgot to set the keyclock. So everything was working fine.
|
|
178
|
-
*
|
|
179
|
-
* However, this feature here is basically useless as it is not being used (it actually only consumes extra memory).
|
|
180
|
-
*
|
|
181
|
-
* I don't know yet how to reintroduce this feature..
|
|
182
|
-
*
|
|
183
|
-
* Older clients won't be able to read updates when we reintroduce this feature. So this should probably be done using a flag.
|
|
184
|
-
*
|
|
185
|
-
*/
|
|
186
|
-
// this.keyMap.set(key, this.keyClock)
|
|
187
|
-
this.keyClockEncoder.write(this.keyClock++)
|
|
273
|
+
this.keyMap.set(key, this.keyClock++)
|
|
274
|
+
this.keyClockEncoder.write(0)
|
|
188
275
|
this.stringEncoder.write(key)
|
|
189
276
|
} else {
|
|
190
|
-
this.keyClockEncoder.write(clock)
|
|
277
|
+
this.keyClockEncoder.write(clock + 1)
|
|
191
278
|
}
|
|
192
279
|
}
|
|
193
280
|
}
|
|
@@ -29,7 +29,7 @@ import { UpdateEncoder } from './UpdateEncoder.js'
|
|
|
29
29
|
import { LazyStructReader, LazyStructWriter, writeStructToLazyStructWriter, finishLazyStructWriting } from './updates.js'
|
|
30
30
|
import { readBlockSet, writeBlockSet } from './BlockSet.js'
|
|
31
31
|
import { Skip } from '../structs/Skip.js'
|
|
32
|
-
import { Item } from '../structs/Item.js'
|
|
32
|
+
import { Item, applyPending, ContentMove } from '../structs/Item.js' // YP20, YP22: applyPending, ContentMove
|
|
33
33
|
import { GC } from '../structs/GC.js'
|
|
34
34
|
import { Doc } from './Doc.js'
|
|
35
35
|
import { transact } from './Transaction.js'
|
|
@@ -287,6 +287,7 @@ export const readUpdate = (decoder, ydoc, transactionOrigin, structDecoder = new
|
|
|
287
287
|
// start = performance.now()
|
|
288
288
|
// console.log('time to merge: ', performance.now() - start) // @todo remove
|
|
289
289
|
// start = performance.now()
|
|
290
|
+
const arrived = Array.from(ss.clients.keys()) // YP20, YP22: a hold or a move of this update may wait for another of its clients
|
|
290
291
|
const restStructs = integrateStructs(transaction, store, ss)
|
|
291
292
|
const pending = store.pendingStructs
|
|
292
293
|
if (pending) {
|
|
@@ -312,6 +313,7 @@ export const readUpdate = (decoder, ydoc, transactionOrigin, structDecoder = new
|
|
|
312
313
|
}
|
|
313
314
|
// console.log('time to integrate: ', performance.now() - start) // @todo remove
|
|
314
315
|
// start = performance.now()
|
|
316
|
+
if (store.pendingHolds !== null) applyPending(transaction, arrived) // YP20: the holds that waited for what just arrived
|
|
315
317
|
const dsRest = readAndApplyDeleteSet(structDecoder, transaction, store)
|
|
316
318
|
if (store.pendingDs) {
|
|
317
319
|
// @todo we could make a lower-bound state-vector check as we do above
|
|
@@ -529,6 +531,16 @@ const getMissing = (struct, transaction, store) => {
|
|
|
529
531
|
if (parent && parent.constructor === ID && (parent.clock >= store.getClock(parent.client) || store.skips.hasId(parent))) {
|
|
530
532
|
return parent.client
|
|
531
533
|
}
|
|
534
|
+
if (struct.content.constructor === ContentMove) { // YP22: a move record waits for the units it names and stands at
|
|
535
|
+
// (a skip stands for none of them: its sort reads the units' order, `sourceOrder`)
|
|
536
|
+
const { ids, at } = /** @type {ContentMove} */ (struct.content)
|
|
537
|
+
if (at !== null) {
|
|
538
|
+
for (const id of at) if (id.clock >= store.getClock(id.client) || store.skips.hasId(id)) return id.client
|
|
539
|
+
}
|
|
540
|
+
for (let i = 0; i < ids.length; i += 3) {
|
|
541
|
+
if (ids[i + 1] + ids[i + 2] > store.getClock(ids[i]) || store.skips.intersects(ids[i], ids[i + 1], ids[i + 2])) return ids[i]
|
|
542
|
+
}
|
|
543
|
+
}
|
|
532
544
|
// We have all missing ids, now find the items
|
|
533
545
|
if (origin) {
|
|
534
546
|
struct.left = getItemCleanEnd(transaction, store, origin)
|