@dabble/patches 0.3.1 → 0.3.2

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,3 +1,3 @@
1
1
  import type { JSONPatch } from '../..';
2
- import type { Change, PatchesSnapshot } from '../../types.js';
3
- export declare function makeChange<T = any>(snapshot: PatchesSnapshot<T>, mutator: (draft: T, patch: JSONPatch) => void, changeMetadata?: Record<string, any>, maxPayloadBytes?: number): Change[];
2
+ import type { Change, DeepRequired, PatchesSnapshot } from '../../types.js';
3
+ export declare function makeChange<T = any>(snapshot: PatchesSnapshot<T>, mutator: (draft: DeepRequired<T>, patch: JSONPatch) => void, changeMetadata?: Record<string, any>, maxPayloadBytes?: number): Change[];
@@ -1,6 +1,6 @@
1
1
  import { type Unsubscriber } from '../event-signal.js';
2
2
  import type { JSONPatch } from '../json-patch/JSONPatch.js';
3
- import type { Change, PatchesSnapshot, SyncingState } from '../types.js';
3
+ import type { Change, DeepRequired, PatchesSnapshot, SyncingState } from '../types.js';
4
4
  /**
5
5
  * Options for creating a PatchesDoc instance
6
6
  */
@@ -71,7 +71,7 @@ export declare class PatchesDoc<T extends object = object> {
71
71
  * @param mutator Function modifying a draft state.
72
72
  * @returns The generated Change object or null if no changes occurred.
73
73
  */
74
- change(mutator: (draft: T, patch: JSONPatch) => void): Change[];
74
+ change(mutator: (draft: DeepRequired<T>, patch: JSONPatch) => void): Change[];
75
75
  /**
76
76
  * Returns the pending changes for this document.
77
77
  * @returns The pending changes.
@@ -1,3 +1,4 @@
1
+ import type { DeepRequired } from '../types.js';
1
2
  import { JSONPatch } from './JSONPatch.js';
2
3
  /**
3
4
  * Creates a `JSONPatch` instance by tracking changes made to a proxy object within an updater function.
@@ -32,4 +33,4 @@ import { JSONPatch } from './JSONPatch.js';
32
33
  * // ]
33
34
  * ```
34
35
  */
35
- export declare function createJSONPatch<T>(target: T, updater: (proxy: T, patch: JSONPatch) => void): JSONPatch;
36
+ export declare function createJSONPatch<T>(target: T, updater: (proxy: DeepRequired<T>, patch: JSONPatch) => void): JSONPatch;
@@ -1,3 +1,4 @@
1
+ import type { DeepRequired } from '../types.js';
1
2
  import { JSONPatch } from './JSONPatch.js';
2
3
  /**
3
4
  * Creates a proxy object that can be used in two ways:
@@ -37,5 +38,5 @@ import { JSONPatch } from './JSONPatch.js';
37
38
  * @param patch The `JSONPatch` instance to add generated operations to (required for automatic patch generation mode).
38
39
  * @returns A proxy object of type T.
39
40
  */
40
- export declare function createPatchProxy<T>(): T;
41
- export declare function createPatchProxy<T>(target: T, patch: JSONPatch): T;
41
+ export declare function createPatchProxy<T>(): DeepRequired<T>;
42
+ export declare function createPatchProxy<T>(target: T, patch: JSONPatch): DeepRequired<T>;
@@ -1,23 +1,30 @@
1
1
  import { Patches } from '../client/Patches.js';
2
+ import type { PatchesStore } from '../client/PatchesStore.js';
2
3
  import type { Change, SyncingState } from '../types.js';
3
4
  import type { ConnectionState } from './protocol/types.js';
5
+ import { PatchesWebSocket } from './websocket/PatchesWebSocket.js';
4
6
  import type { WebSocketOptions } from './websocket/WebSocketTransport.js';
5
7
  export interface PatchesSyncState {
6
8
  online: boolean;
7
9
  connected: boolean;
8
10
  syncing: SyncingState;
9
11
  }
12
+ export interface PatchesSyncOptions {
13
+ subscribeFilter?: (docIds: string[]) => string[];
14
+ websocket?: WebSocketOptions;
15
+ }
10
16
  /**
11
17
  * Handles WebSocket connection, document subscriptions, and syncing logic between
12
18
  * the Patches instance and the server.
13
19
  */
14
20
  export declare class PatchesSync {
15
- private ws;
16
- private patches;
17
- private store;
18
- private maxPayloadBytes?;
19
- private trackedDocs;
20
- private _state;
21
+ protected options?: PatchesSyncOptions | undefined;
22
+ protected ws: PatchesWebSocket;
23
+ protected patches: Patches;
24
+ protected store: PatchesStore;
25
+ protected maxPayloadBytes?: number;
26
+ protected trackedDocs: Set<string>;
27
+ protected _state: PatchesSyncState;
21
28
  /**
22
29
  * Signal emitted when the sync state changes.
23
30
  */
@@ -28,7 +35,7 @@ export declare class PatchesSync {
28
35
  readonly onError: import("../event-signal.js").Signal<(error: Error, context?: {
29
36
  docId?: string;
30
37
  }) => void>;
31
- constructor(patches: Patches, url: string, wsOptions?: WebSocketOptions);
38
+ constructor(patches: Patches, url: string, options?: PatchesSyncOptions | undefined);
32
39
  /**
33
40
  * Gets the current sync state.
34
41
  */
@@ -50,8 +50,8 @@ let PatchesSync = (() => {
50
50
  let _syncDoc_decorators;
51
51
  let __receiveCommittedChanges_decorators;
52
52
  return _a = class PatchesSync {
53
- constructor(patches, url, wsOptions) {
54
- this.ws = __runInitializers(this, _instanceExtraInitializers);
53
+ constructor(patches, url, options) {
54
+ this.options = (__runInitializers(this, _instanceExtraInitializers), options);
55
55
  this._state = { online: false, connected: false, syncing: null };
56
56
  /**
57
57
  * Signal emitted when the sync state changes.
@@ -64,7 +64,7 @@ let PatchesSync = (() => {
64
64
  this.patches = patches;
65
65
  this.store = patches.store;
66
66
  this.maxPayloadBytes = patches.docOptions?.maxPayloadBytes;
67
- this.ws = new PatchesWebSocket(url, wsOptions);
67
+ this.ws = new PatchesWebSocket(url, options?.websocket);
68
68
  this._state.online = onlineState.isOnline;
69
69
  this.trackedDocs = new Set(patches.trackedDocs);
70
70
  // --- Event Listeners ---
@@ -131,7 +131,10 @@ let PatchesSync = (() => {
131
131
  // Subscribe to active docs
132
132
  if (activeDocIds.length > 0) {
133
133
  try {
134
- await this.ws.subscribe(activeDocIds);
134
+ const subscribeIds = this.options?.subscribeFilter?.(activeDocIds) || activeDocIds;
135
+ if (subscribeIds.length) {
136
+ await this.ws.subscribe(subscribeIds);
137
+ }
135
138
  }
136
139
  catch (err) {
137
140
  console.warn('Error subscribing to active docs during sync:', err);
@@ -338,9 +341,17 @@ let PatchesSync = (() => {
338
341
  if (!newIds.length)
339
342
  return;
340
343
  newIds.forEach(id => this.trackedDocs.add(id));
344
+ let subscribeIds = newIds;
345
+ // If a subscribe filter is provided, filter out docs that are already tracked
346
+ if (this.options?.subscribeFilter) {
347
+ const alreadyTracked = this.options.subscribeFilter([...this.trackedDocs]);
348
+ subscribeIds = subscribeIds.filter(id => !alreadyTracked.includes(id));
349
+ }
341
350
  if (this.state.connected) {
342
351
  try {
343
- await this.ws.subscribe(newIds);
352
+ if (subscribeIds.length) {
353
+ await this.ws.subscribe(subscribeIds);
354
+ }
344
355
  // Trigger sync for newly tracked docs immediately
345
356
  await Promise.all(newIds.map(id => this.syncDoc(id)));
346
357
  }
@@ -1,5 +1,5 @@
1
1
  import type { JSONPatch } from '../json-patch/JSONPatch.js';
2
- import type { Change, EditableVersionMetadata, PatchesState } from '../types.js';
2
+ import type { Change, DeepRequired, EditableVersionMetadata, PatchesState } from '../types.js';
3
3
  import type { PatchesStoreBackend } from './types.js';
4
4
  /**
5
5
  * Configuration options for the PatchesServer.
@@ -61,7 +61,7 @@ export declare class PatchesServer {
61
61
  * @param mutator
62
62
  * @returns
63
63
  */
64
- change<T = Record<string, any>>(docId: string, mutator: (draft: T, patch: JSONPatch) => void, metadata?: Record<string, any>): Promise<Change | null>;
64
+ change<T = Record<string, any>>(docId: string, mutator: (draft: DeepRequired<T>, patch: JSONPatch) => void, metadata?: Record<string, any>): Promise<Change | null>;
65
65
  /**
66
66
  * Deletes a document.
67
67
  * @param docId The document ID.
package/dist/types.d.ts CHANGED
@@ -124,4 +124,14 @@ export interface ListVersionsOptions {
124
124
  /** Filter by the group ID (branch ID or offline batch ID). */
125
125
  groupId?: string;
126
126
  }
127
+ /**
128
+ * Makes optional object properties required for Proxies that allow setting deep properties.
129
+ */
130
+ export type DeepRequired<T> = {
131
+ [P in keyof T as undefined extends T[P] ? never : P]: T[P] extends object ? DeepRequired<T[P]> : T[P];
132
+ } & {
133
+ [P in keyof T as undefined extends T[P] ? T[P] extends object | undefined ? T[P] extends Function | Date | RegExp | Array<any> | null | undefined ? never : P : never : never]-?: T[P] extends object | undefined ? DeepRequired<T[P]> : T[P];
134
+ } & {
135
+ [P in keyof T as undefined extends T[P] ? T[P] extends object | undefined ? T[P] extends Function | Date | RegExp | Array<any> | null | undefined ? P : never : P : never]?: T[P];
136
+ };
127
137
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dabble/patches",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
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": {
@@ -56,6 +56,8 @@
56
56
  "prepare": "npm run build",
57
57
  "test": "vitest run",
58
58
  "tdd": "vitest",
59
+ "format": "prettier --write src/",
60
+ "format:check": "prettier --check src/",
59
61
  "lint": "eslint src tests",
60
62
  "lint:fix": "eslint src tests --fix"
61
63
  },
@@ -67,16 +69,16 @@
67
69
  "simplified-concurrency": "^0.2.0"
68
70
  },
69
71
  "devDependencies": {
70
- "@sveltejs/package": "^2.3.11",
72
+ "@sveltejs/package": "^2.5.0",
71
73
  "@types/simple-peer": "^9.11.8",
72
- "@typescript-eslint/eslint-plugin": "^8.33.1",
73
- "@typescript-eslint/parser": "^8.33.1",
74
- "eslint": "^9.28.0",
75
- "fake-indexeddb": "^6.0.0",
76
- "prettier": "^3.5.3",
77
- "typescript": "^5.8.3",
78
- "vite": "^6.3.5",
79
- "vite-plugin-dts": "^4.5.3",
80
- "vitest": "^3.1.3"
74
+ "@typescript-eslint/eslint-plugin": "^8.42.0",
75
+ "@typescript-eslint/parser": "^8.42.0",
76
+ "eslint": "^9.35.0",
77
+ "fake-indexeddb": "^6.2.2",
78
+ "prettier": "^3.6.2",
79
+ "typescript": "^5.9.2",
80
+ "vite": "^7.1.4",
81
+ "vite-plugin-dts": "^4.5.4",
82
+ "vitest": "^3.2.4"
81
83
  }
82
84
  }