@dabble/patches 0.7.15 → 0.7.16
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 +4 -4
- package/dist/{BaseDoc-vaMreJL5.d.ts → BaseDoc-BRIP2YZp.d.ts} +16 -35
- package/dist/algorithms/ot/server/commitChanges.d.ts +1 -1
- package/dist/client/BaseDoc.d.ts +2 -2
- package/dist/client/BaseDoc.js +13 -38
- package/dist/client/ClientAlgorithm.d.ts +2 -2
- package/dist/client/IndexedDBStore.d.ts +2 -2
- package/dist/client/IndexedDBStore.js +3 -8
- package/dist/client/LWWAlgorithm.d.ts +2 -2
- package/dist/client/LWWDoc.d.ts +3 -3
- package/dist/client/LWWDoc.js +10 -7
- package/dist/client/LWWIndexedDBStore.d.ts +1 -1
- package/dist/client/LWWIndexedDBStore.js +4 -4
- package/dist/client/OTAlgorithm.d.ts +2 -2
- package/dist/client/OTDoc.d.ts +2 -2
- package/dist/client/OTDoc.js +7 -8
- package/dist/client/OTIndexedDBStore.d.ts +1 -1
- package/dist/client/OTIndexedDBStore.js +3 -3
- package/dist/client/Patches.d.ts +9 -8
- package/dist/client/Patches.js +5 -2
- package/dist/client/PatchesDoc.d.ts +2 -2
- package/dist/client/PatchesHistoryClient.d.ts +5 -11
- package/dist/client/PatchesHistoryClient.js +14 -29
- package/dist/client/factories.d.ts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +23 -2
- package/dist/net/PatchesClient.d.ts +3 -3
- package/dist/net/PatchesClient.js +1 -1
- package/dist/net/PatchesSync.d.ts +13 -28
- package/dist/net/PatchesSync.js +68 -87
- package/dist/net/http/FetchTransport.d.ts +2 -2
- package/dist/net/http/FetchTransport.js +1 -1
- package/dist/net/index.d.ts +2 -2
- package/dist/net/protocol/JSONRPCClient.d.ts +1 -1
- package/dist/net/protocol/JSONRPCClient.js +1 -1
- package/dist/net/protocol/JSONRPCServer.d.ts +1 -1
- package/dist/net/protocol/JSONRPCServer.js +1 -1
- package/dist/net/protocol/types.d.ts +1 -1
- package/dist/net/protocol/utils.d.ts +1 -1
- package/dist/net/webrtc/WebRTCAwareness.d.ts +2 -2
- package/dist/net/webrtc/WebRTCAwareness.js +1 -1
- package/dist/net/webrtc/WebRTCTransport.d.ts +5 -5
- package/dist/net/webrtc/WebRTCTransport.js +1 -1
- package/dist/net/webrtc/index.d.ts +1 -1
- package/dist/net/websocket/PatchesWebSocket.d.ts +1 -1
- package/dist/net/websocket/PatchesWebSocket.js +1 -1
- package/dist/net/websocket/SignalingService.d.ts +1 -1
- package/dist/net/websocket/WebSocketServer.d.ts +1 -1
- package/dist/net/websocket/WebSocketTransport.d.ts +4 -3
- package/dist/net/websocket/WebSocketTransport.js +1 -1
- package/dist/net/websocket/onlineState.d.ts +2 -2
- package/dist/net/websocket/onlineState.js +1 -1
- package/dist/server/LWWBranchManager.d.ts +1 -1
- package/dist/server/LWWServer.d.ts +3 -3
- package/dist/server/LWWServer.js +1 -1
- package/dist/server/OTBranchManager.d.ts +1 -1
- package/dist/server/OTServer.d.ts +3 -3
- package/dist/server/OTServer.js +1 -1
- package/dist/server/PatchesHistoryManager.d.ts +1 -1
- package/dist/server/PatchesServer.d.ts +1 -1
- package/dist/server/branchUtils.d.ts +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/shared/doc-manager.d.ts +2 -2
- package/dist/shared/utils.d.ts +2 -2
- package/dist/shared/utils.js +2 -2
- package/dist/solid/context.d.ts +2 -2
- package/dist/solid/doc-manager.d.ts +2 -2
- package/dist/solid/index.d.ts +2 -2
- package/dist/solid/managed-docs.js +1 -1
- package/dist/solid/primitives.d.ts +2 -2
- package/dist/solid/primitives.js +7 -7
- package/dist/types.d.ts +4 -4
- package/dist/vue/composables.d.ts +2 -2
- package/dist/vue/composables.js +7 -7
- package/dist/vue/doc-manager.d.ts +2 -2
- package/dist/vue/index.d.ts +2 -2
- package/dist/vue/managed-docs.d.ts +2 -2
- package/dist/vue/managed-docs.js +1 -1
- package/dist/vue/provider.d.ts +2 -2
- package/package.json +2 -1
- package/dist/event-signal.d.ts +0 -32
- package/dist/event-signal.js +0 -25
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ await sync.connect();
|
|
|
114
114
|
const doc = await patches.openDoc<MyDoc>('my-doc-1');
|
|
115
115
|
|
|
116
116
|
// 5. React to updates
|
|
117
|
-
doc.
|
|
117
|
+
doc.subscribe(newState => {
|
|
118
118
|
console.log('Document updated:', newState);
|
|
119
119
|
// Update your UI here
|
|
120
120
|
});
|
|
@@ -265,7 +265,7 @@ See [Awareness documentation](./docs/awareness.md) for implementation details.
|
|
|
265
265
|
1. Create a `Patches` instance with strategies
|
|
266
266
|
2. Connect `PatchesSync` to your server
|
|
267
267
|
3. Open documents with `patches.openDoc(docId)`
|
|
268
|
-
4. Subscribe to updates with `doc.
|
|
268
|
+
4. Subscribe to updates with `doc.subscribe()`
|
|
269
269
|
5. Make changes with `doc.change()` - they sync automatically
|
|
270
270
|
|
|
271
271
|
### Server
|
|
@@ -297,7 +297,7 @@ const patches = new Patches({
|
|
|
297
297
|
const sync = new PatchesSync(patches, 'wss://api.example.com/sync');
|
|
298
298
|
|
|
299
299
|
// Handle connection state
|
|
300
|
-
sync.
|
|
300
|
+
sync.subscribe(state => {
|
|
301
301
|
if (state.connected) {
|
|
302
302
|
console.log('Connected and syncing');
|
|
303
303
|
} else if (!state.online) {
|
|
@@ -315,7 +315,7 @@ await sync.connect();
|
|
|
315
315
|
// Open and use a document
|
|
316
316
|
const doc = await patches.openDoc<MyDoc>('doc-123');
|
|
317
317
|
|
|
318
|
-
doc.
|
|
318
|
+
doc.subscribe(state => {
|
|
319
319
|
renderUI(state);
|
|
320
320
|
});
|
|
321
321
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as easy_signal from 'easy-signal';
|
|
2
|
+
import { ReadonlyStore, Store, Signal, ReadonlyStoreClass } from 'easy-signal';
|
|
2
3
|
import { JSONPatchOp } from './json-patch/types.js';
|
|
3
|
-
import { Change, PatchesSnapshot,
|
|
4
|
+
import { Change, PatchesSnapshot, DocSyncStatus, ChangeMutator } from './types.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* OT (Operational Transformation) document implementation.
|
|
@@ -14,7 +15,7 @@ import { Change, PatchesSnapshot, SyncedDoc, DocSyncStatus, ChangeMutator } from
|
|
|
14
15
|
* ## State Model
|
|
15
16
|
* - `_committedState`: Base state from server (at `_committedRev`)
|
|
16
17
|
* - `_pendingChanges`: Local changes not yet committed by server
|
|
17
|
-
* - `
|
|
18
|
+
* - `state` (getter from BaseDoc): Live state = committedState + pendingChanges applied
|
|
18
19
|
*
|
|
19
20
|
* ## Wire Efficiency
|
|
20
21
|
* For Worker-Tab communication, only changes are sent over the wire (not full state).
|
|
@@ -98,33 +99,25 @@ interface PatchesDocOptions {
|
|
|
98
99
|
*
|
|
99
100
|
* Internal methods (updateSyncStatus, applyChanges, import) are on BaseDoc, not this interface.
|
|
100
101
|
*/
|
|
101
|
-
interface PatchesDoc<T extends object = object> extends
|
|
102
|
+
interface PatchesDoc<T extends object = object> extends ReadonlyStore<T> {
|
|
102
103
|
/** The unique identifier for this document. */
|
|
103
104
|
readonly id: string;
|
|
104
|
-
/** Current local state (committed + pending merged). */
|
|
105
|
-
readonly state: T;
|
|
106
105
|
/** Last committed revision number from the server. */
|
|
107
106
|
readonly committedRev: number;
|
|
108
107
|
/** Are there local changes that haven't been committed yet? */
|
|
109
108
|
readonly hasPending: boolean;
|
|
110
109
|
/** Current sync status of this document. */
|
|
111
|
-
readonly syncStatus: DocSyncStatus
|
|
110
|
+
readonly syncStatus: Store<DocSyncStatus>;
|
|
112
111
|
/** Error from the last failed sync attempt, if any. */
|
|
113
|
-
readonly syncError
|
|
112
|
+
readonly syncError: Store<Error | undefined>;
|
|
114
113
|
/** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
|
|
115
|
-
readonly isLoaded: boolean
|
|
114
|
+
readonly isLoaded: Store<boolean>;
|
|
116
115
|
/**
|
|
117
116
|
* Subscribe to be notified when the user makes local changes.
|
|
118
117
|
* Emits the JSON Patch ops captured from the change() call.
|
|
119
118
|
* The algorithm handles packaging these into Changes.
|
|
120
119
|
*/
|
|
121
120
|
readonly onChange: Signal<(ops: JSONPatchOp[]) => void>;
|
|
122
|
-
/** Subscribe to be notified whenever state changes from any source. */
|
|
123
|
-
readonly onUpdate: Signal<(newState: T) => void>;
|
|
124
|
-
/** Subscribe to be notified when sync status changes. */
|
|
125
|
-
readonly onSyncStatus: Signal<(newStatus: DocSyncStatus) => void>;
|
|
126
|
-
/** Subscribe to be notified whenever the state changes (calls immediately with current state). */
|
|
127
|
-
subscribe(onUpdate: (newValue: T) => void): Unsubscriber;
|
|
128
121
|
/**
|
|
129
122
|
* Captures an update to the document, emitting JSON Patch ops via onChange.
|
|
130
123
|
* Does NOT apply locally - the algorithm handles state updates.
|
|
@@ -144,22 +137,20 @@ interface PatchesDoc<T extends object = object> extends SyncedDoc {
|
|
|
144
137
|
* Internal methods (updateSyncStatus, applyChanges, import) are on this class but not
|
|
145
138
|
* on the PatchesDoc interface, as they're only used by Algorithm and PatchesSync.
|
|
146
139
|
*/
|
|
147
|
-
declare abstract class BaseDoc<T extends object = object> implements PatchesDoc<T> {
|
|
140
|
+
declare abstract class BaseDoc<T extends object = object> extends ReadonlyStoreClass<T> implements PatchesDoc<T> {
|
|
148
141
|
protected _id: string;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
142
|
+
/** Current sync status of this document. */
|
|
143
|
+
readonly syncStatus: Store<DocSyncStatus>;
|
|
144
|
+
/** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
|
|
145
|
+
readonly isLoaded: Store<boolean>;
|
|
146
|
+
/** Error from the last failed sync attempt, if any. */
|
|
147
|
+
readonly syncError: Store<Error | undefined>;
|
|
153
148
|
/**
|
|
154
149
|
* Subscribe to be notified when the user makes local changes.
|
|
155
150
|
* Emits the JSON Patch ops captured from the change() call.
|
|
156
151
|
* The algorithm handles packaging these into Changes.
|
|
157
152
|
*/
|
|
158
|
-
readonly onChange: Signal<(ops: JSONPatchOp[]) => void>;
|
|
159
|
-
/** Subscribe to be notified whenever state changes from any source. */
|
|
160
|
-
readonly onUpdate: Signal<(newState: T) => void>;
|
|
161
|
-
/** Subscribe to be notified when sync status changes. */
|
|
162
|
-
readonly onSyncStatus: Signal<(newStatus: DocSyncStatus) => void>;
|
|
153
|
+
readonly onChange: easy_signal.Signal<(ops: JSONPatchOp[]) => void>;
|
|
163
154
|
/**
|
|
164
155
|
* Creates an instance of BaseDoc.
|
|
165
156
|
* @param id The unique identifier for this document.
|
|
@@ -168,20 +159,10 @@ declare abstract class BaseDoc<T extends object = object> implements PatchesDoc<
|
|
|
168
159
|
constructor(id: string, initialState?: T);
|
|
169
160
|
/** The unique identifier for this document. */
|
|
170
161
|
get id(): string;
|
|
171
|
-
/** Current local state (committed + pending merged). */
|
|
172
|
-
get state(): T;
|
|
173
|
-
/** Current sync status of this document. */
|
|
174
|
-
get syncStatus(): DocSyncStatus;
|
|
175
|
-
/** Error from the last failed sync attempt, if any. */
|
|
176
|
-
get syncError(): Error | undefined;
|
|
177
|
-
/** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
|
|
178
|
-
get isLoaded(): boolean;
|
|
179
162
|
/** Last committed revision number from the server. */
|
|
180
163
|
abstract get committedRev(): number;
|
|
181
164
|
/** Are there local changes that haven't been committed yet? */
|
|
182
165
|
abstract get hasPending(): boolean;
|
|
183
|
-
/** Subscribe to be notified whenever the state changes (calls immediately with current state). */
|
|
184
|
-
subscribe(onUpdate: (newValue: T) => void): Unsubscriber;
|
|
185
166
|
/**
|
|
186
167
|
* Captures an update to the document, emitting JSON Patch ops via onChange.
|
|
187
168
|
* Does NOT apply locally - the algorithm handles state updates via applyChanges.
|
|
@@ -2,7 +2,7 @@ import { CommitResult } from '../../../server/PatchesServer.js';
|
|
|
2
2
|
import { OTStoreBackend } from '../../../server/types.js';
|
|
3
3
|
import { ChangeInput, CommitChangesOptions } from '../../../types.js';
|
|
4
4
|
import '../../../net/protocol/JSONRPCServer.js';
|
|
5
|
-
import '
|
|
5
|
+
import 'easy-signal';
|
|
6
6
|
import '../../../net/websocket/AuthorizationProvider.js';
|
|
7
7
|
import '../../../json-patch/types.js';
|
|
8
8
|
import '../../../json-patch/JSONPatch.js';
|
package/dist/client/BaseDoc.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import '
|
|
1
|
+
import 'easy-signal';
|
|
2
2
|
import '../json-patch/types.js';
|
|
3
3
|
import '../types.js';
|
|
4
|
-
export { B as BaseDoc } from '../BaseDoc-
|
|
4
|
+
export { B as BaseDoc } from '../BaseDoc-BRIP2YZp.js';
|
|
5
5
|
import '../json-patch/JSONPatch.js';
|
|
6
6
|
import '@dabble/delta';
|
package/dist/client/BaseDoc.js
CHANGED
|
@@ -1,58 +1,34 @@
|
|
|
1
1
|
import "../chunk-IZ2YBCUP.js";
|
|
2
|
-
import { signal } from "
|
|
2
|
+
import { ReadonlyStoreClass, signal, store } from "easy-signal";
|
|
3
3
|
import { createJSONPatch } from "../json-patch/createJSONPatch.js";
|
|
4
4
|
import { isDocLoaded } from "../shared/utils.js";
|
|
5
|
-
class BaseDoc {
|
|
5
|
+
class BaseDoc extends ReadonlyStoreClass {
|
|
6
6
|
_id;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/** Current sync status of this document. */
|
|
8
|
+
syncStatus = store("unsynced");
|
|
9
|
+
/** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
|
|
10
|
+
isLoaded = store(false);
|
|
11
|
+
/** Error from the last failed sync attempt, if any. */
|
|
12
|
+
syncError = store(void 0);
|
|
11
13
|
/**
|
|
12
14
|
* Subscribe to be notified when the user makes local changes.
|
|
13
15
|
* Emits the JSON Patch ops captured from the change() call.
|
|
14
16
|
* The algorithm handles packaging these into Changes.
|
|
15
17
|
*/
|
|
16
18
|
onChange = signal();
|
|
17
|
-
/** Subscribe to be notified whenever state changes from any source. */
|
|
18
|
-
onUpdate = signal();
|
|
19
|
-
/** Subscribe to be notified when sync status changes. */
|
|
20
|
-
onSyncStatus = signal();
|
|
21
19
|
/**
|
|
22
20
|
* Creates an instance of BaseDoc.
|
|
23
21
|
* @param id The unique identifier for this document.
|
|
24
22
|
* @param initialState Optional initial state.
|
|
25
23
|
*/
|
|
26
24
|
constructor(id, initialState = {}) {
|
|
25
|
+
super(initialState);
|
|
27
26
|
this._id = id;
|
|
28
|
-
this._state = initialState;
|
|
29
27
|
}
|
|
30
28
|
/** The unique identifier for this document. */
|
|
31
29
|
get id() {
|
|
32
30
|
return this._id;
|
|
33
31
|
}
|
|
34
|
-
/** Current local state (committed + pending merged). */
|
|
35
|
-
get state() {
|
|
36
|
-
return this._state;
|
|
37
|
-
}
|
|
38
|
-
/** Current sync status of this document. */
|
|
39
|
-
get syncStatus() {
|
|
40
|
-
return this._syncStatus;
|
|
41
|
-
}
|
|
42
|
-
/** Error from the last failed sync attempt, if any. */
|
|
43
|
-
get syncError() {
|
|
44
|
-
return this._syncError;
|
|
45
|
-
}
|
|
46
|
-
/** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
|
|
47
|
-
get isLoaded() {
|
|
48
|
-
return this._isLoaded;
|
|
49
|
-
}
|
|
50
|
-
/** Subscribe to be notified whenever the state changes (calls immediately with current state). */
|
|
51
|
-
subscribe(onUpdate) {
|
|
52
|
-
const unsub = this.onUpdate(onUpdate);
|
|
53
|
-
onUpdate(this._state);
|
|
54
|
-
return unsub;
|
|
55
|
-
}
|
|
56
32
|
/**
|
|
57
33
|
* Captures an update to the document, emitting JSON Patch ops via onChange.
|
|
58
34
|
* Does NOT apply locally - the algorithm handles state updates via applyChanges.
|
|
@@ -73,15 +49,14 @@ class BaseDoc {
|
|
|
73
49
|
* @param error Optional error when status is 'error'.
|
|
74
50
|
*/
|
|
75
51
|
updateSyncStatus(status, error) {
|
|
76
|
-
this.
|
|
77
|
-
this.
|
|
52
|
+
this.syncError.state = status === "error" ? error : void 0;
|
|
53
|
+
this.syncStatus.state = status;
|
|
78
54
|
this._checkLoaded();
|
|
79
|
-
this.onSyncStatus.emit(status);
|
|
80
55
|
}
|
|
81
56
|
/** Latches _isLoaded to true when the doc has data or sync has resolved. */
|
|
82
57
|
_checkLoaded() {
|
|
83
|
-
if (!this.
|
|
84
|
-
this.
|
|
58
|
+
if (!this.isLoaded.state && isDocLoaded(this.committedRev, this.hasPending, this.syncStatus.state)) {
|
|
59
|
+
this.isLoaded.state = true;
|
|
85
60
|
}
|
|
86
61
|
}
|
|
87
62
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { JSONPatchOp } from '../json-patch/types.js';
|
|
2
2
|
import { PatchesSnapshot, Change } from '../types.js';
|
|
3
|
-
import { a as PatchesDoc } from '../BaseDoc-
|
|
3
|
+
import { a as PatchesDoc } from '../BaseDoc-BRIP2YZp.js';
|
|
4
4
|
import { PatchesStore, TrackedDoc } from './PatchesStore.js';
|
|
5
5
|
import '../json-patch/JSONPatch.js';
|
|
6
6
|
import '@dabble/delta';
|
|
7
|
-
import '
|
|
7
|
+
import 'easy-signal';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Algorithm interface for client-side sync algorithms (OT or LWW).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as easy_signal from 'easy-signal';
|
|
2
2
|
import { PatchesSnapshot, PatchesState } from '../types.js';
|
|
3
3
|
import { Deferred } from '../utils/deferred.js';
|
|
4
4
|
import { PatchesStore, TrackedDoc } from './PatchesStore.js';
|
|
@@ -29,7 +29,7 @@ declare class IndexedDBStore implements PatchesStore {
|
|
|
29
29
|
* Signal emitted during database upgrade, allowing algorithm-specific stores
|
|
30
30
|
* to create their object stores.
|
|
31
31
|
*/
|
|
32
|
-
readonly onUpgrade: Signal<(db: IDBDatabase, oldVersion: number, transaction: IDBTransaction) => void>;
|
|
32
|
+
readonly onUpgrade: easy_signal.Signal<(db: IDBDatabase, oldVersion: number, transaction: IDBTransaction) => void>;
|
|
33
33
|
constructor(dbName?: string);
|
|
34
34
|
/**
|
|
35
35
|
* Creates shared object stores used by all sync algorithms.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../chunk-IZ2YBCUP.js";
|
|
2
2
|
import { deferred } from "../utils/deferred.js";
|
|
3
|
-
import { signal } from "
|
|
3
|
+
import { signal } from "easy-signal";
|
|
4
4
|
class IndexedDBStore {
|
|
5
5
|
static DB_VERSION = 1;
|
|
6
6
|
db = null;
|
|
@@ -137,13 +137,8 @@ class IndexedDBStore {
|
|
|
137
137
|
async listDocs(includeDeleted = false, algorithm) {
|
|
138
138
|
const [tx, docsStore] = await this.transaction(["docs"], "readonly");
|
|
139
139
|
let docs;
|
|
140
|
-
if (algorithm
|
|
141
|
-
docs = await docsStore.getAllByIndex("algorithm",
|
|
142
|
-
} else if (algorithm === "ot") {
|
|
143
|
-
const otDocs = await docsStore.getAllByIndex("algorithm", "ot");
|
|
144
|
-
const allDocs = await docsStore.getAll();
|
|
145
|
-
const noAlgoDocs = allDocs.filter((doc) => !doc.algorithm);
|
|
146
|
-
docs = [...otDocs, ...noAlgoDocs];
|
|
140
|
+
if (algorithm) {
|
|
141
|
+
docs = await docsStore.getAllByIndex("algorithm", algorithm);
|
|
147
142
|
} else {
|
|
148
143
|
docs = await docsStore.getAll();
|
|
149
144
|
}
|
|
@@ -2,11 +2,11 @@ import { JSONPatchOp } from '../json-patch/types.js';
|
|
|
2
2
|
import { PatchesSnapshot, Change } from '../types.js';
|
|
3
3
|
import { ClientAlgorithm } from './ClientAlgorithm.js';
|
|
4
4
|
import { LWWClientStore } from './LWWClientStore.js';
|
|
5
|
-
import { a as PatchesDoc } from '../BaseDoc-
|
|
5
|
+
import { a as PatchesDoc } from '../BaseDoc-BRIP2YZp.js';
|
|
6
6
|
import { TrackedDoc } from './PatchesStore.js';
|
|
7
7
|
import '../json-patch/JSONPatch.js';
|
|
8
8
|
import '@dabble/delta';
|
|
9
|
-
import '
|
|
9
|
+
import 'easy-signal';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* LWW (Last-Write-Wins) algorithm implementation.
|
package/dist/client/LWWDoc.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { PatchesSnapshot, Change } from '../types.js';
|
|
2
|
-
import { B as BaseDoc } from '../BaseDoc-
|
|
2
|
+
import { B as BaseDoc } from '../BaseDoc-BRIP2YZp.js';
|
|
3
3
|
import '../json-patch/JSONPatch.js';
|
|
4
4
|
import '@dabble/delta';
|
|
5
5
|
import '../json-patch/types.js';
|
|
6
|
-
import '
|
|
6
|
+
import 'easy-signal';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* LWW (Last-Write-Wins) document implementation.
|
|
@@ -41,7 +41,7 @@ declare class LWWDoc<T extends object = object> extends BaseDoc<T> {
|
|
|
41
41
|
import(snapshot: PatchesSnapshot<T>): void;
|
|
42
42
|
/**
|
|
43
43
|
* Applies changes to the document state.
|
|
44
|
-
* Used for Worker
|
|
44
|
+
* Used for Worker-Tab communication where only changes are sent over the wire.
|
|
45
45
|
*
|
|
46
46
|
* For LWW, all ops are applied in order. The method distinguishes between
|
|
47
47
|
* committed and pending changes using `committedAt`:
|
package/dist/client/LWWDoc.js
CHANGED
|
@@ -15,11 +15,13 @@ class LWWDoc extends BaseDoc {
|
|
|
15
15
|
this._committedRev = snapshot?.rev ?? 0;
|
|
16
16
|
this._hasPending = (snapshot?.changes?.length ?? 0) > 0;
|
|
17
17
|
if (snapshot?.changes && snapshot.changes.length > 0) {
|
|
18
|
+
let currentState = this.state;
|
|
18
19
|
for (const change of snapshot.changes) {
|
|
19
20
|
for (const op of change.ops) {
|
|
20
|
-
|
|
21
|
+
currentState = applyPatch(currentState, [op], { partial: true });
|
|
21
22
|
}
|
|
22
23
|
}
|
|
24
|
+
this.state = currentState;
|
|
23
25
|
}
|
|
24
26
|
this._checkLoaded();
|
|
25
27
|
}
|
|
@@ -36,22 +38,22 @@ class LWWDoc extends BaseDoc {
|
|
|
36
38
|
* Resets state completely from the snapshot.
|
|
37
39
|
*/
|
|
38
40
|
import(snapshot) {
|
|
39
|
-
this._state = snapshot.state;
|
|
40
41
|
this._committedRev = snapshot.rev;
|
|
41
42
|
this._hasPending = (snapshot.changes?.length ?? 0) > 0;
|
|
43
|
+
let currentState = snapshot.state;
|
|
42
44
|
if (snapshot.changes && snapshot.changes.length > 0) {
|
|
43
45
|
for (const change of snapshot.changes) {
|
|
44
46
|
for (const op of change.ops) {
|
|
45
|
-
|
|
47
|
+
currentState = applyPatch(currentState, [op], { partial: true });
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
this._checkLoaded();
|
|
50
|
-
this.
|
|
52
|
+
this.state = currentState;
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
55
|
* Applies changes to the document state.
|
|
54
|
-
* Used for Worker
|
|
56
|
+
* Used for Worker-Tab communication where only changes are sent over the wire.
|
|
55
57
|
*
|
|
56
58
|
* For LWW, all ops are applied in order. The method distinguishes between
|
|
57
59
|
* committed and pending changes using `committedAt`:
|
|
@@ -64,9 +66,10 @@ class LWWDoc extends BaseDoc {
|
|
|
64
66
|
*/
|
|
65
67
|
applyChanges(changes, hasPending) {
|
|
66
68
|
if (changes.length === 0) return;
|
|
69
|
+
let currentState = this.state;
|
|
67
70
|
for (const change of changes) {
|
|
68
71
|
for (const op of change.ops) {
|
|
69
|
-
|
|
72
|
+
currentState = applyPatch(currentState, [op], { partial: true });
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
let lastCommittedRev = this._committedRev;
|
|
@@ -81,7 +84,7 @@ class LWWDoc extends BaseDoc {
|
|
|
81
84
|
this._committedRev = lastCommittedRev;
|
|
82
85
|
this._hasPending = hasPending ?? hasPendingChanges;
|
|
83
86
|
this._checkLoaded();
|
|
84
|
-
this.
|
|
87
|
+
this.state = currentState;
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
90
|
export {
|
|
@@ -5,7 +5,7 @@ import { LWWClientStore } from './LWWClientStore.js';
|
|
|
5
5
|
import { TrackedDoc } from './PatchesStore.js';
|
|
6
6
|
import '../json-patch/JSONPatch.js';
|
|
7
7
|
import '@dabble/delta';
|
|
8
|
-
import '
|
|
8
|
+
import 'easy-signal';
|
|
9
9
|
import '../utils/deferred.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -128,7 +128,7 @@ class LWWIndexedDBStore {
|
|
|
128
128
|
);
|
|
129
129
|
const { rev, state } = docState;
|
|
130
130
|
await Promise.all([
|
|
131
|
-
docsStore.put({ docId, committedRev: rev }),
|
|
131
|
+
docsStore.put({ docId, committedRev: rev, algorithm: "lww" }),
|
|
132
132
|
snapshots.put({ docId, state, rev }),
|
|
133
133
|
this.deleteFieldsForDoc(committedOps, docId),
|
|
134
134
|
this.deleteFieldsForDoc(pendingOps, docId),
|
|
@@ -141,7 +141,7 @@ class LWWIndexedDBStore {
|
|
|
141
141
|
["snapshots", "committedOps", "pendingOps", "sendingChanges", "docs"],
|
|
142
142
|
"readwrite"
|
|
143
143
|
);
|
|
144
|
-
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
|
|
144
|
+
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "lww" };
|
|
145
145
|
await docsStore.put({ ...docMeta, deleted: true });
|
|
146
146
|
await Promise.all([
|
|
147
147
|
snapshots.delete(docId),
|
|
@@ -243,7 +243,7 @@ class LWWIndexedDBStore {
|
|
|
243
243
|
return;
|
|
244
244
|
}
|
|
245
245
|
await Promise.all(sending.change.ops.map((op) => committedOps.put({ ...op, docId })));
|
|
246
|
-
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
|
|
246
|
+
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "lww" };
|
|
247
247
|
if (sending.change.rev > docMeta.committedRev) {
|
|
248
248
|
await docsStore.put({ ...docMeta, committedRev: sending.change.rev });
|
|
249
249
|
}
|
|
@@ -259,7 +259,7 @@ class LWWIndexedDBStore {
|
|
|
259
259
|
await Promise.all(allOps.map((op) => committedOps.put({ ...op, docId })));
|
|
260
260
|
const lastCommittedRev = serverChanges.at(-1)?.rev;
|
|
261
261
|
if (lastCommittedRev !== void 0) {
|
|
262
|
-
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
|
|
262
|
+
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "lww" };
|
|
263
263
|
if (lastCommittedRev > docMeta.committedRev) {
|
|
264
264
|
await docsStore.put({ ...docMeta, committedRev: lastCommittedRev });
|
|
265
265
|
}
|
|
@@ -2,11 +2,11 @@ import { JSONPatchOp } from '../json-patch/types.js';
|
|
|
2
2
|
import { PatchesSnapshot, Change } from '../types.js';
|
|
3
3
|
import { ClientAlgorithm } from './ClientAlgorithm.js';
|
|
4
4
|
import { OTClientStore } from './OTClientStore.js';
|
|
5
|
-
import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-
|
|
5
|
+
import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-BRIP2YZp.js';
|
|
6
6
|
import { TrackedDoc } from './PatchesStore.js';
|
|
7
7
|
import '../json-patch/JSONPatch.js';
|
|
8
8
|
import '@dabble/delta';
|
|
9
|
-
import '
|
|
9
|
+
import 'easy-signal';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* OT (Operational Transformation) algorithm implementation.
|
package/dist/client/OTDoc.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../types.js';
|
|
2
|
-
export { O as OTDoc } from '../BaseDoc-
|
|
2
|
+
export { O as OTDoc } from '../BaseDoc-BRIP2YZp.js';
|
|
3
3
|
import '../json-patch/JSONPatch.js';
|
|
4
4
|
import '@dabble/delta';
|
|
5
5
|
import '../json-patch/types.js';
|
|
6
|
-
import '
|
|
6
|
+
import 'easy-signal';
|
package/dist/client/OTDoc.js
CHANGED
|
@@ -17,11 +17,11 @@ class OTDoc extends BaseDoc {
|
|
|
17
17
|
constructor(id, snapshot) {
|
|
18
18
|
const initialState = snapshot?.state ?? {};
|
|
19
19
|
super(id, initialState);
|
|
20
|
-
this._committedState = this.
|
|
20
|
+
this._committedState = this.state;
|
|
21
21
|
this._committedRev = snapshot?.rev ?? 0;
|
|
22
22
|
this._pendingChanges = snapshot?.changes ?? [];
|
|
23
23
|
if (this._pendingChanges.length > 0) {
|
|
24
|
-
this.
|
|
24
|
+
this.state = applyChangesToState(this._committedState, this._pendingChanges);
|
|
25
25
|
}
|
|
26
26
|
this._checkLoaded();
|
|
27
27
|
}
|
|
@@ -48,9 +48,8 @@ class OTDoc extends BaseDoc {
|
|
|
48
48
|
this._committedState = snapshot.state;
|
|
49
49
|
this._committedRev = snapshot.rev;
|
|
50
50
|
this._pendingChanges = snapshot.changes;
|
|
51
|
-
this._state = createStateFromSnapshot(snapshot);
|
|
52
51
|
this._checkLoaded();
|
|
53
|
-
this.
|
|
52
|
+
this.state = createStateFromSnapshot(snapshot);
|
|
54
53
|
}
|
|
55
54
|
/**
|
|
56
55
|
* Unified entry point for applying changes.
|
|
@@ -76,13 +75,13 @@ class OTDoc extends BaseDoc {
|
|
|
76
75
|
this._committedState = applyChangesToState(this._committedState, serverChanges);
|
|
77
76
|
this._committedRev = serverChanges[serverChanges.length - 1].rev;
|
|
78
77
|
this._pendingChanges = rebasedPending;
|
|
79
|
-
this.
|
|
78
|
+
this._checkLoaded();
|
|
79
|
+
this.state = applyChangesToState(this._committedState, this._pendingChanges);
|
|
80
80
|
} else {
|
|
81
|
-
this._state = applyChangesToState(this._state, changes);
|
|
82
81
|
this._pendingChanges.push(...changes);
|
|
82
|
+
this._checkLoaded();
|
|
83
|
+
this.state = applyChangesToState(this.state, changes);
|
|
83
84
|
}
|
|
84
|
-
this._checkLoaded();
|
|
85
|
-
this.onUpdate.emit(this._state);
|
|
86
85
|
}
|
|
87
86
|
/**
|
|
88
87
|
* Returns the document snapshot for serialization.
|
|
@@ -101,7 +101,7 @@ class OTIndexedDBStore {
|
|
|
101
101
|
["snapshots", "committedChanges", "pendingChanges", "docs"],
|
|
102
102
|
"readwrite"
|
|
103
103
|
);
|
|
104
|
-
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
|
|
104
|
+
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "ot" };
|
|
105
105
|
await docsStore.put({ ...docMeta, deleted: true });
|
|
106
106
|
await Promise.all([
|
|
107
107
|
snapshots.delete(docId),
|
|
@@ -117,7 +117,7 @@ class OTIndexedDBStore {
|
|
|
117
117
|
);
|
|
118
118
|
const { rev, state } = docState;
|
|
119
119
|
await Promise.all([
|
|
120
|
-
docsStore.put({ docId, committedRev: rev }),
|
|
120
|
+
docsStore.put({ docId, committedRev: rev, algorithm: "ot" }),
|
|
121
121
|
snapshots.put({ docId, state, rev }),
|
|
122
122
|
committedChanges.delete([docId, 0], [docId, Infinity]),
|
|
123
123
|
pendingChanges.delete([docId, 0], [docId, Infinity])
|
|
@@ -169,7 +169,7 @@ class OTIndexedDBStore {
|
|
|
169
169
|
}
|
|
170
170
|
const lastCommittedRev = serverChanges.at(-1)?.rev;
|
|
171
171
|
if (lastCommittedRev !== void 0) {
|
|
172
|
-
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
|
|
172
|
+
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "ot" };
|
|
173
173
|
if (lastCommittedRev > docMeta.committedRev) {
|
|
174
174
|
await docsStore.put({ ...docMeta, committedRev: lastCommittedRev, deleted: void 0 });
|
|
175
175
|
}
|
package/dist/client/Patches.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as easy_signal from 'easy-signal';
|
|
2
|
+
import { Unsubscriber } from 'easy-signal';
|
|
2
3
|
import { JSONPatchOp } from '../json-patch/types.js';
|
|
3
4
|
import { Change } from '../types.js';
|
|
4
5
|
import { ClientAlgorithm } from './ClientAlgorithm.js';
|
|
5
|
-
import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-
|
|
6
|
+
import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-BRIP2YZp.js';
|
|
6
7
|
import { AlgorithmName } from './PatchesStore.js';
|
|
7
8
|
import '../json-patch/JSONPatch.js';
|
|
8
9
|
import '@dabble/delta';
|
|
@@ -49,15 +50,15 @@ declare class Patches {
|
|
|
49
50
|
readonly algorithms: Partial<Record<AlgorithmName, ClientAlgorithm>>;
|
|
50
51
|
readonly defaultAlgorithm: AlgorithmName;
|
|
51
52
|
readonly trackedDocs: Set<string>;
|
|
52
|
-
readonly onError: Signal<(error: Error, context?: {
|
|
53
|
+
readonly onError: easy_signal.Signal<(error: Error, context?: {
|
|
53
54
|
docId?: string;
|
|
54
55
|
}) => void>;
|
|
55
|
-
readonly onServerCommit: Signal<(docId: string, changes: Change[]) => void>;
|
|
56
|
-
readonly onTrackDocs: Signal<(docIds: string[], algorithmName: AlgorithmName) => void>;
|
|
57
|
-
readonly onUntrackDocs: Signal<(docIds: string[]) => void>;
|
|
58
|
-
readonly onDeleteDoc: Signal<(docId: string) => void>;
|
|
56
|
+
readonly onServerCommit: easy_signal.Signal<(docId: string, changes: Change[]) => void>;
|
|
57
|
+
readonly onTrackDocs: easy_signal.Signal<(docIds: string[], algorithmName: AlgorithmName) => void>;
|
|
58
|
+
readonly onUntrackDocs: easy_signal.Signal<(docIds: string[]) => void>;
|
|
59
|
+
readonly onDeleteDoc: easy_signal.Signal<(docId: string) => void>;
|
|
59
60
|
/** Emitted when a doc has pending changes ready to send */
|
|
60
|
-
readonly onChange: Signal<(docId: string) => void>;
|
|
61
|
+
readonly onChange: easy_signal.Signal<(docId: string) => void>;
|
|
61
62
|
constructor(opts: PatchesOptions);
|
|
62
63
|
/**
|
|
63
64
|
* Loads tracked docs from all registered algorithm stores.
|
package/dist/client/Patches.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
__runInitializers
|
|
7
7
|
} from "../chunk-IZ2YBCUP.js";
|
|
8
8
|
var _openDoc_dec, _init;
|
|
9
|
-
import { signal } from "
|
|
9
|
+
import { signal } from "easy-signal";
|
|
10
10
|
import { singleInvocation } from "../utils/concurrency.js";
|
|
11
11
|
_openDoc_dec = [singleInvocation(true)];
|
|
12
12
|
class Patches {
|
|
@@ -48,7 +48,10 @@ class Patches {
|
|
|
48
48
|
Promise.all(
|
|
49
49
|
entries.map(
|
|
50
50
|
([name, algorithm]) => algorithm.listDocs().then((docs) => {
|
|
51
|
-
this.trackDocs(
|
|
51
|
+
this.trackDocs(
|
|
52
|
+
docs.map(({ docId }) => docId),
|
|
53
|
+
name
|
|
54
|
+
);
|
|
52
55
|
})
|
|
53
56
|
)
|
|
54
57
|
).catch((err) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import '
|
|
1
|
+
import 'easy-signal';
|
|
2
2
|
import '../json-patch/types.js';
|
|
3
3
|
import '../types.js';
|
|
4
|
-
export { O as OTDoc, a as PatchesDoc, O as PatchesDocClass, P as PatchesDocOptions } from '../BaseDoc-
|
|
4
|
+
export { O as OTDoc, a as PatchesDoc, O as PatchesDocClass, P as PatchesDocOptions } from '../BaseDoc-BRIP2YZp.js';
|
|
5
5
|
import '../json-patch/JSONPatch.js';
|
|
6
6
|
import '@dabble/delta';
|