@dabble/patches 0.8.8 → 0.8.10

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.
Files changed (56) hide show
  1. package/dist/algorithms/ot/shared/changeBatching.js +1 -1
  2. package/dist/client/BranchClientStore.d.ts +71 -0
  3. package/dist/client/BranchClientStore.js +0 -0
  4. package/dist/client/ClientAlgorithm.d.ts +12 -0
  5. package/dist/client/IndexedDBStore.d.ts +13 -2
  6. package/dist/client/IndexedDBStore.js +125 -1
  7. package/dist/client/LWWInMemoryStore.js +5 -3
  8. package/dist/client/LWWIndexedDBStore.d.ts +1 -0
  9. package/dist/client/LWWIndexedDBStore.js +14 -5
  10. package/dist/client/OTAlgorithm.d.ts +3 -0
  11. package/dist/client/OTAlgorithm.js +4 -0
  12. package/dist/client/OTClientStore.d.ts +11 -0
  13. package/dist/client/OTIndexedDBStore.d.ts +9 -0
  14. package/dist/client/OTIndexedDBStore.js +15 -3
  15. package/dist/client/Patches.d.ts +6 -0
  16. package/dist/client/Patches.js +11 -0
  17. package/dist/client/PatchesBranchClient.d.ts +73 -12
  18. package/dist/client/PatchesBranchClient.js +143 -14
  19. package/dist/client/index.d.ts +3 -1
  20. package/dist/compression/index.d.ts +15 -7
  21. package/dist/compression/index.js +13 -2
  22. package/dist/index.d.ts +4 -2
  23. package/dist/net/PatchesClient.d.ts +13 -8
  24. package/dist/net/PatchesClient.js +17 -10
  25. package/dist/net/PatchesSync.d.ts +36 -3
  26. package/dist/net/PatchesSync.js +72 -0
  27. package/dist/net/index.d.ts +2 -1
  28. package/dist/net/protocol/types.d.ts +5 -4
  29. package/dist/net/rest/PatchesREST.d.ts +8 -5
  30. package/dist/net/rest/PatchesREST.js +30 -20
  31. package/dist/net/rest/index.d.ts +1 -1
  32. package/dist/net/rest/index.js +1 -2
  33. package/dist/net/rest/utils.d.ts +1 -9
  34. package/dist/net/rest/utils.js +0 -4
  35. package/dist/server/BranchManager.d.ts +11 -10
  36. package/dist/server/LWWBranchManager.d.ts +10 -9
  37. package/dist/server/LWWBranchManager.js +48 -31
  38. package/dist/server/LWWMemoryStoreBackend.d.ts +5 -2
  39. package/dist/server/LWWMemoryStoreBackend.js +21 -3
  40. package/dist/server/OTBranchManager.d.ts +14 -12
  41. package/dist/server/OTBranchManager.js +58 -66
  42. package/dist/server/branchUtils.d.ts +6 -15
  43. package/dist/server/branchUtils.js +16 -13
  44. package/dist/server/index.d.ts +1 -1
  45. package/dist/server/index.js +2 -2
  46. package/dist/server/types.d.ts +8 -2
  47. package/dist/solid/context.d.ts +1 -0
  48. package/dist/solid/index.d.ts +2 -1
  49. package/dist/solid/primitives.d.ts +30 -155
  50. package/dist/solid/primitives.js +53 -219
  51. package/dist/types.d.ts +35 -6
  52. package/dist/vue/composables.d.ts +29 -170
  53. package/dist/vue/composables.js +59 -200
  54. package/dist/vue/index.d.ts +2 -1
  55. package/dist/vue/provider.d.ts +1 -0
  56. package/package.json +1 -1
@@ -7,14 +7,12 @@ import {
7
7
  createEffect,
8
8
  onCleanup
9
9
  } from "solid-js";
10
- import { JSONPatch } from "../json-patch/JSONPatch.js";
11
10
  import { usePatchesContext } from "./context.js";
12
11
  import { getDocManager } from "./doc-manager.js";
13
- function createDocReactiveState(options) {
14
- const { initialLoading = true, hasSyncContext = false, transformState, changeBehavior } = options;
12
+ function createDocReactiveState(hasSyncContext) {
15
13
  const [doc, setDoc] = createSignal(void 0);
16
14
  const [data, setData] = createSignal(void 0);
17
- const [loading, setLoading] = createSignal(initialLoading);
15
+ const [loading, setLoading] = createSignal(false);
18
16
  const [error, setError] = createSignal();
19
17
  const [rev, setRev] = createSignal(0);
20
18
  const [hasPending, setHasPending] = createSignal(false);
@@ -34,9 +32,6 @@ function createDocReactiveState(options) {
34
32
  }
35
33
  }
36
34
  const unsubState = patchesDoc.subscribe((state) => {
37
- if (transformState && state) {
38
- state = transformState(state, patchesDoc);
39
- }
40
35
  setData(() => state);
41
36
  setRev(patchesDoc.committedRev);
42
37
  setHasPending(patchesDoc.hasPending);
@@ -60,155 +55,60 @@ function createDocReactiveState(options) {
60
55
  setHasPending(false);
61
56
  }
62
57
  function change(mutator) {
63
- if (changeBehavior === "throw") {
64
- const currentDoc = doc();
65
- if (!currentDoc) {
66
- throw new Error("Cannot make changes: document not loaded yet");
67
- }
68
- currentDoc.change(mutator);
69
- } else {
70
- doc()?.change(mutator);
71
- }
72
- }
73
- const baseReturn = {
74
- data,
75
- loading,
76
- error,
77
- rev,
78
- hasPending,
79
- change,
80
- doc
81
- };
82
- return {
83
- doc,
84
- setDoc,
85
- data,
86
- setData,
87
- loading,
88
- setLoading,
89
- error,
90
- setError,
91
- rev,
92
- setRev,
93
- hasPending,
94
- setHasPending,
95
- setupDoc,
96
- resetSignals,
97
- change,
98
- baseReturn
99
- };
100
- }
101
- function usePatchesDoc(docIdOrOptions, options) {
102
- if (typeof docIdOrOptions === "string" || typeof docIdOrOptions === "function") {
103
- return _usePatchesDocEager(docIdOrOptions, options ?? {});
58
+ doc()?.change(mutator);
104
59
  }
105
- return _usePatchesDocLazy(docIdOrOptions ?? {});
60
+ const baseReturn = { data, loading, error, rev, hasPending, change, doc };
61
+ return { setupDoc, resetSignals, setError, baseReturn };
106
62
  }
107
- function _usePatchesDocEager(docId, options) {
63
+ function usePatchesDoc(docId, options) {
108
64
  const { patches, sync } = usePatchesContext();
109
- const { autoClose = false, algorithm, metadata } = options;
110
- const shouldUntrack = autoClose === "untrack";
65
+ const { untrack: shouldUntrack = false, algorithm, metadata } = options ?? {};
111
66
  const openDocOpts = { algorithm, metadata };
112
67
  const manager = getDocManager(patches);
113
- const { setupDoc, setError, setLoading, baseReturn } = createDocReactiveState({
114
- initialLoading: !!autoClose,
115
- hasSyncContext: !!sync,
116
- changeBehavior: "throw"
68
+ const { setupDoc, resetSignals, setError, baseReturn } = createDocReactiveState(!!sync);
69
+ const source = typeof docId === "string" ? () => docId : () => docId() || null;
70
+ let currentId = null;
71
+ const [docResource] = createResource(source, async (id) => {
72
+ return await manager.openDoc(patches, id, openDocOpts);
117
73
  });
118
- const docIdAccessor = toAccessor(docId);
119
- if (autoClose) {
120
- const [docResource] = createResource(docIdAccessor, async (id) => {
121
- return await manager.openDoc(patches, id, openDocOpts);
122
- });
123
- createEffect(() => {
124
- const loadedDoc = docResource();
125
- if (loadedDoc) {
126
- const unsub = setupDoc(loadedDoc);
127
- onCleanup(() => unsub());
128
- }
129
- const resourceError = docResource.error;
130
- if (resourceError) {
131
- setError(resourceError);
132
- setLoading(false);
133
- }
134
- });
135
- onCleanup(() => {
136
- const id = docIdAccessor();
137
- manager.closeDoc(patches, id, shouldUntrack);
138
- });
139
- } else {
140
- createEffect(() => {
141
- const id = docIdAccessor();
142
- const patchesDoc = patches.getOpenDoc(id);
143
- if (!patchesDoc) {
144
- throw new Error(
145
- `Document "${id}" is not open. Either open it with patches.openDoc() first, or use { autoClose: true } option.`
146
- );
147
- }
148
- manager.incrementRefCount(id);
149
- const unsub = setupDoc(patchesDoc);
150
- onCleanup(() => {
151
- unsub();
152
- manager.decrementRefCount(id);
153
- });
154
- });
155
- }
156
- return baseReturn;
157
- }
158
- function _usePatchesDocLazy(options) {
159
- const { patches, sync } = usePatchesContext();
160
- const { idProp } = options;
161
- const { setupDoc, resetSignals, setError, setLoading, baseReturn } = createDocReactiveState({
162
- initialLoading: false,
163
- hasSyncContext: !!sync,
164
- changeBehavior: "noop",
165
- transformState: idProp ? (state, patchesDoc) => ({ ...state, [idProp]: patchesDoc.id }) : void 0
74
+ createEffect(() => {
75
+ const currentSource = source();
76
+ const loadedDoc = docResource();
77
+ if (currentSource && loadedDoc) {
78
+ currentId = currentSource;
79
+ const unsub = setupDoc(loadedDoc);
80
+ onCleanup(() => unsub());
81
+ } else {
82
+ resetSignals();
83
+ }
84
+ const resourceError = docResource.error;
85
+ if (resourceError) {
86
+ setError(resourceError);
87
+ }
166
88
  });
167
- let unsubscribe = null;
168
- const [path, setPath] = createSignal(null);
169
- function teardown() {
170
- unsubscribe?.();
171
- unsubscribe = null;
172
- resetSignals();
173
- }
174
- async function load(docPath, options2) {
175
- if (path()) {
176
- const prevPath = path();
177
- teardown();
178
- await patches.closeDoc(prevPath);
89
+ createEffect((prevId) => {
90
+ const id = source();
91
+ if (prevId && prevId !== id) {
92
+ currentId = null;
93
+ manager.closeDoc(patches, prevId, shouldUntrack);
179
94
  }
180
- setPath(docPath);
181
- setLoading(true);
182
- try {
183
- const patchesDoc = await patches.openDoc(docPath, options2);
184
- unsubscribe = setupDoc(patchesDoc);
185
- } catch (err) {
186
- setError(err);
187
- setLoading(false);
95
+ return id;
96
+ });
97
+ baseReturn.close = async () => {
98
+ if (currentId) {
99
+ const id = currentId;
100
+ currentId = null;
101
+ resetSignals();
102
+ await manager.closeDoc(patches, id, shouldUntrack);
188
103
  }
189
- }
190
- async function close() {
191
- if (path()) {
192
- const prevPath = path();
193
- teardown();
194
- setPath(null);
195
- await patches.closeDoc(prevPath);
104
+ };
105
+ onCleanup(() => {
106
+ if (currentId) {
107
+ manager.closeDoc(patches, currentId, shouldUntrack);
108
+ currentId = null;
196
109
  }
197
- }
198
- async function create(docPath, initialState, options2) {
199
- const newDoc = await patches.openDoc(docPath, options2);
200
- newDoc.change((patch, root) => {
201
- if (initialState instanceof JSONPatch) {
202
- patch.ops = initialState.ops;
203
- } else {
204
- const state = { ...initialState };
205
- if (idProp) delete state[idProp];
206
- patch.replace(root, state);
207
- }
208
- });
209
- await patches.closeDoc(docPath);
210
- }
211
- return { ...baseReturn, path, load, close, create };
110
+ });
111
+ return baseReturn;
212
112
  }
213
113
  function usePatchesSync() {
214
114
  const { sync } = usePatchesContext();
@@ -226,79 +126,16 @@ function usePatchesSync() {
226
126
  onCleanup(() => {
227
127
  unsubscribe();
228
128
  });
229
- return {
230
- connected,
231
- syncing,
232
- online
233
- };
234
- }
235
- function toAccessor(value) {
236
- return typeof value === "function" ? value : () => value;
129
+ return { connected, syncing, online };
237
130
  }
238
131
  function createPatchesDoc(name) {
239
132
  const Context = createContext();
240
133
  function Provider(props) {
241
- const { patches, sync } = usePatchesContext();
242
- const manager = getDocManager(patches);
243
- const autoClose = props.autoClose ?? false;
244
- const shouldUntrack = autoClose === "untrack";
245
- const openDocOpts = { algorithm: props.algorithm, metadata: props.metadata };
246
- const { setupDoc, setError, setLoading, baseReturn } = createDocReactiveState({
247
- initialLoading: !!autoClose,
248
- hasSyncContext: !!sync,
249
- changeBehavior: "throw"
250
- });
251
- const docIdAccessor = toAccessor(props.docId);
252
- if (autoClose) {
253
- const [docResource] = createResource(docIdAccessor, async (id) => {
254
- return await manager.openDoc(patches, id, openDocOpts);
255
- });
256
- createEffect(() => {
257
- const loadedDoc = docResource();
258
- if (loadedDoc) {
259
- const unsub = setupDoc(loadedDoc);
260
- onCleanup(() => unsub());
261
- }
262
- const resourceError = docResource.error;
263
- if (resourceError) {
264
- setError(resourceError);
265
- setLoading(false);
266
- }
267
- });
268
- createEffect((prevId) => {
269
- const currentId = docIdAccessor();
270
- if (prevId && prevId !== currentId) {
271
- manager.closeDoc(patches, prevId, shouldUntrack);
272
- }
273
- return currentId;
274
- });
275
- onCleanup(() => {
276
- const id = docIdAccessor();
277
- manager.closeDoc(patches, id, shouldUntrack);
278
- });
279
- } else {
280
- createEffect((prevId) => {
281
- const id = docIdAccessor();
282
- if (prevId && prevId !== id) {
283
- manager.decrementRefCount(prevId);
284
- }
285
- const patchesDoc = patches.getOpenDoc(id);
286
- if (!patchesDoc) {
287
- throw new Error(
288
- `Document "${id}" is not open. Either open it with patches.openDoc() first, or use autoClose option.`
289
- );
290
- }
291
- manager.incrementRefCount(id);
292
- const unsub = setupDoc(patchesDoc);
293
- onCleanup(() => unsub());
294
- return id;
295
- });
296
- onCleanup(() => {
297
- const id = docIdAccessor();
298
- manager.decrementRefCount(id);
299
- });
300
- }
301
- return /* @__PURE__ */ React.createElement(Context.Provider, { value: baseReturn, children: props.children });
134
+ const result = usePatchesDoc(
135
+ typeof props.docId === "function" ? props.docId : props.docId,
136
+ { untrack: props.untrack, algorithm: props.algorithm, metadata: props.metadata }
137
+ );
138
+ return /* @__PURE__ */ React.createElement(Context.Provider, { value: result, children: props.children });
302
139
  }
303
140
  function useDoc() {
304
141
  const context = useContext(Context);
@@ -309,10 +146,7 @@ function createPatchesDoc(name) {
309
146
  }
310
147
  return context;
311
148
  }
312
- return {
313
- Provider,
314
- useDoc
315
- };
149
+ return { Provider, useDoc };
316
150
  }
317
151
  export {
318
152
  createPatchesDoc,
package/dist/types.d.ts CHANGED
@@ -81,8 +81,6 @@ interface DocSyncState {
81
81
  syncError?: Error;
82
82
  isLoaded: boolean;
83
83
  }
84
- /** Status options for a branch */
85
- type BranchStatus = 'open' | 'closed' | 'merged' | 'archived' | 'abandoned';
86
84
  interface Branch {
87
85
  /** The ID of the branch document. */
88
86
  id: string;
@@ -92,10 +90,10 @@ interface Branch {
92
90
  branchedAtRev: number;
93
91
  /** Unix timestamp in milliseconds when the branch was created. */
94
92
  createdAt: number;
93
+ /** Unix timestamp in milliseconds when the branch was last modified. Updated on metadata changes. */
94
+ modifiedAt: number;
95
95
  /** Optional user-friendly name for the branch. */
96
96
  name?: string;
97
- /** Current status of the branch. */
98
- status: BranchStatus;
99
97
  /**
100
98
  * The first revision on the branch that contains user content (after initialization changes).
101
99
  * Initialization changes (e.g. the root-replace that seeds the branch with source state)
@@ -104,10 +102,41 @@ interface Branch {
104
102
  * across multiple changes due to size limits.
105
103
  */
106
104
  contentStartRev: number;
105
+ /**
106
+ * The branch-side revision through which changes have already been merged.
107
+ * Set after each merge so subsequent merges only pick up new changes.
108
+ * Undefined means the branch has never been merged.
109
+ */
110
+ lastMergedRev?: number;
111
+ /** The pending operation to sync to the server. Set by BranchClientStore methods. */
112
+ pendingOp?: 'create' | 'update' | 'delete';
113
+ /** True when this branch has been deleted. Stored as a tombstone for incremental sync. */
114
+ deleted?: true;
107
115
  /** Optional arbitrary metadata associated with the branch record. */
108
116
  [metadata: string]: any;
109
117
  }
110
- type EditableBranchMetadata = Disallowed<Branch, 'id' | 'docId' | 'branchedAtRev' | 'createdAt' | 'status' | 'contentStartRev'>;
118
+ type EditableBranchMetadata = Disallowed<Branch, 'id' | 'docId' | 'branchedAtRev' | 'createdAt' | 'modifiedAt' | 'contentStartRev' | 'pendingOp' | 'deleted'>;
119
+ /**
120
+ * Metadata for creating a new branch.
121
+ * Allows `id` and `contentStartRev` in addition to the fields allowed by `EditableBranchMetadata`.
122
+ * - `id`: Client-provided branch document ID. Required for offline branch creation.
123
+ * - `contentStartRev`: The first revision of user content. Set by the client when creating
124
+ * initial changes offline (the server uses this to know where user content begins during merge).
125
+ */
126
+ type CreateBranchMetadata = Omit<Disallowed<Branch, 'docId' | 'branchedAtRev' | 'createdAt' | 'modifiedAt' | 'pendingOp' | 'deleted'>, 'contentStartRev'> & {
127
+ contentStartRev?: number;
128
+ };
129
+ /**
130
+ * Options for listing branches.
131
+ */
132
+ interface ListBranchesOptions {
133
+ /**
134
+ * Only return branches modified after this timestamp (Unix ms).
135
+ * Enables incremental sync: after the initial full list, subsequent calls can pass the
136
+ * most recent `modifiedAt` value to fetch only updates.
137
+ */
138
+ since?: number;
139
+ }
111
140
  /**
112
141
  * Represents a tombstone for a deleted document.
113
142
  * Tombstones persist after deletion to inform late-connecting clients
@@ -247,4 +276,4 @@ type PathProxy<T = any> = IsAny<T> extends true ? DeepPathProxy : {
247
276
  */
248
277
  type ChangeMutator<T> = (patch: JSONPatch, root: PathProxy<T>) => void;
249
278
 
250
- export type { Branch, BranchStatus, Change, ChangeInput, ChangeMutator, CommitChangesOptions, DeleteDocOptions, DocSyncState, DocSyncStatus, DocumentTombstone, EditableBranchMetadata, EditableVersionMetadata, ListChangesOptions, ListVersionsOptions, PatchesSnapshot, PatchesState, PathProxy, VersionMetadata };
279
+ export type { Branch, Change, ChangeInput, ChangeMutator, CommitChangesOptions, CreateBranchMetadata, DeleteDocOptions, DocSyncState, DocSyncStatus, DocumentTombstone, EditableBranchMetadata, EditableVersionMetadata, ListBranchesOptions, ListChangesOptions, ListVersionsOptions, PatchesSnapshot, PatchesState, PathProxy, VersionMetadata };
@@ -1,243 +1,102 @@
1
- import { ShallowRef, Ref, MaybeRef } from 'vue';
1
+ import { ShallowRef, Ref, MaybeRef, MaybeRefOrGetter } from 'vue';
2
2
  import { OpenDocOptions } from '../client/Patches.js';
3
3
  import { P as PatchesDoc } from '../BaseDoc-BT18xPxU.js';
4
- import { JSONPatch } from '../json-patch/JSONPatch.js';
5
4
  import { ChangeMutator } from '../types.js';
6
5
  import 'easy-signal';
7
6
  import '../json-patch/types.js';
8
7
  import '../client/ClientAlgorithm.js';
9
8
  import '../client/PatchesStore.js';
9
+ import '../json-patch/JSONPatch.js';
10
10
  import '@dabble/delta';
11
11
 
12
12
  /**
13
- * Options for usePatchesDoc composable (eager mode with docId).
13
+ * Options for usePatchesDoc composable.
14
14
  */
15
15
  interface UsePatchesDocOptions extends OpenDocOptions {
16
16
  /**
17
- * Controls document lifecycle management on component unmount.
18
- *
19
- * - `false` (default): Explicit mode. Assumes doc is already open. Throws if not.
20
- * - `true`: Opens doc on mount with ref counting, closes on unmount (doc stays tracked).
21
- * - `'untrack'`: Opens doc on mount, closes AND untracks on unmount (removes from sync).
22
- *
23
- * @default false
17
+ * When true, the document is removed from sync tracking on close.
18
+ * By default documents stay tracked after closing.
24
19
  */
25
- autoClose?: boolean | 'untrack';
20
+ untrack?: boolean;
26
21
  }
27
22
  /**
28
- * Options for usePatchesDoc composable (lazy mode without docId).
29
- */
30
- interface UsePatchesDocLazyOptions {
31
- /**
32
- * Inject doc.id into state under this key on every state update.
33
- * Useful when the document ID is derived from the path but needed in the data.
34
- */
35
- idProp?: string;
36
- }
37
- /**
38
- * Return type for usePatchesDoc composable (eager mode).
23
+ * Return type for usePatchesDoc composable.
39
24
  */
40
25
  interface UsePatchesDocReturn<T extends object> {
41
- /**
42
- * Reactive reference to the document state.
43
- * Updated whenever the document changes (local or remote).
44
- */
26
+ /** Reactive reference to the document state. */
45
27
  data: ShallowRef<T | undefined>;
46
- /**
47
- * Whether the document is currently loading/syncing.
48
- * - `true` during initial load or updates
49
- * - `false` when fully synced
50
- */
28
+ /** Whether the document is currently loading. */
51
29
  loading: Ref<boolean>;
52
- /**
53
- * Error that occurred during sync, if any.
54
- */
30
+ /** Error that occurred during sync, if any. */
55
31
  error: Ref<Error | undefined>;
56
- /**
57
- * The committed revision number.
58
- * Increments each time the server confirms changes.
59
- */
32
+ /** The committed revision number. */
60
33
  rev: Ref<number>;
61
- /**
62
- * Whether there are pending local changes not yet committed by server.
63
- */
34
+ /** Whether there are pending local changes not yet committed by server. */
64
35
  hasPending: Ref<boolean>;
65
- /**
66
- * Make changes to the document.
67
- *
68
- * @example
69
- * ```typescript
70
- * change((patch, root) => {
71
- * patch.replace(root.title!, 'New Title')
72
- * })
73
- * ```
74
- */
36
+ /** Make changes to the document. No-ops if the document is not loaded. */
75
37
  change: (mutator: ChangeMutator<T>) => void;
76
- /**
77
- * The underlying PatchesDoc instance.
78
- * Useful for advanced operations.
79
- */
80
- doc: ShallowRef<PatchesDoc<T> | undefined>;
81
- }
82
- /**
83
- * Return type for usePatchesDoc composable (lazy mode).
84
- * Extends the eager return type with lifecycle management methods.
85
- */
86
- interface UsePatchesDocLazyReturn<T extends object> extends UsePatchesDocReturn<T> {
87
- /**
88
- * Current document path. `null` when no document is loaded.
89
- */
90
- path: Ref<string | null>;
91
- /**
92
- * Open a document by path. Closes any previously loaded document first.
93
- *
94
- * @param docPath - The document path to open
95
- * @param options - Optional algorithm and metadata overrides
96
- */
97
- load: (docPath: string, options?: OpenDocOptions) => Promise<void>;
98
- /**
99
- * Close the current document, unsubscribe, and reset all state.
100
- * Calls `patches.closeDoc()` but does not untrack — tracking is managed separately.
101
- */
38
+ /** Close the document and reset state. Useful for explicit cleanup without unmounting. */
102
39
  close: () => Promise<void>;
103
- /**
104
- * Create a new document: open it, set initial state, then close it.
105
- * A one-shot operation that doesn't bind the document to this handle.
106
- *
107
- * @param docPath - The document path to create
108
- * @param initialState - Initial state object or JSONPatch to apply
109
- * @param options - Optional algorithm and metadata overrides
110
- */
111
- create: (docPath: string, initialState: T | JSONPatch, options?: OpenDocOptions) => Promise<void>;
40
+ /** The underlying PatchesDoc instance. */
41
+ doc: ShallowRef<PatchesDoc<T> | undefined>;
112
42
  }
113
43
  /**
114
44
  * Vue composable for reactive Patches document state.
115
45
  *
116
- * ## Eager Mode (with docId)
117
- *
118
- * Provides reactive access to an already-open Patches document.
119
- *
120
- * @example
121
- * ```typescript
122
- * // Explicit lifecycle — you control open/close
123
- * const { data, loading, change } = usePatchesDoc('doc-123')
124
- *
125
- * // Auto lifecycle — opens on mount, closes on unmount
126
- * const { data, loading, change } = usePatchesDoc('doc-123', { autoClose: true })
127
- * ```
128
- *
129
- * ## Lazy Mode (without docId)
130
- *
131
- * Returns a deferred handle with `load()`, `close()`, and `create()` methods.
132
- * Ideal for Pinia stores where the document path isn't known at creation time.
133
- * Does NOT use `onBeforeUnmount` — caller manages lifecycle.
46
+ * Opens the document automatically and closes it on unmount (or when the path
47
+ * changes). Accepts a static string, a ref, or a getter. When the value is
48
+ * `null` or `undefined`, no document is loaded.
134
49
  *
135
50
  * @example
136
51
  * ```typescript
137
- * // In a Pinia store
138
- * const { data, load, close, change, create } = usePatchesDoc<Project>()
52
+ * // Static
53
+ * const { data, change } = usePatchesDoc('doc-123')
139
54
  *
140
- * // Later, when the user navigates:
141
- * await load('projects/abc/content')
142
- *
143
- * // When leaving:
144
- * await close()
55
+ * // Reactive swaps automatically
56
+ * const { data, change } = usePatchesDoc(() => currentId.value && `projects/${currentId.value}`)
145
57
  * ```
146
58
  */
147
59
  declare function usePatchesDoc<T extends object>(docId: string, options?: UsePatchesDocOptions): UsePatchesDocReturn<T>;
148
- declare function usePatchesDoc<T extends object>(options?: UsePatchesDocLazyOptions): UsePatchesDocLazyReturn<T>;
60
+ declare function usePatchesDoc<T extends object>(docId: MaybeRefOrGetter<string | null | undefined>, options?: UsePatchesDocOptions): UsePatchesDocReturn<T>;
149
61
  /**
150
62
  * Return type for usePatchesSync composable.
151
63
  */
152
64
  interface UsePatchesSyncReturn {
153
- /**
154
- * Whether the WebSocket connection is established.
155
- */
156
65
  connected: Ref<boolean>;
157
- /**
158
- * Whether documents are currently syncing with the server.
159
- */
160
66
  syncing: Ref<boolean>;
161
- /**
162
- * Whether the client believes it has network connectivity.
163
- */
164
67
  online: Ref<boolean>;
165
68
  }
166
69
  /**
167
70
  * Vue composable for reactive Patches sync state.
168
71
  *
169
- * Provides reactive access to PatchesSync connection and sync status.
170
- * Useful for showing "Offline" banners, global loading indicators, etc.
171
- *
172
72
  * @example
173
73
  * ```typescript
174
74
  * const { connected, syncing, online } = usePatchesSync()
175
- *
176
- * // Show offline banner
177
- * if (!connected) {
178
- * // Display "You are offline"
179
- * }
180
75
  * ```
181
- *
182
- * @returns Reactive sync state
183
- * @throws Error if Patches context not provided
184
- * @throws Error if PatchesSync was not provided to context
185
76
  */
186
77
  declare function usePatchesSync(): UsePatchesSyncReturn;
187
78
  /**
188
79
  * Provides a Patches document in the component tree with a given name.
189
80
  *
190
- * This enables child components to access the document using `useCurrentDoc(name)`
191
- * without needing to pass the docId down through props. Supports both static and
192
- * reactive docIds.
193
- *
194
- * ## Use Cases
195
- *
196
- * **Static document (user settings):**
81
+ * @example
197
82
  * ```typescript
83
+ * // Static
198
84
  * providePatchesDoc('user', 'user-123')
199
- * ```
200
85
  *
201
- * **Reactive document (multi-tab with autoClose: false):**
202
- * ```typescript
203
- * const activeTabId = ref('design-1')
204
- * providePatchesDoc('whiteboard', activeTabId) // Keeps all docs open, switches between them
205
- * ```
206
- *
207
- * **Reactive document (single-doc with autoClose: true):**
208
- * ```typescript
86
+ * // Reactive
209
87
  * const currentDocId = ref('doc-1')
210
- * providePatchesDoc('document', currentDocId, { autoClose: true }) // Closes old, opens new
88
+ * providePatchesDoc('document', currentDocId)
211
89
  * ```
212
- *
213
- * @param name - Unique identifier for this document context (e.g., 'whiteboard', 'user')
214
- * @param docId - Document ID (static string or reactive ref)
215
- * @param options - Configuration options (autoClose, etc.)
216
- *
217
- * @throws Error if Patches context not provided
218
- * @throws Error if doc not open in explicit mode
219
90
  */
220
91
  declare function providePatchesDoc<T extends object>(name: string, docId: MaybeRef<string>, options?: UsePatchesDocOptions): UsePatchesDocReturn<T>;
221
92
  /**
222
93
  * Injects a Patches document provided by `providePatchesDoc`.
223
94
  *
224
- * Use this in child components to access a document provided higher up in the
225
- * component tree without passing the docId through props.
226
- *
227
95
  * @example
228
96
  * ```typescript
229
- * // Parent component
230
- * const whiteboardId = ref('whiteboard-123')
231
- * providePatchesDoc('whiteboard', whiteboardId)
232
- *
233
- * // Child component (anywhere in tree)
234
97
  * const { data, loading, change } = useCurrentDoc<WhiteboardDoc>('whiteboard')
235
98
  * ```
236
- *
237
- * @param name - The name used in providePatchesDoc
238
- * @returns Reactive document state and utilities
239
- * @throws Error if no document provided with that name
240
99
  */
241
100
  declare function useCurrentDoc<T extends object>(name: string): UsePatchesDocReturn<T>;
242
101
 
243
- export { type UsePatchesDocLazyOptions, type UsePatchesDocLazyReturn, type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, providePatchesDoc, useCurrentDoc, usePatchesDoc, usePatchesSync };
102
+ export { type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, providePatchesDoc, useCurrentDoc, usePatchesDoc, usePatchesSync };