@dabble/patches 0.7.1 → 0.7.3

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 (50) hide show
  1. package/dist/client/IndexedDBStore.js +1 -1
  2. package/dist/client/LWWAlgorithm.js +3 -2
  3. package/dist/client/LWWBatcher.d.ts +84 -0
  4. package/dist/client/LWWBatcher.js +85 -0
  5. package/dist/client/LWWDoc.d.ts +3 -1
  6. package/dist/client/LWWDoc.js +4 -2
  7. package/dist/client/Patches.d.ts +5 -0
  8. package/dist/client/Patches.js +16 -2
  9. package/dist/client/index.d.ts +1 -0
  10. package/dist/client/index.js +1 -0
  11. package/dist/compression/index.js +1 -6
  12. package/dist/index.d.ts +1 -0
  13. package/dist/net/PatchesClient.d.ts +2 -2
  14. package/dist/net/PatchesClient.js +2 -2
  15. package/dist/net/index.d.ts +0 -1
  16. package/dist/net/protocol/JSONRPCServer.js +5 -3
  17. package/dist/net/protocol/types.d.ts +1 -0
  18. package/dist/net/webrtc/WebRTCAwareness.js +2 -3
  19. package/dist/net/websocket/WebSocketTransport.js +5 -2
  20. package/dist/server/LWWServer.d.ts +4 -4
  21. package/dist/server/LWWServer.js +3 -3
  22. package/dist/server/OTServer.d.ts +2 -2
  23. package/dist/server/OTServer.js +2 -3
  24. package/dist/shared/doc-manager.d.ts +89 -0
  25. package/dist/shared/doc-manager.js +126 -0
  26. package/dist/shared/utils.d.ts +22 -0
  27. package/dist/shared/utils.js +22 -0
  28. package/dist/solid/doc-manager.d.ts +3 -81
  29. package/dist/solid/doc-manager.js +1 -120
  30. package/dist/solid/index.d.ts +4 -2
  31. package/dist/solid/index.js +4 -0
  32. package/dist/solid/managed-docs.d.ts +63 -0
  33. package/dist/solid/managed-docs.js +105 -0
  34. package/dist/solid/primitives.d.ts +73 -68
  35. package/dist/solid/primitives.js +111 -4
  36. package/dist/solid/utils.d.ts +1 -0
  37. package/dist/solid/utils.js +6 -0
  38. package/dist/vue/composables.d.ts +67 -35
  39. package/dist/vue/composables.js +111 -4
  40. package/dist/vue/doc-manager.d.ts +3 -81
  41. package/dist/vue/doc-manager.js +1 -120
  42. package/dist/vue/index.d.ts +4 -2
  43. package/dist/vue/index.js +4 -0
  44. package/dist/vue/managed-docs.d.ts +66 -0
  45. package/dist/vue/managed-docs.js +101 -0
  46. package/dist/vue/utils.d.ts +1 -0
  47. package/dist/vue/utils.js +6 -0
  48. package/package.json +1 -1
  49. package/dist/net/types.d.ts +0 -8
  50. package/dist/net/types.js +0 -0
@@ -1,27 +1,38 @@
1
1
  import { Accessor } from 'solid-js';
2
2
  import { a as PatchesDoc } from '../BaseDoc-DkP3tUhT.js';
3
+ import { JSONPatch } from '../json-patch/JSONPatch.js';
3
4
  import { ChangeMutator } from '../types.js';
4
5
  import '../event-signal.js';
5
6
  import '../json-patch/types.js';
6
- import '../json-patch/JSONPatch.js';
7
7
  import '@dabble/delta';
8
8
 
9
9
  /**
10
- * Options for usePatchesDoc primitive.
10
+ * Options for usePatchesDoc primitive (eager mode with docId).
11
11
  */
12
12
  interface UsePatchesDocOptions {
13
13
  /**
14
- * Whether to automatically open/close the document based on component lifecycle.
14
+ * Controls document lifecycle management on cleanup.
15
15
  *
16
- * - `false` (default): Assumes doc is already open. Throws if not.
17
- * - `true`: Opens doc on mount with ref counting, closes on cleanup.
16
+ * - `false` (default): Explicit mode. Assumes doc is already open. Throws if not.
17
+ * - `true`: Opens doc on mount with ref counting, closes on cleanup (doc stays tracked).
18
+ * - `'untrack'`: Opens doc on mount, closes AND untracks on cleanup (removes from sync).
18
19
  *
19
20
  * @default false
20
21
  */
21
- autoClose?: boolean;
22
+ autoClose?: boolean | 'untrack';
22
23
  }
23
24
  /**
24
- * Return type for usePatchesDoc primitive.
25
+ * Options for usePatchesDoc primitive (lazy mode without docId).
26
+ */
27
+ interface UsePatchesDocLazyOptions {
28
+ /**
29
+ * Inject doc.id into state under this key on every state update.
30
+ * Useful when the document ID is derived from the path but needed in the data.
31
+ */
32
+ idProp?: string;
33
+ }
34
+ /**
35
+ * Return type for usePatchesDoc primitive (eager mode).
25
36
  */
26
37
  interface UsePatchesDocReturn<T extends object> {
27
38
  /**
@@ -65,57 +76,69 @@ interface UsePatchesDocReturn<T extends object> {
65
76
  */
66
77
  doc: Accessor<PatchesDoc<T> | undefined>;
67
78
  }
79
+ /**
80
+ * Return type for usePatchesDoc primitive (lazy mode).
81
+ * Extends the eager return type with lifecycle management methods.
82
+ */
83
+ interface UsePatchesDocLazyReturn<T extends object> extends UsePatchesDocReturn<T> {
84
+ /**
85
+ * Current document path. `null` when no document is loaded.
86
+ */
87
+ path: Accessor<string | null>;
88
+ /**
89
+ * Open a document by path. Closes any previously loaded document first.
90
+ *
91
+ * @param docPath - The document path to open
92
+ */
93
+ load: (docPath: string) => Promise<void>;
94
+ /**
95
+ * Close the current document, unsubscribe, and reset all state.
96
+ * Calls `patches.closeDoc()` but does not untrack — tracking is managed separately.
97
+ */
98
+ close: () => Promise<void>;
99
+ /**
100
+ * Create a new document: open it, set initial state, then close it.
101
+ * A one-shot operation that doesn't bind the document to this handle.
102
+ *
103
+ * @param docPath - The document path to create
104
+ * @param initialState - Initial state object or JSONPatch to apply
105
+ */
106
+ create: (docPath: string, initialState: T | JSONPatch) => Promise<void>;
107
+ }
68
108
  /**
69
109
  * Solid primitive for reactive Patches document state.
70
110
  *
71
- * Provides reactive access to a Patches document with automatic lifecycle management.
72
- *
73
- * ## Explicit Lifecycle (default)
111
+ * ## Eager Mode (with docId)
74
112
  *
75
- * By default, assumes the document is already open and just adds Solid reactivity.
76
- * You control when documents are opened and closed.
113
+ * Provides reactive access to an already-open Patches document.
77
114
  *
78
115
  * @example
79
116
  * ```tsx
80
- * import { onMount, onCleanup } from 'solid-js';
81
- * import { usePatchesContext, usePatchesDoc } from '@dabble/patches/solid';
82
- *
83
- * function MyComponent(props) {
84
- * const { patches } = usePatchesContext();
85
- *
86
- * // You control lifecycle
87
- * onMount(() => patches.openDoc(props.docId));
88
- * onCleanup(() => patches.closeDoc(props.docId));
89
- *
90
- * // Just adds reactivity
91
- * // For static: usePatchesDoc('doc-123')
92
- * // For reactive: usePatchesDoc(() => props.docId)
93
- * const { data, loading, change } = usePatchesDoc(() => props.docId);
117
+ * // Explicit lifecycle you control open/close
118
+ * const { data, loading, change } = usePatchesDoc(() => props.docId)
94
119
  *
95
- * return <div>{data()?.title}</div>;
96
- * }
120
+ * // Auto lifecycle — opens on mount, closes on cleanup
121
+ * const { data, loading, change } = usePatchesDoc(() => props.docId, { autoClose: true })
97
122
  * ```
98
123
  *
99
- * ## Auto Lifecycle
124
+ * ## Lazy Mode (without docId)
100
125
  *
101
- * With `autoClose: true`, the primitive manages the document lifecycle with
102
- * reference counting. Safe to use in multiple components with the same docId.
126
+ * Returns a deferred handle with `load()`, `close()`, and `create()` methods.
127
+ * Does NOT register `onCleanup` caller manages lifecycle.
103
128
  *
104
129
  * @example
105
130
  * ```tsx
106
- * // Opens on mount, closes on cleanup (ref-counted)
107
- * const { data, loading, change } = usePatchesDoc('doc-123', {
108
- * autoClose: true
109
- * });
110
- * ```
131
+ * const { data, load, close, change, create } = usePatchesDoc<Project>()
111
132
  *
112
- * @param docId - Document ID (static string or accessor function)
113
- * @param options - Configuration options
114
- * @returns Reactive document state and utilities
115
- * @throws Error if Patches context not provided
116
- * @throws Error if doc not open in explicit mode
133
+ * // Later, when the user navigates:
134
+ * await load('projects/abc/content')
135
+ *
136
+ * // When leaving:
137
+ * await close()
138
+ * ```
117
139
  */
118
140
  declare function usePatchesDoc<T extends object>(docId: MaybeAccessor<string>, options?: UsePatchesDocOptions): UsePatchesDocReturn<T>;
141
+ declare function usePatchesDoc<T extends object>(options?: UsePatchesDocLazyOptions): UsePatchesDocLazyReturn<T>;
119
142
  /**
120
143
  * Return type for usePatchesSync primitive.
121
144
  */
@@ -159,6 +182,14 @@ declare function usePatchesSync(): UsePatchesSyncReturn;
159
182
  * Type for document ID - can be static string or accessor function.
160
183
  */
161
184
  type MaybeAccessor<T> = T | Accessor<T>;
185
+ /**
186
+ * Props for the Provider component returned by createPatchesDoc.
187
+ */
188
+ interface PatchesDocProviderProps {
189
+ docId: MaybeAccessor<string>;
190
+ autoClose?: boolean | 'untrack';
191
+ children: any;
192
+ }
162
193
  /**
163
194
  * Creates a named document context that can be provided to child components.
164
195
  *
@@ -199,36 +230,10 @@ type MaybeAccessor<T> = T | Accessor<T>;
199
230
  *
200
231
  * @param name - Unique identifier for this document context (e.g., 'whiteboard', 'user')
201
232
  * @returns Object with Provider component and useDoc hook
202
- *
203
- * @example
204
- * ```tsx
205
- * // Create the context
206
- * const { Provider: WhiteboardProvider, useDoc: useWhiteboard } =
207
- * createPatchesDoc<WhiteboardDoc>('whiteboard');
208
- *
209
- * // Use in parent
210
- * <WhiteboardProvider docId="whiteboard-123">
211
- * <Canvas />
212
- * </WhiteboardProvider>
213
- *
214
- * // Use in child
215
- * function Canvas() {
216
- * const { data, change } = useWhiteboard();
217
- * return <div>{data()?.title}</div>;
218
- * }
219
- * ```
220
233
  */
221
- /**
222
- * Props for the Provider component returned by createPatchesDoc.
223
- */
224
- interface PatchesDocProviderProps {
225
- docId: MaybeAccessor<string>;
226
- autoClose?: boolean;
227
- children: any;
228
- }
229
234
  declare function createPatchesDoc<T extends object>(name: string): {
230
235
  Provider: (props: PatchesDocProviderProps) => any;
231
236
  useDoc: () => UsePatchesDocReturn<T>;
232
237
  };
233
238
 
234
- export { type MaybeAccessor, type PatchesDocProviderProps, type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, createPatchesDoc, usePatchesDoc, usePatchesSync };
239
+ export { type MaybeAccessor, type PatchesDocProviderProps, type UsePatchesDocLazyOptions, type UsePatchesDocLazyReturn, type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, createPatchesDoc, usePatchesDoc, usePatchesSync };
@@ -7,11 +7,19 @@ import {
7
7
  createEffect,
8
8
  onCleanup
9
9
  } from "solid-js";
10
+ import { JSONPatch } from "../json-patch/JSONPatch.js";
10
11
  import { usePatchesContext } from "./context.js";
11
12
  import { getDocManager } from "./doc-manager.js";
12
- function usePatchesDoc(docId, options = {}) {
13
+ function usePatchesDoc(docIdOrOptions, options) {
14
+ if (typeof docIdOrOptions === "string" || typeof docIdOrOptions === "function") {
15
+ return _usePatchesDocEager(docIdOrOptions, options ?? {});
16
+ }
17
+ return _usePatchesDocLazy(docIdOrOptions ?? {});
18
+ }
19
+ function _usePatchesDocEager(docId, options) {
13
20
  const { patches } = usePatchesContext();
14
21
  const autoClose = options.autoClose ?? false;
22
+ const shouldUntrack = autoClose === "untrack";
15
23
  const [doc, setDoc] = createSignal(void 0);
16
24
  const [data, setData] = createSignal(void 0);
17
25
  const [loading, setLoading] = createSignal(true);
@@ -52,7 +60,7 @@ function usePatchesDoc(docId, options = {}) {
52
60
  });
53
61
  onCleanup(() => {
54
62
  const id = docIdAccessor();
55
- manager.closeDoc(patches, id);
63
+ manager.closeDoc(patches, id, shouldUntrack);
56
64
  });
57
65
  } else {
58
66
  createEffect(() => {
@@ -87,6 +95,104 @@ function usePatchesDoc(docId, options = {}) {
87
95
  doc
88
96
  };
89
97
  }
98
+ function _usePatchesDocLazy(options) {
99
+ const { patches } = usePatchesContext();
100
+ const { idProp } = options;
101
+ let currentDoc = null;
102
+ let unsubscribe = null;
103
+ const [path, setPath] = createSignal(null);
104
+ const [doc, setDoc] = createSignal(void 0);
105
+ const [data, setData] = createSignal(void 0);
106
+ const [loading, setLoading] = createSignal(false);
107
+ const [error, setError] = createSignal(null);
108
+ const [rev, setRev] = createSignal(0);
109
+ const [hasPending, setHasPending] = createSignal(false);
110
+ function setupDoc(patchesDoc) {
111
+ currentDoc = patchesDoc;
112
+ setDoc(patchesDoc);
113
+ unsubscribe = patchesDoc.subscribe((state) => {
114
+ if (state && idProp && currentDoc) {
115
+ state = { ...state, [idProp]: currentDoc.id };
116
+ }
117
+ setData(() => state);
118
+ setRev(patchesDoc.committedRev);
119
+ setHasPending(patchesDoc.hasPending);
120
+ });
121
+ const unsubSync = patchesDoc.onSyncing((syncState) => {
122
+ setLoading(syncState === "initial" || syncState === "updating");
123
+ setError(syncState instanceof Error ? syncState : null);
124
+ });
125
+ const origUnsub = unsubscribe;
126
+ unsubscribe = () => {
127
+ origUnsub();
128
+ unsubSync();
129
+ };
130
+ setLoading(patchesDoc.syncing !== null);
131
+ }
132
+ function teardown() {
133
+ unsubscribe?.();
134
+ unsubscribe = null;
135
+ currentDoc = null;
136
+ setDoc(void 0);
137
+ setData(void 0);
138
+ setLoading(false);
139
+ setError(null);
140
+ setRev(0);
141
+ setHasPending(false);
142
+ }
143
+ async function load(docPath) {
144
+ if (path()) {
145
+ const prevPath = path();
146
+ teardown();
147
+ await patches.closeDoc(prevPath);
148
+ }
149
+ setPath(docPath);
150
+ try {
151
+ const patchesDoc = await patches.openDoc(docPath);
152
+ setupDoc(patchesDoc);
153
+ } catch (err) {
154
+ setError(err);
155
+ setLoading(false);
156
+ }
157
+ }
158
+ async function close() {
159
+ if (path()) {
160
+ const prevPath = path();
161
+ teardown();
162
+ setPath(null);
163
+ await patches.closeDoc(prevPath);
164
+ }
165
+ }
166
+ async function create(docPath, initialState) {
167
+ const newDoc = await patches.openDoc(docPath);
168
+ newDoc.change((patch, root) => {
169
+ if (initialState instanceof JSONPatch) {
170
+ patch.ops = initialState.ops;
171
+ } else {
172
+ const state = { ...initialState };
173
+ if (idProp) delete state[idProp];
174
+ patch.replace(root, state);
175
+ }
176
+ });
177
+ await patches.closeDoc(docPath);
178
+ }
179
+ function change(mutator) {
180
+ currentDoc?.change(mutator);
181
+ }
182
+ return {
183
+ data,
184
+ loading,
185
+ error,
186
+ rev,
187
+ hasPending,
188
+ change,
189
+ doc,
190
+ path,
191
+ load,
192
+ close,
193
+ create
194
+ };
195
+ }
90
196
  function usePatchesSync() {
91
197
  const { sync } = usePatchesContext();
92
198
  if (!sync) {
@@ -118,6 +224,7 @@ function createPatchesDoc(name) {
118
224
  const { patches } = usePatchesContext();
119
225
  const manager = getDocManager(patches);
120
226
  const autoClose = props.autoClose ?? false;
227
+ const shouldUntrack = autoClose === "untrack";
121
228
  const [doc, setDoc] = createSignal(void 0);
122
229
  const [data, setData] = createSignal(void 0);
123
230
  const [loading, setLoading] = createSignal(true);
@@ -158,13 +265,13 @@ function createPatchesDoc(name) {
158
265
  createEffect((prevId) => {
159
266
  const currentId = docIdAccessor();
160
267
  if (prevId && prevId !== currentId) {
161
- manager.closeDoc(patches, prevId);
268
+ manager.closeDoc(patches, prevId, shouldUntrack);
162
269
  }
163
270
  return currentId;
164
271
  });
165
272
  onCleanup(() => {
166
273
  const id = docIdAccessor();
167
- manager.closeDoc(patches, id);
274
+ manager.closeDoc(patches, id, shouldUntrack);
168
275
  });
169
276
  } else {
170
277
  createEffect((prevId) => {
@@ -0,0 +1 @@
1
+ export { areSetsEqual, fillPath } from '../shared/utils.js';
@@ -0,0 +1,6 @@
1
+ import "../chunk-IZ2YBCUP.js";
2
+ import { fillPath, areSetsEqual } from "../shared/utils.js";
3
+ export {
4
+ areSetsEqual,
5
+ fillPath
6
+ };
@@ -1,27 +1,38 @@
1
1
  import { ShallowRef, Ref, MaybeRef } from 'vue';
2
2
  import { a as PatchesDoc } from '../BaseDoc-DkP3tUhT.js';
3
+ import { JSONPatch } from '../json-patch/JSONPatch.js';
3
4
  import { ChangeMutator } from '../types.js';
4
5
  import '../event-signal.js';
5
6
  import '../json-patch/types.js';
6
- import '../json-patch/JSONPatch.js';
7
7
  import '@dabble/delta';
8
8
 
9
9
  /**
10
- * Options for usePatchesDoc composable.
10
+ * Options for usePatchesDoc composable (eager mode with docId).
11
11
  */
12
12
  interface UsePatchesDocOptions {
13
13
  /**
14
- * Whether to automatically open/close the document based on component lifecycle.
14
+ * Controls document lifecycle management on component unmount.
15
15
  *
16
- * - `false` (default): Assumes doc is already open. Throws if not.
17
- * - `true`: Opens doc on mount with ref counting, closes on unmount.
16
+ * - `false` (default): Explicit mode. Assumes doc is already open. Throws if not.
17
+ * - `true`: Opens doc on mount with ref counting, closes on unmount (doc stays tracked).
18
+ * - `'untrack'`: Opens doc on mount, closes AND untracks on unmount (removes from sync).
18
19
  *
19
20
  * @default false
20
21
  */
21
- autoClose?: boolean;
22
+ autoClose?: boolean | 'untrack';
22
23
  }
23
24
  /**
24
- * Return type for usePatchesDoc composable.
25
+ * Options for usePatchesDoc composable (lazy mode without docId).
26
+ */
27
+ interface UsePatchesDocLazyOptions {
28
+ /**
29
+ * Inject doc.id into state under this key on every state update.
30
+ * Useful when the document ID is derived from the path but needed in the data.
31
+ */
32
+ idProp?: string;
33
+ }
34
+ /**
35
+ * Return type for usePatchesDoc composable (eager mode).
25
36
  */
26
37
  interface UsePatchesDocReturn<T extends object> {
27
38
  /**
@@ -65,50 +76,71 @@ interface UsePatchesDocReturn<T extends object> {
65
76
  */
66
77
  doc: Ref<PatchesDoc<T> | undefined>;
67
78
  }
79
+ /**
80
+ * Return type for usePatchesDoc composable (lazy mode).
81
+ * Extends the eager return type with lifecycle management methods.
82
+ */
83
+ interface UsePatchesDocLazyReturn<T extends object> extends UsePatchesDocReturn<T> {
84
+ /**
85
+ * Current document path. `null` when no document is loaded.
86
+ */
87
+ path: Ref<string | null>;
88
+ /**
89
+ * Open a document by path. Closes any previously loaded document first.
90
+ *
91
+ * @param docPath - The document path to open
92
+ */
93
+ load: (docPath: string) => Promise<void>;
94
+ /**
95
+ * Close the current document, unsubscribe, and reset all state.
96
+ * Calls `patches.closeDoc()` but does not untrack — tracking is managed separately.
97
+ */
98
+ close: () => Promise<void>;
99
+ /**
100
+ * Create a new document: open it, set initial state, then close it.
101
+ * A one-shot operation that doesn't bind the document to this handle.
102
+ *
103
+ * @param docPath - The document path to create
104
+ * @param initialState - Initial state object or JSONPatch to apply
105
+ */
106
+ create: (docPath: string, initialState: T | JSONPatch) => Promise<void>;
107
+ }
68
108
  /**
69
109
  * Vue composable for reactive Patches document state.
70
110
  *
71
- * Provides reactive access to a Patches document with automatic lifecycle management.
72
- *
73
- * ## Explicit Lifecycle (default)
111
+ * ## Eager Mode (with docId)
74
112
  *
75
- * By default, assumes the document is already open and just adds Vue reactivity.
76
- * You control when documents are opened and closed.
113
+ * Provides reactive access to an already-open Patches document.
77
114
  *
78
115
  * @example
79
116
  * ```typescript
80
- * // Component.vue
81
- * const props = defineProps(['docId'])
82
- * const { patches } = usePatchesContext()
83
- *
84
- * // You control lifecycle
85
- * onMounted(() => patches.openDoc(props.docId))
86
- * onBeforeUnmount(() => patches.closeDoc(props.docId))
117
+ * // Explicit lifecycle — you control open/close
118
+ * const { data, loading, change } = usePatchesDoc('doc-123')
87
119
  *
88
- * // Just adds reactivity
89
- * const { data, loading, change } = usePatchesDoc(props.docId)
120
+ * // Auto lifecycle — opens on mount, closes on unmount
121
+ * const { data, loading, change } = usePatchesDoc('doc-123', { autoClose: true })
90
122
  * ```
91
123
  *
92
- * ## Auto Lifecycle
124
+ * ## Lazy Mode (without docId)
93
125
  *
94
- * With `autoClose: true`, the composable manages the document lifecycle with
95
- * reference counting. Safe to use in multiple components with the same docId.
126
+ * Returns a deferred handle with `load()`, `close()`, and `create()` methods.
127
+ * Ideal for Pinia stores where the document path isn't known at creation time.
128
+ * Does NOT use `onBeforeUnmount` — caller manages lifecycle.
96
129
  *
97
130
  * @example
98
131
  * ```typescript
99
- * // Opens on mount, closes on unmount (ref-counted)
100
- * const { data, loading, change } = usePatchesDoc('doc-123', {
101
- * autoClose: true
102
- * })
103
- * ```
132
+ * // In a Pinia store
133
+ * const { data, load, close, change, create } = usePatchesDoc<Project>()
104
134
  *
105
- * @param docId - Document ID to open
106
- * @param options - Configuration options
107
- * @returns Reactive document state and utilities
108
- * @throws Error if Patches context not provided
109
- * @throws Error if doc not open in explicit mode
135
+ * // Later, when the user navigates:
136
+ * await load('projects/abc/content')
137
+ *
138
+ * // When leaving:
139
+ * await close()
140
+ * ```
110
141
  */
111
142
  declare function usePatchesDoc<T extends object>(docId: string, options?: UsePatchesDocOptions): UsePatchesDocReturn<T>;
143
+ declare function usePatchesDoc<T extends object>(options?: UsePatchesDocLazyOptions): UsePatchesDocLazyReturn<T>;
112
144
  /**
113
145
  * Return type for usePatchesSync composable.
114
146
  */
@@ -203,4 +235,4 @@ declare function providePatchesDoc<T extends object>(name: string, docId: MaybeR
203
235
  */
204
236
  declare function useCurrentDoc<T extends object>(name: string): UsePatchesDocReturn<T>;
205
237
 
206
- export { type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, providePatchesDoc, useCurrentDoc, usePatchesDoc, usePatchesSync };
238
+ export { type UsePatchesDocLazyOptions, type UsePatchesDocLazyReturn, type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, providePatchesDoc, useCurrentDoc, usePatchesDoc, usePatchesSync };
@@ -8,11 +8,19 @@ import {
8
8
  provide,
9
9
  inject
10
10
  } from "vue";
11
+ import { JSONPatch } from "../json-patch/JSONPatch.js";
11
12
  import { usePatchesContext } from "./provider.js";
12
13
  import { getDocManager } from "./doc-manager.js";
13
- function usePatchesDoc(docId, options = {}) {
14
+ function usePatchesDoc(docIdOrOptions, options) {
15
+ if (typeof docIdOrOptions === "string") {
16
+ return _usePatchesDocEager(docIdOrOptions, options ?? {});
17
+ }
18
+ return _usePatchesDocLazy(docIdOrOptions ?? {});
19
+ }
20
+ function _usePatchesDocEager(docId, options) {
14
21
  const { patches } = usePatchesContext();
15
22
  const { autoClose = false } = options;
23
+ const shouldUntrack = autoClose === "untrack";
16
24
  const doc = ref(void 0);
17
25
  const data = shallowRef(void 0);
18
26
  const loading = ref(true);
@@ -45,7 +53,7 @@ function usePatchesDoc(docId, options = {}) {
45
53
  });
46
54
  onBeforeUnmount(() => {
47
55
  unsubscribers.forEach((unsub) => unsub());
48
- manager.closeDoc(patches, docId);
56
+ manager.closeDoc(patches, docId, shouldUntrack);
49
57
  });
50
58
  } else {
51
59
  const patchesDoc = patches.getOpenDoc(docId);
@@ -77,6 +85,104 @@ function usePatchesDoc(docId, options = {}) {
77
85
  doc
78
86
  };
79
87
  }
88
+ function _usePatchesDocLazy(options) {
89
+ const { patches } = usePatchesContext();
90
+ const { idProp } = options;
91
+ let currentDoc = null;
92
+ let unsubscribe = null;
93
+ const path = ref(null);
94
+ const doc = ref(void 0);
95
+ const data = shallowRef(void 0);
96
+ const loading = ref(false);
97
+ const error = ref(null);
98
+ const rev = ref(0);
99
+ const hasPending = ref(false);
100
+ function setupDoc(patchesDoc) {
101
+ currentDoc = patchesDoc;
102
+ doc.value = patchesDoc;
103
+ unsubscribe = patchesDoc.subscribe((state) => {
104
+ if (state && idProp && currentDoc) {
105
+ state = { ...state, [idProp]: currentDoc.id };
106
+ }
107
+ data.value = state;
108
+ rev.value = patchesDoc.committedRev;
109
+ hasPending.value = patchesDoc.hasPending;
110
+ });
111
+ const unsubSync = patchesDoc.onSyncing((syncState) => {
112
+ loading.value = syncState === "initial" || syncState === "updating";
113
+ error.value = syncState instanceof Error ? syncState : null;
114
+ });
115
+ const origUnsub = unsubscribe;
116
+ unsubscribe = () => {
117
+ origUnsub();
118
+ unsubSync();
119
+ };
120
+ loading.value = patchesDoc.syncing !== null;
121
+ }
122
+ function teardown() {
123
+ unsubscribe?.();
124
+ unsubscribe = null;
125
+ currentDoc = null;
126
+ doc.value = void 0;
127
+ data.value = void 0;
128
+ loading.value = false;
129
+ error.value = null;
130
+ rev.value = 0;
131
+ hasPending.value = false;
132
+ }
133
+ async function load(docPath) {
134
+ if (path.value) {
135
+ const prevPath = path.value;
136
+ teardown();
137
+ await patches.closeDoc(prevPath);
138
+ }
139
+ path.value = docPath;
140
+ try {
141
+ const patchesDoc = await patches.openDoc(docPath);
142
+ setupDoc(patchesDoc);
143
+ } catch (err) {
144
+ error.value = err;
145
+ loading.value = false;
146
+ }
147
+ }
148
+ async function close() {
149
+ if (path.value) {
150
+ const prevPath = path.value;
151
+ teardown();
152
+ path.value = null;
153
+ await patches.closeDoc(prevPath);
154
+ }
155
+ }
156
+ async function create(docPath, initialState) {
157
+ const newDoc = await patches.openDoc(docPath);
158
+ newDoc.change((patch, root) => {
159
+ if (initialState instanceof JSONPatch) {
160
+ patch.ops = initialState.ops;
161
+ } else {
162
+ const state = { ...initialState };
163
+ if (idProp) delete state[idProp];
164
+ patch.replace(root, state);
165
+ }
166
+ });
167
+ await patches.closeDoc(docPath);
168
+ }
169
+ function change(mutator) {
170
+ currentDoc?.change(mutator);
171
+ }
172
+ return {
173
+ data,
174
+ loading,
175
+ error,
176
+ rev,
177
+ hasPending,
178
+ change,
179
+ doc,
180
+ path,
181
+ load,
182
+ close,
183
+ create
184
+ };
185
+ }
80
186
  function usePatchesSync() {
81
187
  const { sync } = usePatchesContext();
82
188
  if (!sync) {
@@ -105,6 +211,7 @@ function createDocInjectionKey(name) {
105
211
  function providePatchesDoc(name, docId, options = {}) {
106
212
  const { patches } = usePatchesContext();
107
213
  const { autoClose = false } = options;
214
+ const shouldUntrack = autoClose === "untrack";
108
215
  const manager = getDocManager(patches);
109
216
  const doc = ref(void 0);
110
217
  const data = shallowRef(void 0);
@@ -181,7 +288,7 @@ function providePatchesDoc(name, docId, options = {}) {
181
288
  unsubscribers.forEach((unsub) => unsub());
182
289
  unsubscribers.length = 0;
183
290
  if (autoClose) {
184
- await manager.closeDoc(patches, oldDocId);
291
+ await manager.closeDoc(patches, oldDocId, shouldUntrack);
185
292
  } else {
186
293
  manager.decrementRefCount(oldDocId);
187
294
  }
@@ -191,7 +298,7 @@ function providePatchesDoc(name, docId, options = {}) {
191
298
  onBeforeUnmount(async () => {
192
299
  unsubscribers.forEach((unsub) => unsub());
193
300
  if (autoClose) {
194
- await manager.closeDoc(patches, currentDocId.value);
301
+ await manager.closeDoc(patches, currentDocId.value, shouldUntrack);
195
302
  } else {
196
303
  manager.decrementRefCount(currentDocId.value);
197
304
  }