@dabble/patches 0.8.5 → 0.8.7
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/dist/client/LWWInMemoryStore.d.ts +8 -2
- package/dist/client/LWWInMemoryStore.js +11 -8
- package/dist/client/LWWIndexedDBStore.d.ts +8 -2
- package/dist/client/LWWIndexedDBStore.js +5 -14
- package/dist/client/OTInMemoryStore.js +2 -1
- package/dist/client/OTIndexedDBStore.js +1 -2
- package/dist/net/PatchesSync.d.ts +1 -1
- package/dist/net/PatchesSync.js +6 -6
- package/dist/server/LWWBranchManager.d.ts +2 -2
- package/dist/server/LWWServer.d.ts +2 -2
- package/dist/server/types.d.ts +1 -1
- package/dist/utils/concurrency.d.ts +28 -1
- package/dist/utils/concurrency.js +34 -0
- package/dist/vue/composables.js +20 -4
- package/package.json +1 -1
|
@@ -31,7 +31,7 @@ declare class LWWInMemoryStore implements LWWClientStore {
|
|
|
31
31
|
listDocs(includeDeleted?: boolean): Promise<TrackedDoc[]>;
|
|
32
32
|
/**
|
|
33
33
|
* Saves the current document state to storage.
|
|
34
|
-
* Clears
|
|
34
|
+
* Clears committed fields (subsumed by the snapshot) but preserves pending ops.
|
|
35
35
|
*/
|
|
36
36
|
saveDoc(docId: string, docState: PatchesState): Promise<void>;
|
|
37
37
|
/**
|
|
@@ -71,7 +71,13 @@ declare class LWWInMemoryStore implements LWWClientStore {
|
|
|
71
71
|
*/
|
|
72
72
|
saveSendingChange(docId: string, change: Change): Promise<void>;
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
74
|
+
* Move sending ops to committed, then clear the sending slot.
|
|
75
|
+
* committedRev is NOT updated here — applyServerChanges owns that using the
|
|
76
|
+
* server's actual rev. Updating it here would bump the rev above the server's
|
|
77
|
+
* real value for noop changes (where the server doesn't create a new rev).
|
|
78
|
+
*
|
|
79
|
+
* Call this BEFORE applyServerChanges so that server corrections (which run
|
|
80
|
+
* after) overwrite any stale ops for fields the server won via LWW.
|
|
75
81
|
*/
|
|
76
82
|
confirmSendingChange(docId: string): Promise<void>;
|
|
77
83
|
/**
|
|
@@ -51,14 +51,15 @@ class LWWInMemoryStore {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Saves the current document state to storage.
|
|
54
|
-
* Clears
|
|
54
|
+
* Clears committed fields (subsumed by the snapshot) but preserves pending ops.
|
|
55
55
|
*/
|
|
56
56
|
async saveDoc(docId, docState) {
|
|
57
|
+
const existing = this.docs.get(docId);
|
|
57
58
|
this.docs.set(docId, {
|
|
58
59
|
snapshot: { state: docState.state, rev: docState.rev },
|
|
59
60
|
committedFields: /* @__PURE__ */ new Map(),
|
|
60
|
-
pendingOps: /* @__PURE__ */ new Map(),
|
|
61
|
-
sendingChange: null,
|
|
61
|
+
pendingOps: existing?.pendingOps ?? /* @__PURE__ */ new Map(),
|
|
62
|
+
sendingChange: existing?.sendingChange ?? null,
|
|
62
63
|
committedRev: docState.rev
|
|
63
64
|
});
|
|
64
65
|
}
|
|
@@ -150,7 +151,13 @@ class LWWInMemoryStore {
|
|
|
150
151
|
buf.pendingOps.clear();
|
|
151
152
|
}
|
|
152
153
|
/**
|
|
153
|
-
*
|
|
154
|
+
* Move sending ops to committed, then clear the sending slot.
|
|
155
|
+
* committedRev is NOT updated here — applyServerChanges owns that using the
|
|
156
|
+
* server's actual rev. Updating it here would bump the rev above the server's
|
|
157
|
+
* real value for noop changes (where the server doesn't create a new rev).
|
|
158
|
+
*
|
|
159
|
+
* Call this BEFORE applyServerChanges so that server corrections (which run
|
|
160
|
+
* after) overwrite any stale ops for fields the server won via LWW.
|
|
154
161
|
*/
|
|
155
162
|
async confirmSendingChange(docId) {
|
|
156
163
|
const buf = this.docs.get(docId);
|
|
@@ -158,10 +165,6 @@ class LWWInMemoryStore {
|
|
|
158
165
|
for (const op of buf.sendingChange.ops) {
|
|
159
166
|
buf.committedFields.set(op.path, op.value);
|
|
160
167
|
}
|
|
161
|
-
const changeRev = buf.sendingChange.rev;
|
|
162
|
-
if (changeRev !== void 0 && changeRev > buf.committedRev) {
|
|
163
|
-
buf.committedRev = changeRev;
|
|
164
|
-
}
|
|
165
168
|
buf.sendingChange = null;
|
|
166
169
|
}
|
|
167
170
|
/**
|
|
@@ -73,7 +73,7 @@ declare class LWWIndexedDBStore implements LWWClientStore {
|
|
|
73
73
|
getDoc(docId: string): Promise<PatchesSnapshot | undefined>;
|
|
74
74
|
/**
|
|
75
75
|
* Saves the current document state to storage.
|
|
76
|
-
* Clears
|
|
76
|
+
* Clears committed fields (subsumed by the snapshot) but preserves pending ops.
|
|
77
77
|
*/
|
|
78
78
|
saveDoc(docId: string, docState: PatchesState): Promise<void>;
|
|
79
79
|
/**
|
|
@@ -101,7 +101,13 @@ declare class LWWIndexedDBStore implements LWWClientStore {
|
|
|
101
101
|
*/
|
|
102
102
|
saveSendingChange(docId: string, change: Change): Promise<void>;
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
104
|
+
* Move sending ops to committed, then clear the sending slot.
|
|
105
|
+
* committedRev is NOT updated here — applyServerChanges owns that using the
|
|
106
|
+
* server's actual rev. Updating it here would bump the rev above the server's
|
|
107
|
+
* real value for noop changes (where the server doesn't create a new rev).
|
|
108
|
+
*
|
|
109
|
+
* Call this BEFORE applyServerChanges so that server corrections (which run
|
|
110
|
+
* after) overwrite any stale ops for fields the server won via LWW.
|
|
105
111
|
*/
|
|
106
112
|
confirmSendingChange(docId: string): Promise<void>;
|
|
107
113
|
/**
|
|
@@ -122,17 +122,15 @@ class LWWIndexedDBStore {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
async saveDoc(docId, docState) {
|
|
125
|
-
const [tx, snapshots, committedOps,
|
|
126
|
-
["snapshots", "committedOps", "
|
|
125
|
+
const [tx, snapshots, committedOps, docsStore] = await this.db.transaction(
|
|
126
|
+
["snapshots", "committedOps", "docs"],
|
|
127
127
|
"readwrite"
|
|
128
128
|
);
|
|
129
129
|
const { rev, state } = docState;
|
|
130
130
|
await Promise.all([
|
|
131
131
|
docsStore.put({ docId, committedRev: rev, algorithm: "lww" }),
|
|
132
132
|
snapshots.put({ docId, state, rev }),
|
|
133
|
-
this.deleteFieldsForDoc(committedOps, docId)
|
|
134
|
-
this.deleteFieldsForDoc(pendingOps, docId),
|
|
135
|
-
sendingChanges.delete(docId)
|
|
133
|
+
this.deleteFieldsForDoc(committedOps, docId)
|
|
136
134
|
]);
|
|
137
135
|
await tx.complete();
|
|
138
136
|
}
|
|
@@ -233,8 +231,8 @@ class LWWIndexedDBStore {
|
|
|
233
231
|
await tx.complete();
|
|
234
232
|
}
|
|
235
233
|
async confirmSendingChange(docId) {
|
|
236
|
-
const [tx, sendingChanges, committedOps
|
|
237
|
-
["sendingChanges", "committedOps"
|
|
234
|
+
const [tx, sendingChanges, committedOps] = await this.db.transaction(
|
|
235
|
+
["sendingChanges", "committedOps"],
|
|
238
236
|
"readwrite"
|
|
239
237
|
);
|
|
240
238
|
const sending = await sendingChanges.get(docId);
|
|
@@ -243,13 +241,6 @@ class LWWIndexedDBStore {
|
|
|
243
241
|
return;
|
|
244
242
|
}
|
|
245
243
|
await Promise.all(sending.change.ops.map((op) => committedOps.put({ ...op, docId })));
|
|
246
|
-
const changeRev = sending.change.rev;
|
|
247
|
-
if (changeRev !== void 0) {
|
|
248
|
-
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "lww" };
|
|
249
|
-
if (changeRev > docMeta.committedRev) {
|
|
250
|
-
await docsStore.put({ ...docMeta, committedRev: changeRev });
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
244
|
await sendingChanges.delete(docId);
|
|
254
245
|
await tx.complete();
|
|
255
246
|
}
|
|
@@ -31,10 +31,11 @@ class OTInMemoryStore {
|
|
|
31
31
|
}
|
|
32
32
|
// ─── Writes ────────────────────────────────────────────────────────────
|
|
33
33
|
async saveDoc(docId, snapshot) {
|
|
34
|
+
const existing = this.docs.get(docId);
|
|
34
35
|
this.docs.set(docId, {
|
|
35
36
|
snapshot,
|
|
36
37
|
committed: [],
|
|
37
|
-
pending: []
|
|
38
|
+
pending: existing?.pending ?? []
|
|
38
39
|
});
|
|
39
40
|
}
|
|
40
41
|
async savePendingChanges(docId, changes) {
|
|
@@ -119,8 +119,7 @@ class OTIndexedDBStore {
|
|
|
119
119
|
await Promise.all([
|
|
120
120
|
docsStore.put({ docId, committedRev: rev, algorithm: "ot" }),
|
|
121
121
|
snapshots.put({ docId, state, rev }),
|
|
122
|
-
committedChanges.delete([docId, 0], [docId, Infinity])
|
|
123
|
-
pendingChanges.delete([docId, 0], [docId, Infinity])
|
|
122
|
+
committedChanges.delete([docId, 0], [docId, Infinity])
|
|
124
123
|
]);
|
|
125
124
|
await tx.complete();
|
|
126
125
|
}
|
|
@@ -144,7 +144,7 @@ declare class PatchesSync extends ReadonlyStoreClass<PatchesSyncState> {
|
|
|
144
144
|
protected _handleConnectionChange(connectionState: ConnectionState): void;
|
|
145
145
|
protected _handleDocsTracked(docIds: string[], algorithmName?: AlgorithmName): Promise<void>;
|
|
146
146
|
protected _handleDocsUntracked(docIds: string[]): Promise<void>;
|
|
147
|
-
protected _handleDocChange(docId: string):
|
|
147
|
+
protected _handleDocChange(docId: string): void;
|
|
148
148
|
/**
|
|
149
149
|
* Unified handler for remote document deletion (both real-time notifications and offline discovery).
|
|
150
150
|
* Cleans up local state and notifies the application with any pending changes that were lost.
|
package/dist/net/PatchesSync.js
CHANGED
|
@@ -12,7 +12,7 @@ import { breakChangesIntoBatches } from "../algorithms/ot/shared/changeBatching.
|
|
|
12
12
|
import { BaseDoc } from "../client/BaseDoc.js";
|
|
13
13
|
import { Patches } from "../client/Patches.js";
|
|
14
14
|
import { isDocLoaded } from "../shared/utils.js";
|
|
15
|
-
import { blockable } from "../utils/concurrency.js";
|
|
15
|
+
import { blockable, serialGate } from "../utils/concurrency.js";
|
|
16
16
|
import { ErrorCodes, StatusError } from "./error.js";
|
|
17
17
|
import { PatchesWebSocket } from "./websocket/PatchesWebSocket.js";
|
|
18
18
|
import { onlineState } from "./websocket/onlineState.js";
|
|
@@ -22,7 +22,7 @@ const EMPTY_DOC_STATE = {
|
|
|
22
22
|
syncStatus: "unsynced",
|
|
23
23
|
isLoaded: false
|
|
24
24
|
};
|
|
25
|
-
class PatchesSync extends (_a = ReadonlyStoreClass, _syncDoc_dec = [
|
|
25
|
+
class PatchesSync extends (_a = ReadonlyStoreClass, _syncDoc_dec = [serialGate], __receiveCommittedChanges_dec = [blockable], _a) {
|
|
26
26
|
constructor(patches, urlOrConnection, options) {
|
|
27
27
|
super({
|
|
28
28
|
online: onlineState.isOnline,
|
|
@@ -232,7 +232,7 @@ class PatchesSync extends (_a = ReadonlyStoreClass, _syncDoc_dec = [blockable],
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
async syncDoc(docId) {
|
|
235
|
-
if (!this.state.connected) return;
|
|
235
|
+
if (!this.state.connected || !this.trackedDocs.has(docId)) return;
|
|
236
236
|
this._updateDocSyncState(docId, { syncStatus: "syncing" });
|
|
237
237
|
const doc = this.patches.getOpenDoc(docId);
|
|
238
238
|
const algorithm = this._getAlgorithm(docId);
|
|
@@ -318,8 +318,8 @@ class PatchesSync extends (_a = ReadonlyStoreClass, _syncDoc_dec = [blockable],
|
|
|
318
318
|
openDoc.import({ ...snapshot, changes: [] });
|
|
319
319
|
}
|
|
320
320
|
} else {
|
|
321
|
-
await this._applyServerChangesToDoc(docId, committed);
|
|
322
321
|
await algorithm.confirmSent(docId, changeBatch);
|
|
322
|
+
await this._applyServerChangesToDoc(docId, committed);
|
|
323
323
|
}
|
|
324
324
|
pending = await algorithm.getPendingToSend(docId) ?? [];
|
|
325
325
|
}
|
|
@@ -460,11 +460,11 @@ class PatchesSync extends (_a = ReadonlyStoreClass, _syncDoc_dec = [blockable],
|
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
}
|
|
463
|
-
|
|
463
|
+
_handleDocChange(docId) {
|
|
464
464
|
if (!this.trackedDocs.has(docId)) return;
|
|
465
465
|
this._updateDocSyncState(docId, { hasPending: true });
|
|
466
466
|
if (!this.state.connected) return;
|
|
467
|
-
|
|
467
|
+
this.syncDoc(docId);
|
|
468
468
|
}
|
|
469
469
|
/**
|
|
470
470
|
* Unified handler for remote document deletion (both real-time notifications and offline discovery).
|
|
@@ -5,10 +5,10 @@ import { LWWServer } from './LWWServer.js';
|
|
|
5
5
|
import { LWWStoreBackend, BranchingStoreBackend } from './types.js';
|
|
6
6
|
import 'easy-signal';
|
|
7
7
|
import '../net/websocket/AuthorizationProvider.js';
|
|
8
|
-
import '../
|
|
8
|
+
import '../json-patch/types.js';
|
|
9
9
|
import '../json-patch/JSONPatch.js';
|
|
10
10
|
import '@dabble/delta';
|
|
11
|
-
import '../
|
|
11
|
+
import '../net/protocol/types.js';
|
|
12
12
|
import './PatchesServer.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -4,10 +4,10 @@ import { Change, CommitChangesOptions, DeleteDocOptions, ChangeInput, ChangeMuta
|
|
|
4
4
|
import { PatchesServer } from './PatchesServer.js';
|
|
5
5
|
import { LWWStoreBackend } from './types.js';
|
|
6
6
|
import '../net/websocket/AuthorizationProvider.js';
|
|
7
|
-
import '../
|
|
7
|
+
import '../json-patch/types.js';
|
|
8
8
|
import '../json-patch/JSONPatch.js';
|
|
9
9
|
import '@dabble/delta';
|
|
10
|
-
import '../
|
|
10
|
+
import '../net/protocol/types.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Configuration options for LWWServer.
|
package/dist/server/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSONPatchOp } from '../json-patch/types.js';
|
|
2
|
-
import { VersionMetadata, Change, ListVersionsOptions, EditableVersionMetadata, ListChangesOptions,
|
|
2
|
+
import { DocumentTombstone, VersionMetadata, Change, ListVersionsOptions, EditableVersionMetadata, ListChangesOptions, Branch } from '../types.js';
|
|
3
3
|
import '../json-patch/JSONPatch.js';
|
|
4
4
|
import '@dabble/delta';
|
|
5
5
|
|
|
@@ -18,6 +18,33 @@ declare function blocking<T extends (docId: string, ...args: any[]) => Promise<a
|
|
|
18
18
|
* Also, a Typescript decorator for functions whose response should be blocked when needed.
|
|
19
19
|
*/
|
|
20
20
|
declare function blockableResponse<T extends (docId: string, ...args: any[]) => Promise<any>>(target: T): T;
|
|
21
|
+
/**
|
|
22
|
+
* Wraps a function so that only one invocation per key runs at a time.
|
|
23
|
+
* While in-flight, any additional calls for the same key are collapsed into
|
|
24
|
+
* exactly one queued follow-up. When the in-flight call finishes, the follow-up
|
|
25
|
+
* runs once (picking up all work that accumulated in the window). Further calls
|
|
26
|
+
* while the follow-up is itself in-flight queue another single follow-up, and
|
|
27
|
+
* so on — naturally serialising all work without ever dropping it.
|
|
28
|
+
*
|
|
29
|
+
* Contrast with `singleInvocation(true)`, which collapses concurrent calls but
|
|
30
|
+
* does not schedule a follow-up, so work that arrives mid-flight is lost until
|
|
31
|
+
* the next external trigger.
|
|
32
|
+
*
|
|
33
|
+
* ### Example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const syncDoc = serialGate(async (docId: string) => {
|
|
36
|
+
* const pending = await store.getPending(docId);
|
|
37
|
+
* await server.commit(docId, pending);
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* // Three rapid calls: only one commitChanges in-flight at a time,
|
|
41
|
+
* // one follow-up picks up everything that arrived during the window.
|
|
42
|
+
* syncDoc('doc1');
|
|
43
|
+
* syncDoc('doc1');
|
|
44
|
+
* syncDoc('doc1');
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
declare function serialGate<T extends (key: string, ...args: any[]) => Promise<void>>(target: T): T;
|
|
21
48
|
/**
|
|
22
49
|
* Wrap a function to only return the result of the first call.
|
|
23
50
|
*
|
|
@@ -30,4 +57,4 @@ declare function blockableResponse<T extends (docId: string, ...args: any[]) =>
|
|
|
30
57
|
declare function singleInvocation<T extends (...args: any[]) => Promise<any>>(target: T): T;
|
|
31
58
|
declare function singleInvocation<T extends (...args: any[]) => Promise<any>>(matchOnFirstArg: boolean): (target: T) => T;
|
|
32
59
|
|
|
33
|
-
export { blockable, blockableResponse, blocking, releaseConcurrency, singleInvocation };
|
|
60
|
+
export { blockable, blockableResponse, blocking, releaseConcurrency, serialGate, singleInvocation };
|
|
@@ -27,6 +27,39 @@ function blockableResponse(target) {
|
|
|
27
27
|
return concurrency(args[0]).blockResponse(target.apply(this, args));
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
+
function serialGate(target) {
|
|
31
|
+
const instances = /* @__PURE__ */ new WeakMap();
|
|
32
|
+
function getState(thisArg) {
|
|
33
|
+
let state = instances.get(thisArg);
|
|
34
|
+
if (!state) {
|
|
35
|
+
state = { inFlight: /* @__PURE__ */ new Map(), queued: /* @__PURE__ */ new Set() };
|
|
36
|
+
instances.set(thisArg, state);
|
|
37
|
+
}
|
|
38
|
+
return state;
|
|
39
|
+
}
|
|
40
|
+
function run(thisArg, key, args) {
|
|
41
|
+
const { inFlight, queued } = getState(thisArg);
|
|
42
|
+
const promise = target.apply(thisArg, [key, ...args]).finally(() => {
|
|
43
|
+
if (inFlight.get(key) === promise) {
|
|
44
|
+
inFlight.delete(key);
|
|
45
|
+
if (queued.has(key)) {
|
|
46
|
+
queued.delete(key);
|
|
47
|
+
run(thisArg, key, args);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
inFlight.set(key, promise);
|
|
52
|
+
return promise;
|
|
53
|
+
}
|
|
54
|
+
return function(key, ...args) {
|
|
55
|
+
const { inFlight, queued } = getState(this);
|
|
56
|
+
if (inFlight.has(key)) {
|
|
57
|
+
queued.add(key);
|
|
58
|
+
return inFlight.get(key);
|
|
59
|
+
}
|
|
60
|
+
return run(this, key, args);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
30
63
|
function singleInvocation(matchOnFirstArgOrTarget) {
|
|
31
64
|
if (typeof matchOnFirstArgOrTarget === "function") {
|
|
32
65
|
return singleInvocation(false)(matchOnFirstArgOrTarget);
|
|
@@ -50,5 +83,6 @@ export {
|
|
|
50
83
|
blockableResponse,
|
|
51
84
|
blocking,
|
|
52
85
|
releaseConcurrency,
|
|
86
|
+
serialGate,
|
|
53
87
|
singleInvocation
|
|
54
88
|
};
|
package/dist/vue/composables.js
CHANGED
|
@@ -100,13 +100,21 @@ function _usePatchesDocEager(docId, options) {
|
|
|
100
100
|
});
|
|
101
101
|
let unsubscribe = null;
|
|
102
102
|
if (autoClose) {
|
|
103
|
+
let unmounted = false;
|
|
103
104
|
manager.openDoc(patches, docId, openDocOpts).then((patchesDoc) => {
|
|
105
|
+
if (unmounted) {
|
|
106
|
+
manager.closeDoc(patches, docId, shouldUntrack);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
104
109
|
unsubscribe = setupDoc(patchesDoc);
|
|
105
110
|
}).catch((err) => {
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
if (!unmounted) {
|
|
112
|
+
baseReturn.error.value = err;
|
|
113
|
+
baseReturn.loading.value = false;
|
|
114
|
+
}
|
|
108
115
|
});
|
|
109
116
|
onBeforeUnmount(() => {
|
|
117
|
+
unmounted = true;
|
|
110
118
|
unsubscribe?.();
|
|
111
119
|
manager.closeDoc(patches, docId, shouldUntrack);
|
|
112
120
|
});
|
|
@@ -219,6 +227,7 @@ function providePatchesDoc(name, docId, options = {}) {
|
|
|
219
227
|
});
|
|
220
228
|
const currentDocId = ref(unref(docId));
|
|
221
229
|
let unsubscribe = null;
|
|
230
|
+
let providerUnmounted = false;
|
|
222
231
|
async function initDoc(id) {
|
|
223
232
|
currentDocId.value = id;
|
|
224
233
|
unsubscribe?.();
|
|
@@ -226,10 +235,16 @@ function providePatchesDoc(name, docId, options = {}) {
|
|
|
226
235
|
if (autoClose) {
|
|
227
236
|
try {
|
|
228
237
|
const patchesDoc = await manager.openDoc(patches, id, openDocOpts);
|
|
238
|
+
if (providerUnmounted || currentDocId.value !== id) {
|
|
239
|
+
manager.closeDoc(patches, id, shouldUntrack);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
229
242
|
unsubscribe = setupDoc(patchesDoc);
|
|
230
243
|
} catch (err) {
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
if (!providerUnmounted && currentDocId.value === id) {
|
|
245
|
+
baseReturn.error.value = err;
|
|
246
|
+
baseReturn.loading.value = false;
|
|
247
|
+
}
|
|
233
248
|
}
|
|
234
249
|
} else {
|
|
235
250
|
try {
|
|
@@ -264,6 +279,7 @@ function providePatchesDoc(name, docId, options = {}) {
|
|
|
264
279
|
});
|
|
265
280
|
}
|
|
266
281
|
onBeforeUnmount(async () => {
|
|
282
|
+
providerUnmounted = true;
|
|
267
283
|
unsubscribe?.();
|
|
268
284
|
if (autoClose) {
|
|
269
285
|
await manager.closeDoc(patches, currentDocId.value, shouldUntrack);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dabble/patches",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"description": "Immutable JSON Patch implementation based on RFC 6902 supporting operational transformation and last-writer-wins",
|
|
5
5
|
"author": "Jacob Wright <jacwright@gmail.com>",
|
|
6
6
|
"bugs": {
|