@dabble/patches 0.2.20 → 0.2.21
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/InMemoryStore.d.ts +1 -3
- package/dist/client/InMemoryStore.js +2 -6
- package/dist/client/IndexedDBStore.d.ts +1 -3
- package/dist/client/IndexedDBStore.js +2 -6
- package/dist/client/Patches.js +2 -5
- package/dist/client/PatchesHistoryClient.d.ts +3 -3
- package/dist/client/PatchesHistoryClient.js +4 -4
- package/dist/client/PatchesStore.d.ts +1 -1
- package/dist/net/websocket/WebSocketServer.d.ts +2 -2
- package/dist/net/websocket/WebSocketServer.js +9 -4
- package/package.json +1 -1
|
@@ -7,13 +7,11 @@ import type { PatchesStore, TrackedDoc } from './PatchesStore.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class InMemoryStore implements PatchesStore {
|
|
9
9
|
private docs;
|
|
10
|
-
/** Signal emitted when pending changes are added (mirrors IndexedDBStore API) */
|
|
11
|
-
readonly onPendingChanges: import("../event-signal.js").Signal<(docId: string, changes: Change[]) => void>;
|
|
12
10
|
getDoc(docId: string): Promise<PatchesSnapshot | undefined>;
|
|
13
11
|
getPendingChanges(docId: string): Promise<Change[]>;
|
|
14
12
|
getLastRevs(docId: string): Promise<[number, number]>;
|
|
15
13
|
listDocs(includeDeleted?: boolean): Promise<TrackedDoc[]>;
|
|
16
|
-
|
|
14
|
+
savePendingChange(docId: string, change: Change): Promise<void>;
|
|
17
15
|
saveCommittedChanges(docId: string, changes: Change[], sentPendingRange?: [number, number]): Promise<void>;
|
|
18
16
|
trackDocs(docIds: string[]): Promise<void>;
|
|
19
17
|
untrackDocs(docIds: string[]): Promise<void>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { signal } from '../event-signal.js';
|
|
2
1
|
import { transformPatch } from '../json-patch/transformPatch.js';
|
|
3
2
|
import { applyChanges } from '../utils.js';
|
|
4
3
|
/**
|
|
@@ -9,8 +8,6 @@ import { applyChanges } from '../utils.js';
|
|
|
9
8
|
export class InMemoryStore {
|
|
10
9
|
constructor() {
|
|
11
10
|
this.docs = new Map();
|
|
12
|
-
/** Signal emitted when pending changes are added (mirrors IndexedDBStore API) */
|
|
13
|
-
this.onPendingChanges = signal();
|
|
14
11
|
}
|
|
15
12
|
// ─── Reconstruction ────────────────────────────────────────────────────
|
|
16
13
|
async getDoc(docId) {
|
|
@@ -55,12 +52,11 @@ export class InMemoryStore {
|
|
|
55
52
|
}));
|
|
56
53
|
}
|
|
57
54
|
// ─── Writes ────────────────────────────────────────────────────────────
|
|
58
|
-
async
|
|
55
|
+
async savePendingChange(docId, change) {
|
|
59
56
|
const buf = this.docs.get(docId) ?? { committed: [], pending: [] };
|
|
60
57
|
if (!this.docs.has(docId))
|
|
61
58
|
this.docs.set(docId, buf);
|
|
62
|
-
buf.pending.push(
|
|
63
|
-
this.onPendingChanges.emit(docId, changes);
|
|
59
|
+
buf.pending.push(change);
|
|
64
60
|
}
|
|
65
61
|
async saveCommittedChanges(docId, changes, sentPendingRange) {
|
|
66
62
|
const buf = this.docs.get(docId) ?? { committed: [], pending: [] };
|
|
@@ -16,8 +16,6 @@ export declare class IndexedDBStore implements PatchesStore {
|
|
|
16
16
|
private db;
|
|
17
17
|
private dbName?;
|
|
18
18
|
private dbPromise;
|
|
19
|
-
/** Subscribe to be notified after local state changes are saved to the database. */
|
|
20
|
-
readonly onPendingChanges: import("../event-signal.js").Signal<(docId: string, changes: Change[]) => void>;
|
|
21
19
|
constructor(dbName?: string);
|
|
22
20
|
private initDB;
|
|
23
21
|
private getDB;
|
|
@@ -54,7 +52,7 @@ export declare class IndexedDBStore implements PatchesStore {
|
|
|
54
52
|
* Append an array of local changes to the pending queue.
|
|
55
53
|
* Called *before* you attempt to send them to the server.
|
|
56
54
|
*/
|
|
57
|
-
|
|
55
|
+
savePendingChange(docId: string, change: Change): Promise<void>;
|
|
58
56
|
/** Read back all pending changes for this docId (in order). */
|
|
59
57
|
getPendingChanges(docId: string): Promise<Change[]>;
|
|
60
58
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { signal } from '../event-signal.js';
|
|
2
1
|
import { transformPatch } from '../json-patch/transformPatch.js';
|
|
3
2
|
import { applyChanges, deferred } from '../utils.js';
|
|
4
3
|
const DB_VERSION = 1;
|
|
@@ -18,8 +17,6 @@ const SNAPSHOT_INTERVAL = 200;
|
|
|
18
17
|
export class IndexedDBStore {
|
|
19
18
|
constructor(dbName) {
|
|
20
19
|
this.db = null;
|
|
21
|
-
/** Subscribe to be notified after local state changes are saved to the database. */
|
|
22
|
-
this.onPendingChanges = signal();
|
|
23
20
|
this.dbName = dbName;
|
|
24
21
|
this.dbPromise = deferred();
|
|
25
22
|
if (this.dbName) {
|
|
@@ -169,7 +166,7 @@ export class IndexedDBStore {
|
|
|
169
166
|
* Append an array of local changes to the pending queue.
|
|
170
167
|
* Called *before* you attempt to send them to the server.
|
|
171
168
|
*/
|
|
172
|
-
async
|
|
169
|
+
async savePendingChange(docId, change) {
|
|
173
170
|
const [tx, pendingChanges, docsStore] = await this.transaction(['pendingChanges', 'docs'], 'readwrite');
|
|
174
171
|
let docMeta = await docsStore.get(docId);
|
|
175
172
|
if (!docMeta) {
|
|
@@ -181,8 +178,7 @@ export class IndexedDBStore {
|
|
|
181
178
|
await docsStore.put(docMeta);
|
|
182
179
|
console.warn(`Revived document ${docId} by saving pending changes.`);
|
|
183
180
|
}
|
|
184
|
-
await
|
|
185
|
-
this.onPendingChanges.emit(docId, changes);
|
|
181
|
+
await pendingChanges.put({ ...change, docId });
|
|
186
182
|
await tx.complete();
|
|
187
183
|
}
|
|
188
184
|
/** Read back all pending changes for this docId (in order). */
|
package/dist/client/Patches.js
CHANGED
|
@@ -207,12 +207,9 @@ export class Patches {
|
|
|
207
207
|
* @returns An Unsubscriber function to remove the listener.
|
|
208
208
|
*/
|
|
209
209
|
_setupLocalDocListener(docId, doc) {
|
|
210
|
-
return doc.onChange(async () => {
|
|
211
|
-
const changes = doc.getUpdatesForServer();
|
|
212
|
-
if (!changes.length)
|
|
213
|
-
return;
|
|
210
|
+
return doc.onChange(async (change) => {
|
|
214
211
|
try {
|
|
215
|
-
await this.store.
|
|
212
|
+
await this.store.savePendingChange(docId, change);
|
|
216
213
|
// Note: When used with PatchesSync, it will handle flushing the changes
|
|
217
214
|
}
|
|
218
215
|
catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PatchesAPI } from '../net/protocol/types.js';
|
|
2
|
-
import type { Change, ListVersionsOptions, VersionMetadata } from '../types.js';
|
|
2
|
+
import type { Change, EditableVersionMetadata, ListVersionsOptions, VersionMetadata } from '../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Client-side history/scrubbing interface for a document.
|
|
5
5
|
* Read-only: allows listing versions, loading states/changes, and scrubbing.
|
|
@@ -23,9 +23,9 @@ export declare class PatchesHistoryClient<T = any> {
|
|
|
23
23
|
/** List version metadata for this document (with options) */
|
|
24
24
|
listVersions(options?: ListVersionsOptions): Promise<VersionMetadata[]>;
|
|
25
25
|
/** Create a new named version snapshot of the document's current state. */
|
|
26
|
-
createVersion(
|
|
26
|
+
createVersion(metadata: EditableVersionMetadata): Promise<string>;
|
|
27
27
|
/** Update the name of a specific version. */
|
|
28
|
-
updateVersion(versionId: string,
|
|
28
|
+
updateVersion(versionId: string, metadata: EditableVersionMetadata): Promise<void>;
|
|
29
29
|
/** Load the state for a specific version */
|
|
30
30
|
getStateAtVersion(versionId: string): Promise<any>;
|
|
31
31
|
/** Load the changes for a specific version */
|
|
@@ -62,14 +62,14 @@ export class PatchesHistoryClient {
|
|
|
62
62
|
return this._versions;
|
|
63
63
|
}
|
|
64
64
|
/** Create a new named version snapshot of the document's current state. */
|
|
65
|
-
async createVersion(
|
|
66
|
-
const versionId = await this.api.createVersion(this.id,
|
|
65
|
+
async createVersion(metadata) {
|
|
66
|
+
const versionId = await this.api.createVersion(this.id, metadata);
|
|
67
67
|
await this.listVersions(); // Refresh the list of versions
|
|
68
68
|
return versionId;
|
|
69
69
|
}
|
|
70
70
|
/** Update the name of a specific version. */
|
|
71
|
-
async updateVersion(versionId,
|
|
72
|
-
await this.api.updateVersion(this.id, versionId,
|
|
71
|
+
async updateVersion(versionId, metadata) {
|
|
72
|
+
await this.api.updateVersion(this.id, versionId, metadata);
|
|
73
73
|
await this.listVersions(); // Refresh the list of versions
|
|
74
74
|
}
|
|
75
75
|
/** Load the state for a specific version */
|
|
@@ -27,7 +27,7 @@ export interface PatchesStore {
|
|
|
27
27
|
getDoc(docId: string): Promise<PatchesSnapshot | undefined>;
|
|
28
28
|
getPendingChanges(docId: string): Promise<Change[]>;
|
|
29
29
|
getLastRevs(docId: string): Promise<[committedRev: number, pendingRev: number]>;
|
|
30
|
-
|
|
30
|
+
savePendingChange(docId: string, change: Change): Promise<void>;
|
|
31
31
|
saveCommittedChanges(docId: string, changes: Change[], sentPendingRange?: [number, number]): Promise<void>;
|
|
32
32
|
/** Permanently delete document (writes tombstone so server delete happens later). */
|
|
33
33
|
deleteDoc(docId: string): Promise<void>;
|
|
@@ -90,11 +90,11 @@ export declare class WebSocketServer {
|
|
|
90
90
|
}): Promise<Change[]>;
|
|
91
91
|
/**
|
|
92
92
|
* Deletes a document on the server.
|
|
93
|
-
* @param
|
|
93
|
+
* @param connectionId - The ID of the connection making the request
|
|
94
94
|
* @param params - The deletion parameters
|
|
95
95
|
* @param params.docId - The ID of the document to delete
|
|
96
96
|
*/
|
|
97
|
-
deleteDoc(
|
|
97
|
+
deleteDoc(connectionId: string, params: {
|
|
98
98
|
docId: string;
|
|
99
99
|
}): Promise<void>;
|
|
100
100
|
listVersions(connectionId: string, params: {
|
|
@@ -151,14 +151,19 @@ export class WebSocketServer {
|
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
153
|
* Deletes a document on the server.
|
|
154
|
-
* @param
|
|
154
|
+
* @param connectionId - The ID of the connection making the request
|
|
155
155
|
* @param params - The deletion parameters
|
|
156
156
|
* @param params.docId - The ID of the document to delete
|
|
157
157
|
*/
|
|
158
|
-
async deleteDoc(
|
|
158
|
+
async deleteDoc(connectionId, params) {
|
|
159
159
|
const { docId } = params;
|
|
160
|
-
await this.assertWrite(
|
|
161
|
-
|
|
160
|
+
await this.assertWrite(connectionId, docId, 'deleteDoc', params);
|
|
161
|
+
await this.patches.deleteDoc(docId);
|
|
162
|
+
// Notify other clients that the document has been deleted
|
|
163
|
+
const connectionIds = this.transport.getConnectionIds().filter(id => id !== connectionId);
|
|
164
|
+
if (connectionIds.length > 0) {
|
|
165
|
+
this.rpc.notify(connectionIds, 'docDeleted', { docId });
|
|
166
|
+
}
|
|
162
167
|
}
|
|
163
168
|
// ---------------------------------------------------------------------------
|
|
164
169
|
// History Manager wrappers
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dabble/patches",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.21",
|
|
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": {
|