@dabble/patches 0.8.9 → 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.
- package/dist/solid/index.d.ts +1 -1
- package/dist/solid/primitives.d.ts +30 -155
- package/dist/solid/primitives.js +53 -219
- package/dist/vue/composables.d.ts +29 -170
- package/dist/vue/composables.js +59 -200
- package/dist/vue/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/solid/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { PatchesContextValue, PatchesProvider, PatchesProviderProps, usePatchesContext } from './context.js';
|
|
2
|
-
export { MaybeAccessor, PatchesDocProviderProps,
|
|
2
|
+
export { MaybeAccessor, PatchesDocProviderProps, UsePatchesDocOptions, UsePatchesDocReturn, UsePatchesSyncReturn, createPatchesDoc, usePatchesDoc, usePatchesSync } from './primitives.js';
|
|
3
3
|
export { CreateManagedDocsOptions, CreateManagedDocsReturn, createManagedDocs } from './managed-docs.js';
|
|
4
4
|
export { fillPath } from '../shared/utils.js';
|
|
5
5
|
export { DocManager, getDocManager } from '../shared/doc-manager.js';
|
|
@@ -1,172 +1,78 @@
|
|
|
1
1
|
import { Accessor } from 'solid-js';
|
|
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 primitive
|
|
13
|
+
* Options for usePatchesDoc primitive.
|
|
14
14
|
*/
|
|
15
15
|
interface UsePatchesDocOptions extends OpenDocOptions {
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
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 cleanup (doc stays tracked).
|
|
21
|
-
* - `'untrack'`: Opens doc on mount, closes AND untracks on cleanup (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
|
-
|
|
20
|
+
untrack?: boolean;
|
|
26
21
|
}
|
|
27
22
|
/**
|
|
28
|
-
*
|
|
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 primitive (eager mode).
|
|
23
|
+
* Return type for usePatchesDoc primitive.
|
|
39
24
|
*/
|
|
40
25
|
interface UsePatchesDocReturn<T extends object> {
|
|
41
|
-
/**
|
|
42
|
-
* Accessor for the document state.
|
|
43
|
-
* Updated whenever the document changes (local or remote).
|
|
44
|
-
*/
|
|
26
|
+
/** Accessor for the document state. */
|
|
45
27
|
data: Accessor<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: Accessor<boolean>;
|
|
52
|
-
/**
|
|
53
|
-
* Error that occurred during sync, if any.
|
|
54
|
-
*/
|
|
30
|
+
/** Error that occurred during sync, if any. */
|
|
55
31
|
error: Accessor<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: Accessor<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: Accessor<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
|
-
|
|
78
|
-
|
|
79
|
-
*/
|
|
38
|
+
/** Close the document and reset state. Useful for explicit cleanup. */
|
|
39
|
+
close: () => Promise<void>;
|
|
40
|
+
/** The underlying PatchesDoc instance. */
|
|
80
41
|
doc: Accessor<PatchesDoc<T> | undefined>;
|
|
81
42
|
}
|
|
82
43
|
/**
|
|
83
|
-
*
|
|
84
|
-
* Extends the eager return type with lifecycle management methods.
|
|
44
|
+
* Type for document ID — can be a static string or accessor function.
|
|
85
45
|
*/
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Current document path. `null` when no document is loaded.
|
|
89
|
-
*/
|
|
90
|
-
path: Accessor<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
|
-
*/
|
|
102
|
-
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>;
|
|
112
|
-
}
|
|
46
|
+
type MaybeAccessor<T> = T | Accessor<T>;
|
|
113
47
|
/**
|
|
114
48
|
* Solid primitive for reactive Patches document state.
|
|
115
49
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
50
|
+
* Opens the document automatically and closes it on cleanup (or when the
|
|
51
|
+
* accessor value changes). Accepts a static string or an accessor. When the
|
|
52
|
+
* value is falsy, no document is loaded.
|
|
119
53
|
*
|
|
120
54
|
* @example
|
|
121
55
|
* ```tsx
|
|
122
|
-
* //
|
|
123
|
-
* const { data,
|
|
56
|
+
* // Static
|
|
57
|
+
* const { data, change } = usePatchesDoc('doc-123')
|
|
124
58
|
*
|
|
125
|
-
* //
|
|
126
|
-
* const
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* ## Lazy Mode (without docId)
|
|
130
|
-
*
|
|
131
|
-
* Returns a deferred handle with `load()`, `close()`, and `create()` methods.
|
|
132
|
-
* Does NOT register `onCleanup` — caller manages lifecycle.
|
|
133
|
-
*
|
|
134
|
-
* @example
|
|
135
|
-
* ```tsx
|
|
136
|
-
* const { data, load, close, change, create } = usePatchesDoc<Project>()
|
|
137
|
-
*
|
|
138
|
-
* // Later, when the user navigates:
|
|
139
|
-
* await load('projects/abc/content')
|
|
140
|
-
*
|
|
141
|
-
* // When leaving:
|
|
142
|
-
* await close()
|
|
59
|
+
* // Reactive — swaps automatically
|
|
60
|
+
* const [projectId, setProjectId] = createSignal<string | null>('abc')
|
|
61
|
+
* const { data, change } = usePatchesDoc(() => projectId() && `projects/${projectId()}`)
|
|
143
62
|
* ```
|
|
144
63
|
*/
|
|
145
|
-
declare function usePatchesDoc<T extends object>(docId: MaybeAccessor<string>, options?: UsePatchesDocOptions): UsePatchesDocReturn<T>;
|
|
146
|
-
declare function usePatchesDoc<T extends object>(options?: UsePatchesDocLazyOptions): UsePatchesDocLazyReturn<T>;
|
|
64
|
+
declare function usePatchesDoc<T extends object>(docId: MaybeAccessor<string | null | undefined | false>, options?: UsePatchesDocOptions): UsePatchesDocReturn<T>;
|
|
147
65
|
/**
|
|
148
66
|
* Return type for usePatchesSync primitive.
|
|
149
67
|
*/
|
|
150
68
|
interface UsePatchesSyncReturn {
|
|
151
|
-
/**
|
|
152
|
-
* Whether the WebSocket connection is established.
|
|
153
|
-
*/
|
|
154
69
|
connected: Accessor<boolean>;
|
|
155
|
-
/**
|
|
156
|
-
* Whether documents are currently syncing with the server.
|
|
157
|
-
*/
|
|
158
70
|
syncing: Accessor<boolean>;
|
|
159
|
-
/**
|
|
160
|
-
* Whether the client believes it has network connectivity.
|
|
161
|
-
*/
|
|
162
71
|
online: Accessor<boolean>;
|
|
163
72
|
}
|
|
164
73
|
/**
|
|
165
74
|
* Solid primitive for reactive Patches sync state.
|
|
166
75
|
*
|
|
167
|
-
* Provides reactive access to PatchesSync connection and sync status.
|
|
168
|
-
* Useful for showing "Offline" banners, global loading indicators, etc.
|
|
169
|
-
*
|
|
170
76
|
* @example
|
|
171
77
|
* ```tsx
|
|
172
78
|
* const { connected, syncing, online } = usePatchesSync();
|
|
@@ -177,68 +83,37 @@ interface UsePatchesSyncReturn {
|
|
|
177
83
|
* </Show>
|
|
178
84
|
* );
|
|
179
85
|
* ```
|
|
180
|
-
*
|
|
181
|
-
* @returns Reactive sync state
|
|
182
|
-
* @throws Error if Patches context not provided
|
|
183
|
-
* @throws Error if PatchesSync was not provided to context
|
|
184
86
|
*/
|
|
185
87
|
declare function usePatchesSync(): UsePatchesSyncReturn;
|
|
186
|
-
/**
|
|
187
|
-
* Type for document ID - can be static string or accessor function.
|
|
188
|
-
*/
|
|
189
|
-
type MaybeAccessor<T> = T | Accessor<T>;
|
|
190
88
|
/**
|
|
191
89
|
* Props for the Provider component returned by createPatchesDoc.
|
|
192
90
|
*/
|
|
193
91
|
interface PatchesDocProviderProps extends OpenDocOptions {
|
|
194
92
|
docId: MaybeAccessor<string>;
|
|
195
|
-
|
|
93
|
+
untrack?: boolean;
|
|
196
94
|
children: any;
|
|
197
95
|
}
|
|
198
96
|
/**
|
|
199
97
|
* Creates a named document context that can be provided to child components.
|
|
200
98
|
*
|
|
201
|
-
*
|
|
202
|
-
* without needing to pass the docId down through props. Supports both static and
|
|
203
|
-
* reactive docIds.
|
|
204
|
-
*
|
|
205
|
-
* ## Use Cases
|
|
206
|
-
*
|
|
207
|
-
* **Static document (user settings):**
|
|
99
|
+
* @example
|
|
208
100
|
* ```tsx
|
|
209
101
|
* const { Provider, useDoc } = createPatchesDoc<User>('user');
|
|
210
102
|
*
|
|
211
103
|
* <Provider docId="user-123">
|
|
212
104
|
* <UserProfile />
|
|
213
105
|
* </Provider>
|
|
214
|
-
* ```
|
|
215
106
|
*
|
|
216
|
-
*
|
|
217
|
-
* ```tsx
|
|
218
|
-
* const { Provider, useDoc } = createPatchesDoc<Whiteboard>('whiteboard');
|
|
107
|
+
* // Reactive
|
|
219
108
|
* const [activeTabId, setActiveTabId] = createSignal('design-1');
|
|
220
|
-
*
|
|
221
109
|
* <Provider docId={activeTabId}>
|
|
222
110
|
* <WhiteboardCanvas />
|
|
223
111
|
* </Provider>
|
|
224
112
|
* ```
|
|
225
|
-
*
|
|
226
|
-
* **With autoClose:**
|
|
227
|
-
* ```tsx
|
|
228
|
-
* const { Provider, useDoc } = createPatchesDoc<Doc>('document');
|
|
229
|
-
* const [currentDocId, setCurrentDocId] = createSignal('doc-1');
|
|
230
|
-
*
|
|
231
|
-
* <Provider docId={currentDocId} autoClose>
|
|
232
|
-
* <DocumentEditor />
|
|
233
|
-
* </Provider>
|
|
234
|
-
* ```
|
|
235
|
-
*
|
|
236
|
-
* @param name - Unique identifier for this document context (e.g., 'whiteboard', 'user')
|
|
237
|
-
* @returns Object with Provider component and useDoc hook
|
|
238
113
|
*/
|
|
239
114
|
declare function createPatchesDoc<T extends object>(name: string): {
|
|
240
115
|
Provider: (props: PatchesDocProviderProps) => any;
|
|
241
116
|
useDoc: () => UsePatchesDocReturn<T>;
|
|
242
117
|
};
|
|
243
118
|
|
|
244
|
-
export { type MaybeAccessor, type PatchesDocProviderProps, type
|
|
119
|
+
export { type MaybeAccessor, type PatchesDocProviderProps, type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, createPatchesDoc, usePatchesDoc, usePatchesSync };
|
package/dist/solid/primitives.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
60
|
+
const baseReturn = { data, loading, error, rev, hasPending, change, doc };
|
|
61
|
+
return { setupDoc, resetSignals, setError, baseReturn };
|
|
106
62
|
}
|
|
107
|
-
function
|
|
63
|
+
function usePatchesDoc(docId, options) {
|
|
108
64
|
const { patches, sync } = usePatchesContext();
|
|
109
|
-
const {
|
|
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,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
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
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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,
|
|
@@ -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
|
|
13
|
+
* Options for usePatchesDoc composable.
|
|
14
14
|
*/
|
|
15
15
|
interface UsePatchesDocOptions extends OpenDocOptions {
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
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
|
-
|
|
20
|
+
untrack?: boolean;
|
|
26
21
|
}
|
|
27
22
|
/**
|
|
28
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
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
|
-
* //
|
|
138
|
-
* const { data,
|
|
52
|
+
* // Static
|
|
53
|
+
* const { data, change } = usePatchesDoc('doc-123')
|
|
139
54
|
*
|
|
140
|
-
* //
|
|
141
|
-
*
|
|
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?:
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
|
102
|
+
export { type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, providePatchesDoc, useCurrentDoc, usePatchesDoc, usePatchesSync };
|
package/dist/vue/composables.js
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import "../chunk-IZ2YBCUP.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
shallowRef,
|
|
3
|
+
inject,
|
|
5
4
|
onBeforeUnmount,
|
|
6
|
-
watch,
|
|
7
|
-
unref,
|
|
8
5
|
provide,
|
|
9
|
-
|
|
6
|
+
ref,
|
|
7
|
+
shallowRef,
|
|
8
|
+
toValue,
|
|
9
|
+
watch
|
|
10
10
|
} from "vue";
|
|
11
|
-
import { JSONPatch } from "../json-patch/JSONPatch.js";
|
|
12
|
-
import { usePatchesContext } from "./provider.js";
|
|
13
11
|
import { getDocManager } from "./doc-manager.js";
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
import { usePatchesContext } from "./provider.js";
|
|
13
|
+
function createDocReactiveState(hasSyncContext) {
|
|
16
14
|
const doc = shallowRef(void 0);
|
|
17
15
|
const data = shallowRef(void 0);
|
|
18
|
-
const loading = ref(
|
|
16
|
+
const loading = ref(false);
|
|
19
17
|
const error = ref();
|
|
20
18
|
const rev = ref(0);
|
|
21
19
|
const hasPending = ref(false);
|
|
@@ -35,9 +33,6 @@ function createDocReactiveState(options) {
|
|
|
35
33
|
}
|
|
36
34
|
}
|
|
37
35
|
const unsubState = patchesDoc.subscribe((state) => {
|
|
38
|
-
if (transformState && state) {
|
|
39
|
-
state = transformState(state, patchesDoc);
|
|
40
|
-
}
|
|
41
36
|
data.value = state;
|
|
42
37
|
rev.value = patchesDoc.committedRev;
|
|
43
38
|
hasPending.value = patchesDoc.hasPending;
|
|
@@ -61,133 +56,70 @@ function createDocReactiveState(options) {
|
|
|
61
56
|
hasPending.value = false;
|
|
62
57
|
}
|
|
63
58
|
function change(mutator) {
|
|
64
|
-
|
|
65
|
-
if (!doc.value) {
|
|
66
|
-
throw new Error("Cannot make changes: document not loaded yet");
|
|
67
|
-
}
|
|
68
|
-
doc.value.change(mutator);
|
|
69
|
-
} else {
|
|
70
|
-
doc.value?.change(mutator);
|
|
71
|
-
}
|
|
59
|
+
doc.value?.change(mutator);
|
|
72
60
|
}
|
|
73
|
-
const baseReturn = {
|
|
74
|
-
|
|
75
|
-
loading,
|
|
76
|
-
error,
|
|
77
|
-
rev,
|
|
78
|
-
hasPending,
|
|
79
|
-
change,
|
|
80
|
-
doc
|
|
81
|
-
};
|
|
82
|
-
return { doc, data, loading, error, rev, hasPending, setupDoc, resetRefs, change, baseReturn };
|
|
83
|
-
}
|
|
84
|
-
function usePatchesDoc(docIdOrOptions, options) {
|
|
85
|
-
if (typeof docIdOrOptions === "string") {
|
|
86
|
-
return _usePatchesDocEager(docIdOrOptions, options ?? {});
|
|
87
|
-
}
|
|
88
|
-
return _usePatchesDocLazy(docIdOrOptions ?? {});
|
|
61
|
+
const baseReturn = { data, loading, error, rev, hasPending, change, doc };
|
|
62
|
+
return { setupDoc, resetRefs, baseReturn };
|
|
89
63
|
}
|
|
90
|
-
function
|
|
64
|
+
function usePatchesDoc(docId, options) {
|
|
91
65
|
const { patches, sync } = usePatchesContext();
|
|
92
|
-
const {
|
|
93
|
-
const shouldUntrack = autoClose === "untrack";
|
|
66
|
+
const { untrack: shouldUntrack = false, algorithm, metadata } = options ?? {};
|
|
94
67
|
const openDocOpts = { algorithm, metadata };
|
|
95
68
|
const manager = getDocManager(patches);
|
|
96
|
-
const { setupDoc, baseReturn } = createDocReactiveState(
|
|
97
|
-
initialLoading: !!autoClose,
|
|
98
|
-
hasSyncContext: !!sync,
|
|
99
|
-
changeBehavior: "throw"
|
|
100
|
-
});
|
|
69
|
+
const { setupDoc, resetRefs, baseReturn } = createDocReactiveState(!!sync);
|
|
101
70
|
let unsubscribe = null;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
71
|
+
let currentDocId = null;
|
|
72
|
+
let unmounted = false;
|
|
73
|
+
function teardown() {
|
|
74
|
+
unsubscribe?.();
|
|
75
|
+
unsubscribe = null;
|
|
76
|
+
}
|
|
77
|
+
async function openPath(id) {
|
|
78
|
+
currentDocId = id;
|
|
79
|
+
baseReturn.loading.value = true;
|
|
80
|
+
try {
|
|
81
|
+
const patchesDoc = await manager.openDoc(patches, id, openDocOpts);
|
|
82
|
+
if (unmounted || currentDocId !== id) {
|
|
83
|
+
manager.closeDoc(patches, id, shouldUntrack);
|
|
107
84
|
return;
|
|
108
85
|
}
|
|
109
86
|
unsubscribe = setupDoc(patchesDoc);
|
|
110
|
-
}
|
|
111
|
-
if (!unmounted) {
|
|
87
|
+
} catch (err) {
|
|
88
|
+
if (!unmounted && currentDocId === id) {
|
|
112
89
|
baseReturn.error.value = err;
|
|
113
90
|
baseReturn.loading.value = false;
|
|
114
91
|
}
|
|
115
|
-
});
|
|
116
|
-
onBeforeUnmount(() => {
|
|
117
|
-
unmounted = true;
|
|
118
|
-
unsubscribe?.();
|
|
119
|
-
manager.closeDoc(patches, docId, shouldUntrack);
|
|
120
|
-
});
|
|
121
|
-
} else {
|
|
122
|
-
const patchesDoc = patches.getOpenDoc(docId);
|
|
123
|
-
if (!patchesDoc) {
|
|
124
|
-
throw new Error(
|
|
125
|
-
`Document "${docId}" is not open. Either open it with patches.openDoc() first, or use { autoClose: true } option.`
|
|
126
|
-
);
|
|
127
92
|
}
|
|
128
|
-
manager.incrementRefCount(docId);
|
|
129
|
-
unsubscribe = setupDoc(patchesDoc);
|
|
130
|
-
onBeforeUnmount(() => {
|
|
131
|
-
unsubscribe?.();
|
|
132
|
-
manager.decrementRefCount(docId);
|
|
133
|
-
});
|
|
134
93
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
function _usePatchesDocLazy(options) {
|
|
138
|
-
const { patches, sync } = usePatchesContext();
|
|
139
|
-
const { idProp } = options;
|
|
140
|
-
const { setupDoc, resetRefs, loading, baseReturn } = createDocReactiveState({
|
|
141
|
-
initialLoading: false,
|
|
142
|
-
hasSyncContext: !!sync,
|
|
143
|
-
changeBehavior: "noop",
|
|
144
|
-
transformState: idProp ? (state, patchesDoc) => ({ ...state, [idProp]: patchesDoc.id }) : void 0
|
|
145
|
-
});
|
|
146
|
-
let unsubscribe = null;
|
|
147
|
-
const path = ref(null);
|
|
148
|
-
function teardown() {
|
|
149
|
-
unsubscribe?.();
|
|
150
|
-
unsubscribe = null;
|
|
94
|
+
async function closePath(id) {
|
|
95
|
+
teardown();
|
|
151
96
|
resetRefs();
|
|
97
|
+
await manager.closeDoc(patches, id, shouldUntrack);
|
|
152
98
|
}
|
|
153
|
-
async
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
await patches.closeDoc(prevPath);
|
|
99
|
+
baseReturn.close = async () => {
|
|
100
|
+
if (currentDocId) {
|
|
101
|
+
await closePath(currentDocId);
|
|
102
|
+
currentDocId = null;
|
|
158
103
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
104
|
+
};
|
|
105
|
+
const getter = typeof docId === "string" ? () => docId : () => toValue(docId) || null;
|
|
106
|
+
watch(
|
|
107
|
+
getter,
|
|
108
|
+
async (newId, oldId) => {
|
|
109
|
+
if (newId === oldId) return;
|
|
110
|
+
if (oldId) await closePath(oldId);
|
|
111
|
+
if (newId) await openPath(newId);
|
|
112
|
+
},
|
|
113
|
+
{ immediate: true }
|
|
114
|
+
);
|
|
115
|
+
onBeforeUnmount(async () => {
|
|
116
|
+
unmounted = true;
|
|
117
|
+
if (currentDocId) {
|
|
118
|
+
await closePath(currentDocId);
|
|
119
|
+
currentDocId = null;
|
|
175
120
|
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const newDoc = await patches.openDoc(docPath, options2);
|
|
179
|
-
newDoc.change((patch, root) => {
|
|
180
|
-
if (initialState instanceof JSONPatch) {
|
|
181
|
-
patch.ops = initialState.ops;
|
|
182
|
-
} else {
|
|
183
|
-
const state = { ...initialState };
|
|
184
|
-
if (idProp) delete state[idProp];
|
|
185
|
-
patch.replace(root, state);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
await patches.closeDoc(docPath);
|
|
189
|
-
}
|
|
190
|
-
return { ...baseReturn, path, load, close, create };
|
|
121
|
+
});
|
|
122
|
+
return baseReturn;
|
|
191
123
|
}
|
|
192
124
|
function usePatchesSync() {
|
|
193
125
|
const { sync } = usePatchesContext();
|
|
@@ -205,89 +137,16 @@ function usePatchesSync() {
|
|
|
205
137
|
onBeforeUnmount(() => {
|
|
206
138
|
unsubscribe();
|
|
207
139
|
});
|
|
208
|
-
return {
|
|
209
|
-
connected,
|
|
210
|
-
syncing,
|
|
211
|
-
online
|
|
212
|
-
};
|
|
140
|
+
return { connected, syncing, online };
|
|
213
141
|
}
|
|
214
142
|
function createDocInjectionKey(name) {
|
|
215
143
|
return Symbol(`patches-doc-${name}`);
|
|
216
144
|
}
|
|
217
|
-
function providePatchesDoc(name, docId, options
|
|
218
|
-
const { patches, sync } = usePatchesContext();
|
|
219
|
-
const { autoClose = false, algorithm, metadata } = options;
|
|
220
|
-
const shouldUntrack = autoClose === "untrack";
|
|
221
|
-
const openDocOpts = { algorithm, metadata };
|
|
222
|
-
const manager = getDocManager(patches);
|
|
223
|
-
const { setupDoc, baseReturn } = createDocReactiveState({
|
|
224
|
-
initialLoading: !!autoClose,
|
|
225
|
-
hasSyncContext: !!sync,
|
|
226
|
-
changeBehavior: "throw"
|
|
227
|
-
});
|
|
228
|
-
const currentDocId = ref(unref(docId));
|
|
229
|
-
let unsubscribe = null;
|
|
230
|
-
let providerUnmounted = false;
|
|
231
|
-
async function initDoc(id) {
|
|
232
|
-
currentDocId.value = id;
|
|
233
|
-
unsubscribe?.();
|
|
234
|
-
unsubscribe = null;
|
|
235
|
-
if (autoClose) {
|
|
236
|
-
try {
|
|
237
|
-
const patchesDoc = await manager.openDoc(patches, id, openDocOpts);
|
|
238
|
-
if (providerUnmounted || currentDocId.value !== id) {
|
|
239
|
-
manager.closeDoc(patches, id, shouldUntrack);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
unsubscribe = setupDoc(patchesDoc);
|
|
243
|
-
} catch (err) {
|
|
244
|
-
if (!providerUnmounted && currentDocId.value === id) {
|
|
245
|
-
baseReturn.error.value = err;
|
|
246
|
-
baseReturn.loading.value = false;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
} else {
|
|
250
|
-
try {
|
|
251
|
-
const patchesDoc = patches.getOpenDoc(id);
|
|
252
|
-
if (!patchesDoc) {
|
|
253
|
-
throw new Error(
|
|
254
|
-
`Document "${id}" is not open. Either open it with patches.openDoc() first, or use { autoClose: true } option.`
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
manager.incrementRefCount(id);
|
|
258
|
-
unsubscribe = setupDoc(patchesDoc);
|
|
259
|
-
} catch (err) {
|
|
260
|
-
baseReturn.error.value = err;
|
|
261
|
-
baseReturn.loading.value = false;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
145
|
+
function providePatchesDoc(name, docId, options) {
|
|
265
146
|
const key = createDocInjectionKey(name);
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
watch(docId, async (newDocId, oldDocId) => {
|
|
270
|
-
if (newDocId === oldDocId) return;
|
|
271
|
-
unsubscribe?.();
|
|
272
|
-
unsubscribe = null;
|
|
273
|
-
if (autoClose) {
|
|
274
|
-
await manager.closeDoc(patches, oldDocId, shouldUntrack);
|
|
275
|
-
} else {
|
|
276
|
-
manager.decrementRefCount(oldDocId);
|
|
277
|
-
}
|
|
278
|
-
await initDoc(newDocId);
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
onBeforeUnmount(async () => {
|
|
282
|
-
providerUnmounted = true;
|
|
283
|
-
unsubscribe?.();
|
|
284
|
-
if (autoClose) {
|
|
285
|
-
await manager.closeDoc(patches, currentDocId.value, shouldUntrack);
|
|
286
|
-
} else {
|
|
287
|
-
manager.decrementRefCount(currentDocId.value);
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
return baseReturn;
|
|
147
|
+
const result = typeof docId === "string" ? usePatchesDoc(docId, options) : usePatchesDoc(() => docId.value, options);
|
|
148
|
+
provide(key, result);
|
|
149
|
+
return result;
|
|
291
150
|
}
|
|
292
151
|
function useCurrentDoc(name) {
|
|
293
152
|
const key = createDocInjectionKey(name);
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { PATCHES_KEY, PATCHES_SYNC_KEY, PatchesContext, providePatches, providePatchesContext, usePatchesContext } from './provider.js';
|
|
2
|
-
export {
|
|
2
|
+
export { UsePatchesDocOptions, UsePatchesDocReturn, UsePatchesSyncReturn, providePatchesDoc, useCurrentDoc, usePatchesDoc, usePatchesSync } from './composables.js';
|
|
3
3
|
export { UseManagedDocsOptions, UseManagedDocsReturn, useManagedDocs } from './managed-docs.js';
|
|
4
4
|
export { fillPath } from '../shared/utils.js';
|
|
5
5
|
export { DocManager, getDocManager } from '../shared/doc-manager.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dabble/patches",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10",
|
|
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": {
|