@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.
@@ -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
- savePendingChanges(docId: string, changes: Change[]): Promise<void>;
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 savePendingChanges(docId, changes) {
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(...changes);
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
- savePendingChanges(docId: string, changes: Change[]): Promise<void>;
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 savePendingChanges(docId, changes) {
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 Promise.all(changes.map(change => pendingChanges.put({ ...change, docId })));
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). */
@@ -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.savePendingChanges(docId, changes);
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(name: string): Promise<string>;
26
+ createVersion(metadata: EditableVersionMetadata): Promise<string>;
27
27
  /** Update the name of a specific version. */
28
- updateVersion(versionId: string, updates: Pick<VersionMetadata, 'name'>): Promise<void>;
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(name) {
66
- const versionId = await this.api.createVersion(this.id, name);
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, updates) {
72
- await this.api.updateVersion(this.id, versionId, updates);
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
- savePendingChanges(docId: string, changes: Change[]): Promise<void>;
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 _connectionId - The ID of the connection making the request
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(_connectionId: string, params: {
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 _connectionId - The ID of the connection making the request
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(_connectionId, params) {
158
+ async deleteDoc(connectionId, params) {
159
159
  const { docId } = params;
160
- await this.assertWrite(_connectionId, docId, 'deleteDoc', params);
161
- return this.patches.deleteDoc(docId);
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.20",
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": {