@dabble/patches 0.2.30 → 0.2.32

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.
@@ -1,4 +1,4 @@
1
- import type { Change, PatchesSnapshot } from '../types.js';
1
+ import type { Change, PatchesSnapshot, PatchesState } from '../types.js';
2
2
  import type { PatchesStore, TrackedDoc } from './PatchesStore.js';
3
3
  /**
4
4
  * A trivial in‑memory implementation of OfflineStore (soon PatchesStore).
@@ -11,6 +11,7 @@ export declare class InMemoryStore implements PatchesStore {
11
11
  getPendingChanges(docId: string): Promise<Change[]>;
12
12
  getLastRevs(docId: string): Promise<[number, number]>;
13
13
  listDocs(includeDeleted?: boolean): Promise<TrackedDoc[]>;
14
+ saveDoc(docId: string, snapshot: PatchesState): Promise<void>;
14
15
  savePendingChange(docId: string, change: Change): Promise<void>;
15
16
  saveCommittedChanges(docId: string, changes: Change[], sentPendingRange?: [number, number]): Promise<void>;
16
17
  trackDocs(docIds: string[]): Promise<void>;
@@ -52,6 +52,9 @@ export class InMemoryStore {
52
52
  }));
53
53
  }
54
54
  // ─── Writes ────────────────────────────────────────────────────────────
55
+ async saveDoc(docId, snapshot) {
56
+ this.docs.set(docId, { snapshot, committed: [], pending: [] });
57
+ }
55
58
  async savePendingChange(docId, change) {
56
59
  const buf = this.docs.get(docId) ?? { committed: [], pending: [] };
57
60
  if (!this.docs.has(docId))
@@ -1,4 +1,4 @@
1
- import type { Change, PatchesSnapshot } from '../types.js';
1
+ import type { Change, PatchesSnapshot, PatchesState } from '../types.js';
2
2
  import type { PatchesStore, TrackedDoc } from './PatchesStore.js';
3
3
  /**
4
4
  * Creates a new IndexedDB database with stores:
@@ -48,6 +48,7 @@ export declare class IndexedDBStore implements PatchesStore {
48
48
  */
49
49
  deleteDoc(docId: string): Promise<void>;
50
50
  confirmDeleteDoc(docId: string): Promise<void>;
51
+ saveDoc(docId: string, docState: PatchesState): Promise<void>;
51
52
  /**
52
53
  * Append an array of local changes to the pending queue.
53
54
  * Called *before* you attempt to send them to the server.
@@ -162,6 +162,17 @@ export class IndexedDBStore {
162
162
  await tx.complete();
163
163
  }
164
164
  // ─── Pending Changes ────────────────────────────────────────────────────────
165
+ async saveDoc(docId, docState) {
166
+ const [tx, snapshots, committedChanges, pendingChanges, docsStore] = await this.transaction(['snapshots', 'committedChanges', 'pendingChanges', 'docs'], 'readwrite');
167
+ const { rev, state } = docState;
168
+ await Promise.all([
169
+ docsStore.put({ docId, committedRev: rev }),
170
+ snapshots.put({ docId, state, rev }),
171
+ committedChanges.delete([docId, 0], [docId, Infinity]),
172
+ pendingChanges.delete([docId, 0], [docId, Infinity]),
173
+ ]);
174
+ await tx.complete();
175
+ }
165
176
  /**
166
177
  * Append an array of local changes to the pending queue.
167
178
  * Called *before* you attempt to send them to the server.
@@ -1,4 +1,4 @@
1
- import type { Change, PatchesSnapshot } from '../types.js';
1
+ import type { Change, PatchesSnapshot, PatchesState } from '../types.js';
2
2
  /** Represents metadata for a document tracked by the store. */
3
3
  export interface TrackedDoc {
4
4
  docId: string;
@@ -27,6 +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
+ saveDoc(docId: string, docState: PatchesState): Promise<void>;
30
31
  savePendingChange(docId: string, change: Change): Promise<void>;
31
32
  saveCommittedChanges(docId: string, changes: Change[], sentPendingRange?: [number, number]): Promise<void>;
32
33
  /** Permanently delete document (writes tombstone so server delete happens later). */
@@ -21,24 +21,16 @@ export function signal() {
21
21
  const subscribers = new Set();
22
22
  const errorListeners = new Set();
23
23
  function signal(subscriber) {
24
- subscribers.add(new WeakRef(subscriber));
25
- return () => subscribers.delete(new WeakRef(subscriber));
24
+ subscribers.add(subscriber);
25
+ return () => subscribers.delete(subscriber);
26
26
  }
27
27
  signal.emit = async (...args) => {
28
28
  const listeners = args[0] instanceof Error ? errorListeners : subscribers;
29
- await Promise.all(Array.from(listeners).map(ref => {
30
- const listener = ref.deref();
31
- if (listener) {
32
- return listener(...args);
33
- }
34
- else {
35
- listeners.delete(ref);
36
- }
37
- }));
29
+ await Promise.all(Array.from(listeners).map(listener => listener(...args)));
38
30
  };
39
31
  signal.error = (errorListener) => {
40
- errorListeners.add(new WeakRef(errorListener));
41
- return () => errorListeners.delete(new WeakRef(errorListener));
32
+ errorListeners.add(errorListener);
33
+ return () => errorListeners.delete(errorListener);
42
34
  };
43
35
  signal.clear = () => {
44
36
  subscribers.clear();
@@ -200,10 +200,16 @@ export class PatchesSync {
200
200
  else {
201
201
  // No pending, just check for server changes
202
202
  const [committedRev] = await this.store.getLastRevs(docId);
203
- const serverChanges = await this.ws.getChangesSince(docId, committedRev);
204
- if (serverChanges.length > 0) {
205
- await this.store.saveCommittedChanges(docId, serverChanges);
206
- this.patches.applyServerChanges(docId, serverChanges);
203
+ if (committedRev) {
204
+ const serverChanges = await this.ws.getChangesSince(docId, committedRev);
205
+ if (serverChanges.length > 0) {
206
+ await this.store.saveCommittedChanges(docId, serverChanges);
207
+ this.patches.applyServerChanges(docId, serverChanges);
208
+ }
209
+ }
210
+ else {
211
+ const snapshot = await this.ws.getDoc(docId);
212
+ await this.store.saveDoc(docId, snapshot);
207
213
  }
208
214
  }
209
215
  }
@@ -3,8 +3,6 @@ export * from './protocol/JSONRPCClient.js';
3
3
  export * from './protocol/JSONRPCServer.js';
4
4
  export type * from './protocol/types.js';
5
5
  export type * from './types.js';
6
- export * from './webrtc/WebRTCAwareness.js';
7
- export * from './webrtc/WebRTCTransport.js';
8
6
  export * from './websocket/AuthorizationProvider.js';
9
7
  export * from './websocket/onlineState.js';
10
8
  export * from './websocket/PatchesWebSocket.js';
package/dist/net/index.js CHANGED
@@ -1,8 +1,6 @@
1
1
  export * from './PatchesSync.js';
2
2
  export * from './protocol/JSONRPCClient.js';
3
3
  export * from './protocol/JSONRPCServer.js';
4
- export * from './webrtc/WebRTCAwareness.js';
5
- export * from './webrtc/WebRTCTransport.js';
6
4
  export * from './websocket/AuthorizationProvider.js';
7
5
  export * from './websocket/onlineState.js';
8
6
  export * from './websocket/PatchesWebSocket.js';
@@ -1,5 +1,5 @@
1
1
  import type { Unsubscriber } from '../../event-signal.js';
2
- import type { Change, EditableVersionMetadata, ListVersionsOptions, PatchesSnapshot, PatchesState, VersionMetadata } from '../../types';
2
+ import type { Change, EditableVersionMetadata, ListVersionsOptions, PatchesState, VersionMetadata } from '../../types';
3
3
  /**
4
4
  * Represents the possible states of a network transport connection.
5
5
  * - 'connecting': Connection is being established
@@ -115,7 +115,7 @@ export interface PatchesAPI {
115
115
  */
116
116
  unsubscribe(ids: string | string[]): Promise<void>;
117
117
  /** Get the latest version of a document and changes since the last version. */
118
- getDoc(docId: string, atRev?: number): Promise<PatchesSnapshot>;
118
+ getDoc(docId: string, atRev?: number): Promise<PatchesState>;
119
119
  /** Get changes that occurred after a specific revision. */
120
120
  getChangesSince(docId: string, rev: number): Promise<Change[]>;
121
121
  /** Apply a set of changes from the client to a document. Returns the committed changes. */
@@ -0,0 +1,2 @@
1
+ export * from './WebRTCAwareness.js';
2
+ export * from './WebRTCTransport.js';
@@ -0,0 +1,2 @@
1
+ export * from './WebRTCAwareness.js';
2
+ export * from './WebRTCTransport.js';
@@ -1,5 +1,5 @@
1
1
  import { type Signal } from '../../event-signal.js';
2
- import type { Change, EditableVersionMetadata, ListVersionsOptions, PatchesSnapshot, VersionMetadata } from '../../types.js';
2
+ import type { Change, EditableVersionMetadata, ListVersionsOptions, PatchesSnapshot, PatchesState, VersionMetadata } from '../../types.js';
3
3
  import type { ConnectionState, PatchesAPI, PatchesNotificationParams } from '../protocol/types.js';
4
4
  import { type WebSocketOptions } from './WebSocketTransport.js';
5
5
  /**
@@ -47,7 +47,7 @@ export declare class PatchesWebSocket implements PatchesAPI {
47
47
  * @param docId - The ID of the document.
48
48
  * @returns A promise resolving with the document snapshot.
49
49
  */
50
- getDoc(docId: string): Promise<PatchesSnapshot>;
50
+ getDoc(docId: string, atRev?: number): Promise<PatchesState>;
51
51
  /**
52
52
  * Gets changes that occurred for a document after a specific revision number.
53
53
  * @param docId - The ID of the document.
@@ -65,8 +65,8 @@ export class PatchesWebSocket {
65
65
  * @param docId - The ID of the document.
66
66
  * @returns A promise resolving with the document snapshot.
67
67
  */
68
- async getDoc(docId) {
69
- return this.rpc.request('getDoc', { docId });
68
+ async getDoc(docId, atRev) {
69
+ return this.rpc.request('getDoc', { docId, atRev });
70
70
  }
71
71
  /**
72
72
  * Gets changes that occurred for a document after a specific revision number.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dabble/patches",
3
- "version": "0.2.30",
3
+ "version": "0.2.32",
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": {
@@ -25,6 +25,10 @@
25
25
  "./net": {
26
26
  "import": "./dist/net/index.js",
27
27
  "types": "./dist/net/index.d.ts"
28
+ },
29
+ "./webrtc": {
30
+ "import": "./dist/net/webrtc/index.js",
31
+ "types": "./dist/net/webrtc/index.d.ts"
28
32
  }
29
33
  },
30
34
  "files": [
@@ -56,16 +60,9 @@
56
60
  "dependencies": {
57
61
  "@dabble/delta": "^1.2.4",
58
62
  "alphacounter": "^2.x",
59
- "crypto-id": "^0.2.3"
60
- },
61
- "peerDependencies": {
63
+ "crypto-id": "^0.2.3",
62
64
  "simple-peer": "^9.11.1"
63
65
  },
64
- "peerDependenciesMeta": {
65
- "simple-peer": {
66
- "optional": true
67
- }
68
- },
69
66
  "devDependencies": {
70
67
  "@sveltejs/package": "^2.3.11",
71
68
  "@types/simple-peer": "^9.11.8",