@dabble/patches 0.5.6 → 0.5.7
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/algorithms/shared/changeBatching.js +4 -1
- package/dist/client/PatchesDoc.js +7 -1
- package/dist/net/PatchesSync.d.ts +6 -6
- package/dist/solid/context.d.ts +67 -0
- package/dist/solid/context.js +20 -0
- package/dist/solid/doc-manager.d.ts +88 -0
- package/dist/solid/doc-manager.js +125 -0
- package/dist/solid/index.d.ts +19 -0
- package/dist/solid/index.js +17 -0
- package/dist/solid/primitives.d.ts +235 -0
- package/dist/solid/primitives.js +226 -0
- package/dist/vue/composables.d.ts +207 -0
- package/dist/vue/composables.js +216 -0
- package/dist/vue/doc-manager.d.ts +88 -0
- package/dist/vue/doc-manager.js +125 -0
- package/dist/vue/index.d.ts +19 -0
- package/dist/vue/index.js +28 -0
- package/dist/vue/provider.d.ts +89 -0
- package/dist/vue/provider.js +27 -0
- package/package.json +29 -5
|
@@ -142,7 +142,10 @@ function breakTextOp(origChange, textOp, maxBytes, startRev, sizeCalculator) {
|
|
|
142
142
|
testBatchOps.push({ retain: retainToPrefixCurrentPiece });
|
|
143
143
|
}
|
|
144
144
|
testBatchOps.push(op);
|
|
145
|
-
const testBatchSize = getSizeForStorage(
|
|
145
|
+
const testBatchSize = getSizeForStorage(
|
|
146
|
+
{ ...origChange, ops: [{ ...textOp, value: testBatchOps }] },
|
|
147
|
+
sizeCalculator
|
|
148
|
+
);
|
|
146
149
|
if (currentOpsForNextChangePiece.length > 0 && testBatchSize > maxBytes) {
|
|
147
150
|
flushCurrentChangePiece();
|
|
148
151
|
}
|
|
@@ -88,7 +88,13 @@ class PatchesDoc {
|
|
|
88
88
|
* @returns The generated Change objects.
|
|
89
89
|
*/
|
|
90
90
|
change(mutator) {
|
|
91
|
-
const changes = makeChange(
|
|
91
|
+
const changes = makeChange(
|
|
92
|
+
this._snapshot,
|
|
93
|
+
mutator,
|
|
94
|
+
this._changeMetadata,
|
|
95
|
+
this._maxStorageBytes,
|
|
96
|
+
this._sizeCalculator
|
|
97
|
+
);
|
|
92
98
|
if (changes.length === 0) {
|
|
93
99
|
return changes;
|
|
94
100
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { Signal } from '../event-signal.js';
|
|
2
|
-
import { ConnectionState } from './protocol/types.js';
|
|
3
|
-
import { SyncingState, Change } from '../types.js';
|
|
4
1
|
import { JSONRPCClient } from './protocol/JSONRPCClient.js';
|
|
5
|
-
import {
|
|
6
|
-
import { WebSocketOptions } from './websocket/WebSocketTransport.js';
|
|
2
|
+
import { Signal } from '../event-signal.js';
|
|
7
3
|
import { SizeCalculator } from '../algorithms/shared/changeBatching.js';
|
|
8
4
|
import { Patches } from '../client/Patches.js';
|
|
9
5
|
import { PatchesStore } from '../client/PatchesStore.js';
|
|
6
|
+
import { SyncingState, Change } from '../types.js';
|
|
7
|
+
import { ConnectionState } from './protocol/types.js';
|
|
8
|
+
import { PatchesWebSocket } from './websocket/PatchesWebSocket.js';
|
|
9
|
+
import { WebSocketOptions } from './websocket/WebSocketTransport.js';
|
|
10
10
|
import '../json-patch/JSONPatch.js';
|
|
11
11
|
import '@dabble/delta';
|
|
12
12
|
import '../json-patch/types.js';
|
|
13
|
-
import './PatchesClient.js';
|
|
14
13
|
import '../client/PatchesDoc.js';
|
|
14
|
+
import './PatchesClient.js';
|
|
15
15
|
|
|
16
16
|
interface PatchesSyncState {
|
|
17
17
|
online: boolean;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
import { Patches } from '../client/Patches.js';
|
|
3
|
+
import { PatchesSync } from '../net/PatchesSync.js';
|
|
4
|
+
import '../event-signal.js';
|
|
5
|
+
import '../types.js';
|
|
6
|
+
import '../json-patch/JSONPatch.js';
|
|
7
|
+
import '@dabble/delta';
|
|
8
|
+
import '../json-patch/types.js';
|
|
9
|
+
import '../client/PatchesDoc.js';
|
|
10
|
+
import '../algorithms/shared/changeBatching.js';
|
|
11
|
+
import '../client/PatchesStore.js';
|
|
12
|
+
import '../net/protocol/JSONRPCClient.js';
|
|
13
|
+
import '../net/protocol/types.js';
|
|
14
|
+
import '../net/websocket/PatchesWebSocket.js';
|
|
15
|
+
import '../net/PatchesClient.js';
|
|
16
|
+
import '../net/websocket/WebSocketTransport.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Context value containing Patches and optional PatchesSync instances.
|
|
20
|
+
*/
|
|
21
|
+
interface PatchesContextValue {
|
|
22
|
+
patches: Patches;
|
|
23
|
+
sync?: PatchesSync;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Props for the PatchesProvider component.
|
|
27
|
+
*/
|
|
28
|
+
interface PatchesProviderProps {
|
|
29
|
+
patches: Patches;
|
|
30
|
+
sync?: PatchesSync;
|
|
31
|
+
children: JSX.Element;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Provider component for making Patches and PatchesSync available to child components.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* import { PatchesProvider } from '@dabble/patches/solid';
|
|
39
|
+
* import { Patches, InMemoryStore } from '@dabble/patches/client';
|
|
40
|
+
*
|
|
41
|
+
* const patches = new Patches({ store: new InMemoryStore() });
|
|
42
|
+
*
|
|
43
|
+
* <PatchesProvider patches={patches}>
|
|
44
|
+
* <App />
|
|
45
|
+
* </PatchesProvider>
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare function PatchesProvider(props: PatchesProviderProps): any;
|
|
49
|
+
/**
|
|
50
|
+
* Hook to access the Patches context.
|
|
51
|
+
*
|
|
52
|
+
* @throws Error if called outside of a PatchesProvider
|
|
53
|
+
* @returns PatchesContextValue containing patches and optional sync instances
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* import { usePatchesContext } from '@dabble/patches/solid';
|
|
58
|
+
*
|
|
59
|
+
* function MyComponent() {
|
|
60
|
+
* const { patches, sync } = usePatchesContext();
|
|
61
|
+
* // Use patches and sync...
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
declare function usePatchesContext(): PatchesContextValue;
|
|
66
|
+
|
|
67
|
+
export { type PatchesContextValue, PatchesProvider, type PatchesProviderProps, usePatchesContext };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import "../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { createContext, useContext } from "solid-js";
|
|
3
|
+
const PatchesContext = createContext();
|
|
4
|
+
function PatchesProvider(props) {
|
|
5
|
+
const value = { patches: props.patches, sync: props.sync };
|
|
6
|
+
return /* @__PURE__ */ React.createElement(PatchesContext.Provider, { value, children: props.children });
|
|
7
|
+
}
|
|
8
|
+
function usePatchesContext() {
|
|
9
|
+
const context = useContext(PatchesContext);
|
|
10
|
+
if (!context) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
"usePatchesContext must be called within a PatchesProvider. Make sure your component is wrapped with <PatchesProvider>."
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
PatchesProvider,
|
|
19
|
+
usePatchesContext
|
|
20
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Patches } from '../client/Patches.js';
|
|
2
|
+
import { PatchesDoc } from '../client/PatchesDoc.js';
|
|
3
|
+
import '../event-signal.js';
|
|
4
|
+
import '../types.js';
|
|
5
|
+
import '../json-patch/JSONPatch.js';
|
|
6
|
+
import '@dabble/delta';
|
|
7
|
+
import '../json-patch/types.js';
|
|
8
|
+
import '../client/PatchesStore.js';
|
|
9
|
+
import '../algorithms/shared/changeBatching.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Reference counting manager for PatchesDoc instances.
|
|
13
|
+
*
|
|
14
|
+
* Tracks how many Solid components are using each document and only opens/closes
|
|
15
|
+
* documents when the reference count goes to/from zero.
|
|
16
|
+
*
|
|
17
|
+
* This prevents the footgun where multiple components open the same doc but the
|
|
18
|
+
* first one to unmount closes it for everyone else.
|
|
19
|
+
*/
|
|
20
|
+
declare class DocManager {
|
|
21
|
+
private refCounts;
|
|
22
|
+
private pendingOps;
|
|
23
|
+
/**
|
|
24
|
+
* Opens a document with reference counting.
|
|
25
|
+
*
|
|
26
|
+
* - If this is the first reference, calls patches.openDoc()
|
|
27
|
+
* - If doc is already open, returns existing instance and increments count
|
|
28
|
+
* - Handles concurrent opens to the same doc safely
|
|
29
|
+
*
|
|
30
|
+
* @param patches - Patches instance
|
|
31
|
+
* @param docId - Document ID to open
|
|
32
|
+
* @returns Promise resolving to PatchesDoc instance
|
|
33
|
+
*/
|
|
34
|
+
openDoc<T extends object>(patches: Patches, docId: string): Promise<PatchesDoc<T>>;
|
|
35
|
+
/**
|
|
36
|
+
* Closes a document with reference counting.
|
|
37
|
+
*
|
|
38
|
+
* - Decrements the reference count
|
|
39
|
+
* - Only calls patches.closeDoc() when count reaches zero
|
|
40
|
+
* - Safe to call even if doc was never opened
|
|
41
|
+
*
|
|
42
|
+
* @param patches - Patches instance
|
|
43
|
+
* @param docId - Document ID to close
|
|
44
|
+
*/
|
|
45
|
+
closeDoc(patches: Patches, docId: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Increments the reference count for a document without opening it.
|
|
48
|
+
*
|
|
49
|
+
* Used in explicit mode to track usage and prevent premature closes
|
|
50
|
+
* from autoClose mode.
|
|
51
|
+
*
|
|
52
|
+
* @param docId - Document ID
|
|
53
|
+
*/
|
|
54
|
+
incrementRefCount(docId: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Decrements the reference count for a document without closing it.
|
|
57
|
+
*
|
|
58
|
+
* Used in explicit mode to release usage tracking.
|
|
59
|
+
*
|
|
60
|
+
* @param docId - Document ID
|
|
61
|
+
*/
|
|
62
|
+
decrementRefCount(docId: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Gets the current reference count for a document.
|
|
65
|
+
*
|
|
66
|
+
* Useful for debugging or advanced use cases.
|
|
67
|
+
*
|
|
68
|
+
* @param docId - Document ID
|
|
69
|
+
* @returns Current reference count (0 if not tracked)
|
|
70
|
+
*/
|
|
71
|
+
getRefCount(docId: string): number;
|
|
72
|
+
/**
|
|
73
|
+
* Clears all reference counts without closing documents.
|
|
74
|
+
*
|
|
75
|
+
* Use with caution - this is mainly for testing or cleanup scenarios
|
|
76
|
+
* where you want to reset the manager state.
|
|
77
|
+
*/
|
|
78
|
+
reset(): void;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Gets or creates a DocManager for a Patches instance.
|
|
82
|
+
*
|
|
83
|
+
* @param patches - Patches instance
|
|
84
|
+
* @returns DocManager for this Patches instance
|
|
85
|
+
*/
|
|
86
|
+
declare function getDocManager(patches: Patches): DocManager;
|
|
87
|
+
|
|
88
|
+
export { DocManager, getDocManager };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import "../chunk-IZ2YBCUP.js";
|
|
2
|
+
class DocManager {
|
|
3
|
+
refCounts = /* @__PURE__ */ new Map();
|
|
4
|
+
pendingOps = /* @__PURE__ */ new Map();
|
|
5
|
+
/**
|
|
6
|
+
* Opens a document with reference counting.
|
|
7
|
+
*
|
|
8
|
+
* - If this is the first reference, calls patches.openDoc()
|
|
9
|
+
* - If doc is already open, returns existing instance and increments count
|
|
10
|
+
* - Handles concurrent opens to the same doc safely
|
|
11
|
+
*
|
|
12
|
+
* @param patches - Patches instance
|
|
13
|
+
* @param docId - Document ID to open
|
|
14
|
+
* @returns Promise resolving to PatchesDoc instance
|
|
15
|
+
*/
|
|
16
|
+
async openDoc(patches, docId) {
|
|
17
|
+
const currentCount = this.refCounts.get(docId) || 0;
|
|
18
|
+
if (currentCount === 0 && this.pendingOps.has(docId)) {
|
|
19
|
+
const doc = await this.pendingOps.get(docId);
|
|
20
|
+
this.refCounts.set(docId, (this.refCounts.get(docId) || 0) + 1);
|
|
21
|
+
return doc;
|
|
22
|
+
}
|
|
23
|
+
if (currentCount > 0) {
|
|
24
|
+
this.refCounts.set(docId, currentCount + 1);
|
|
25
|
+
const doc = patches.getOpenDoc(docId);
|
|
26
|
+
if (!doc) {
|
|
27
|
+
throw new Error(`Document ${docId} has ref count ${currentCount} but is not open in Patches`);
|
|
28
|
+
}
|
|
29
|
+
return doc;
|
|
30
|
+
}
|
|
31
|
+
const openPromise = patches.openDoc(docId);
|
|
32
|
+
this.pendingOps.set(docId, openPromise);
|
|
33
|
+
try {
|
|
34
|
+
const doc = await openPromise;
|
|
35
|
+
this.refCounts.set(docId, 1);
|
|
36
|
+
return doc;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
this.refCounts.delete(docId);
|
|
39
|
+
throw error;
|
|
40
|
+
} finally {
|
|
41
|
+
this.pendingOps.delete(docId);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Closes a document with reference counting.
|
|
46
|
+
*
|
|
47
|
+
* - Decrements the reference count
|
|
48
|
+
* - Only calls patches.closeDoc() when count reaches zero
|
|
49
|
+
* - Safe to call even if doc was never opened
|
|
50
|
+
*
|
|
51
|
+
* @param patches - Patches instance
|
|
52
|
+
* @param docId - Document ID to close
|
|
53
|
+
*/
|
|
54
|
+
async closeDoc(patches, docId) {
|
|
55
|
+
const currentCount = this.refCounts.get(docId) || 0;
|
|
56
|
+
if (currentCount === 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (currentCount === 1) {
|
|
60
|
+
this.refCounts.delete(docId);
|
|
61
|
+
await patches.closeDoc(docId);
|
|
62
|
+
} else {
|
|
63
|
+
this.refCounts.set(docId, currentCount - 1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Increments the reference count for a document without opening it.
|
|
68
|
+
*
|
|
69
|
+
* Used in explicit mode to track usage and prevent premature closes
|
|
70
|
+
* from autoClose mode.
|
|
71
|
+
*
|
|
72
|
+
* @param docId - Document ID
|
|
73
|
+
*/
|
|
74
|
+
incrementRefCount(docId) {
|
|
75
|
+
const currentCount = this.refCounts.get(docId) || 0;
|
|
76
|
+
this.refCounts.set(docId, currentCount + 1);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Decrements the reference count for a document without closing it.
|
|
80
|
+
*
|
|
81
|
+
* Used in explicit mode to release usage tracking.
|
|
82
|
+
*
|
|
83
|
+
* @param docId - Document ID
|
|
84
|
+
*/
|
|
85
|
+
decrementRefCount(docId) {
|
|
86
|
+
const currentCount = this.refCounts.get(docId) || 0;
|
|
87
|
+
if (currentCount > 0) {
|
|
88
|
+
this.refCounts.set(docId, currentCount - 1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Gets the current reference count for a document.
|
|
93
|
+
*
|
|
94
|
+
* Useful for debugging or advanced use cases.
|
|
95
|
+
*
|
|
96
|
+
* @param docId - Document ID
|
|
97
|
+
* @returns Current reference count (0 if not tracked)
|
|
98
|
+
*/
|
|
99
|
+
getRefCount(docId) {
|
|
100
|
+
return this.refCounts.get(docId) || 0;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Clears all reference counts without closing documents.
|
|
104
|
+
*
|
|
105
|
+
* Use with caution - this is mainly for testing or cleanup scenarios
|
|
106
|
+
* where you want to reset the manager state.
|
|
107
|
+
*/
|
|
108
|
+
reset() {
|
|
109
|
+
this.refCounts.clear();
|
|
110
|
+
this.pendingOps.clear();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const managers = /* @__PURE__ */ new WeakMap();
|
|
114
|
+
function getDocManager(patches) {
|
|
115
|
+
let manager = managers.get(patches);
|
|
116
|
+
if (!manager) {
|
|
117
|
+
manager = new DocManager();
|
|
118
|
+
managers.set(patches, manager);
|
|
119
|
+
}
|
|
120
|
+
return manager;
|
|
121
|
+
}
|
|
122
|
+
export {
|
|
123
|
+
DocManager,
|
|
124
|
+
getDocManager
|
|
125
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { PatchesContextValue, PatchesProvider, PatchesProviderProps, usePatchesContext } from './context.js';
|
|
2
|
+
export { MaybeAccessor, UsePatchesDocOptions, UsePatchesDocReturn, UsePatchesSyncReturn, createPatchesDoc, usePatchesDoc, usePatchesSync } from './primitives.js';
|
|
3
|
+
export { DocManager, getDocManager } from './doc-manager.js';
|
|
4
|
+
import 'solid-js';
|
|
5
|
+
import '../client/Patches.js';
|
|
6
|
+
import '../event-signal.js';
|
|
7
|
+
import '../types.js';
|
|
8
|
+
import '../json-patch/JSONPatch.js';
|
|
9
|
+
import '@dabble/delta';
|
|
10
|
+
import '../json-patch/types.js';
|
|
11
|
+
import '../client/PatchesDoc.js';
|
|
12
|
+
import '../algorithms/shared/changeBatching.js';
|
|
13
|
+
import '../client/PatchesStore.js';
|
|
14
|
+
import '../net/PatchesSync.js';
|
|
15
|
+
import '../net/protocol/JSONRPCClient.js';
|
|
16
|
+
import '../net/protocol/types.js';
|
|
17
|
+
import '../net/websocket/PatchesWebSocket.js';
|
|
18
|
+
import '../net/PatchesClient.js';
|
|
19
|
+
import '../net/websocket/WebSocketTransport.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { PatchesProvider, usePatchesContext } from "./context.js";
|
|
3
|
+
import {
|
|
4
|
+
usePatchesDoc,
|
|
5
|
+
usePatchesSync,
|
|
6
|
+
createPatchesDoc
|
|
7
|
+
} from "./primitives.js";
|
|
8
|
+
import { getDocManager, DocManager } from "./doc-manager.js";
|
|
9
|
+
export {
|
|
10
|
+
DocManager,
|
|
11
|
+
PatchesProvider,
|
|
12
|
+
createPatchesDoc,
|
|
13
|
+
getDocManager,
|
|
14
|
+
usePatchesContext,
|
|
15
|
+
usePatchesDoc,
|
|
16
|
+
usePatchesSync
|
|
17
|
+
};
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { Accessor } from 'solid-js';
|
|
2
|
+
import { PatchesDoc } from '../client/PatchesDoc.js';
|
|
3
|
+
import { ChangeMutator } from '../types.js';
|
|
4
|
+
import '../event-signal.js';
|
|
5
|
+
import '../algorithms/shared/changeBatching.js';
|
|
6
|
+
import '../json-patch/JSONPatch.js';
|
|
7
|
+
import '@dabble/delta';
|
|
8
|
+
import '../json-patch/types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Options for usePatchesDoc primitive.
|
|
12
|
+
*/
|
|
13
|
+
interface UsePatchesDocOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Whether to automatically open/close the document based on component lifecycle.
|
|
16
|
+
*
|
|
17
|
+
* - `false` (default): Assumes doc is already open. Throws if not.
|
|
18
|
+
* - `true`: Opens doc on mount with ref counting, closes on cleanup.
|
|
19
|
+
*
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
autoClose?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Return type for usePatchesDoc primitive.
|
|
26
|
+
*/
|
|
27
|
+
interface UsePatchesDocReturn<T extends object> {
|
|
28
|
+
/**
|
|
29
|
+
* Accessor for the document state.
|
|
30
|
+
* Updated whenever the document changes (local or remote).
|
|
31
|
+
*/
|
|
32
|
+
data: Accessor<T | undefined>;
|
|
33
|
+
/**
|
|
34
|
+
* Whether the document is currently loading/syncing.
|
|
35
|
+
* - `true` during initial load or updates
|
|
36
|
+
* - `false` when fully synced
|
|
37
|
+
*/
|
|
38
|
+
loading: Accessor<boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* Error that occurred during sync, if any.
|
|
41
|
+
*/
|
|
42
|
+
error: Accessor<Error | null>;
|
|
43
|
+
/**
|
|
44
|
+
* The committed revision number.
|
|
45
|
+
* Increments each time the server confirms changes.
|
|
46
|
+
*/
|
|
47
|
+
rev: Accessor<number>;
|
|
48
|
+
/**
|
|
49
|
+
* Whether there are pending local changes not yet committed by server.
|
|
50
|
+
*/
|
|
51
|
+
hasPending: Accessor<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* Make changes to the document.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* change((patch, root) => {
|
|
58
|
+
* patch.replace(root.title!, 'New Title')
|
|
59
|
+
* })
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
change: (mutator: ChangeMutator<T>) => void;
|
|
63
|
+
/**
|
|
64
|
+
* The underlying PatchesDoc instance.
|
|
65
|
+
* Useful for advanced operations.
|
|
66
|
+
*/
|
|
67
|
+
doc: Accessor<PatchesDoc<T> | undefined>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Solid primitive for reactive Patches document state.
|
|
71
|
+
*
|
|
72
|
+
* Provides reactive access to a Patches document with automatic lifecycle management.
|
|
73
|
+
*
|
|
74
|
+
* ## Explicit Lifecycle (default)
|
|
75
|
+
*
|
|
76
|
+
* By default, assumes the document is already open and just adds Solid reactivity.
|
|
77
|
+
* You control when documents are opened and closed.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```tsx
|
|
81
|
+
* import { onMount, onCleanup } from 'solid-js';
|
|
82
|
+
* import { usePatchesContext, usePatchesDoc } from '@dabble/patches/solid';
|
|
83
|
+
*
|
|
84
|
+
* function MyComponent(props) {
|
|
85
|
+
* const { patches } = usePatchesContext();
|
|
86
|
+
*
|
|
87
|
+
* // You control lifecycle
|
|
88
|
+
* onMount(() => patches.openDoc(props.docId));
|
|
89
|
+
* onCleanup(() => patches.closeDoc(props.docId));
|
|
90
|
+
*
|
|
91
|
+
* // Just adds reactivity
|
|
92
|
+
* // For static: usePatchesDoc('doc-123')
|
|
93
|
+
* // For reactive: usePatchesDoc(() => props.docId)
|
|
94
|
+
* const { data, loading, change } = usePatchesDoc(() => props.docId);
|
|
95
|
+
*
|
|
96
|
+
* return <div>{data()?.title}</div>;
|
|
97
|
+
* }
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* ## Auto Lifecycle
|
|
101
|
+
*
|
|
102
|
+
* With `autoClose: true`, the primitive manages the document lifecycle with
|
|
103
|
+
* reference counting. Safe to use in multiple components with the same docId.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```tsx
|
|
107
|
+
* // Opens on mount, closes on cleanup (ref-counted)
|
|
108
|
+
* const { data, loading, change } = usePatchesDoc('doc-123', {
|
|
109
|
+
* autoClose: true
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @param docId - Document ID (static string or accessor function)
|
|
114
|
+
* @param options - Configuration options
|
|
115
|
+
* @returns Reactive document state and utilities
|
|
116
|
+
* @throws Error if Patches context not provided
|
|
117
|
+
* @throws Error if doc not open in explicit mode
|
|
118
|
+
*/
|
|
119
|
+
declare function usePatchesDoc<T extends object>(docId: MaybeAccessor<string>, options?: UsePatchesDocOptions): UsePatchesDocReturn<T>;
|
|
120
|
+
/**
|
|
121
|
+
* Return type for usePatchesSync primitive.
|
|
122
|
+
*/
|
|
123
|
+
interface UsePatchesSyncReturn {
|
|
124
|
+
/**
|
|
125
|
+
* Whether the WebSocket connection is established.
|
|
126
|
+
*/
|
|
127
|
+
connected: Accessor<boolean>;
|
|
128
|
+
/**
|
|
129
|
+
* Whether documents are currently syncing with the server.
|
|
130
|
+
*/
|
|
131
|
+
syncing: Accessor<boolean>;
|
|
132
|
+
/**
|
|
133
|
+
* Whether the client believes it has network connectivity.
|
|
134
|
+
*/
|
|
135
|
+
online: Accessor<boolean>;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Solid primitive for reactive Patches sync state.
|
|
139
|
+
*
|
|
140
|
+
* Provides reactive access to PatchesSync connection and sync status.
|
|
141
|
+
* Useful for showing "Offline" banners, global loading indicators, etc.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```tsx
|
|
145
|
+
* const { connected, syncing, online } = usePatchesSync();
|
|
146
|
+
*
|
|
147
|
+
* return (
|
|
148
|
+
* <Show when={!connected()}>
|
|
149
|
+
* <div>You are offline</div>
|
|
150
|
+
* </Show>
|
|
151
|
+
* );
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @returns Reactive sync state
|
|
155
|
+
* @throws Error if Patches context not provided
|
|
156
|
+
* @throws Error if PatchesSync was not provided to context
|
|
157
|
+
*/
|
|
158
|
+
declare function usePatchesSync(): UsePatchesSyncReturn;
|
|
159
|
+
/**
|
|
160
|
+
* Type for document ID - can be static string or accessor function.
|
|
161
|
+
*/
|
|
162
|
+
type MaybeAccessor<T> = T | Accessor<T>;
|
|
163
|
+
/**
|
|
164
|
+
* Creates a named document context that can be provided to child components.
|
|
165
|
+
*
|
|
166
|
+
* This enables child components to access the document using the returned `useDoc` hook
|
|
167
|
+
* without needing to pass the docId down through props. Supports both static and
|
|
168
|
+
* reactive docIds.
|
|
169
|
+
*
|
|
170
|
+
* ## Use Cases
|
|
171
|
+
*
|
|
172
|
+
* **Static document (user settings):**
|
|
173
|
+
* ```tsx
|
|
174
|
+
* const { Provider, useDoc } = createPatchesDoc<User>('user');
|
|
175
|
+
*
|
|
176
|
+
* <Provider docId="user-123">
|
|
177
|
+
* <UserProfile />
|
|
178
|
+
* </Provider>
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* **Reactive document (multi-tab):**
|
|
182
|
+
* ```tsx
|
|
183
|
+
* const { Provider, useDoc } = createPatchesDoc<Whiteboard>('whiteboard');
|
|
184
|
+
* const [activeTabId, setActiveTabId] = createSignal('design-1');
|
|
185
|
+
*
|
|
186
|
+
* <Provider docId={activeTabId}>
|
|
187
|
+
* <WhiteboardCanvas />
|
|
188
|
+
* </Provider>
|
|
189
|
+
* ```
|
|
190
|
+
*
|
|
191
|
+
* **With autoClose:**
|
|
192
|
+
* ```tsx
|
|
193
|
+
* const { Provider, useDoc } = createPatchesDoc<Doc>('document');
|
|
194
|
+
* const [currentDocId, setCurrentDocId] = createSignal('doc-1');
|
|
195
|
+
*
|
|
196
|
+
* <Provider docId={currentDocId} autoClose>
|
|
197
|
+
* <DocumentEditor />
|
|
198
|
+
* </Provider>
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* @param name - Unique identifier for this document context (e.g., 'whiteboard', 'user')
|
|
202
|
+
* @returns Object with Provider component and useDoc hook
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```tsx
|
|
206
|
+
* // Create the context
|
|
207
|
+
* const { Provider: WhiteboardProvider, useDoc: useWhiteboard } =
|
|
208
|
+
* createPatchesDoc<WhiteboardDoc>('whiteboard');
|
|
209
|
+
*
|
|
210
|
+
* // Use in parent
|
|
211
|
+
* <WhiteboardProvider docId="whiteboard-123">
|
|
212
|
+
* <Canvas />
|
|
213
|
+
* </WhiteboardProvider>
|
|
214
|
+
*
|
|
215
|
+
* // Use in child
|
|
216
|
+
* function Canvas() {
|
|
217
|
+
* const { data, change } = useWhiteboard();
|
|
218
|
+
* return <div>{data()?.title}</div>;
|
|
219
|
+
* }
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
/**
|
|
223
|
+
* Props for the Provider component returned by createPatchesDoc.
|
|
224
|
+
*/
|
|
225
|
+
interface PatchesDocProviderProps {
|
|
226
|
+
docId: MaybeAccessor<string>;
|
|
227
|
+
autoClose?: boolean;
|
|
228
|
+
children: any;
|
|
229
|
+
}
|
|
230
|
+
declare function createPatchesDoc<T extends object>(name: string): {
|
|
231
|
+
Provider: (props: PatchesDocProviderProps) => any;
|
|
232
|
+
useDoc: () => UsePatchesDocReturn<T>;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export { type MaybeAccessor, type PatchesDocProviderProps, type UsePatchesDocOptions, type UsePatchesDocReturn, type UsePatchesSyncReturn, createPatchesDoc, usePatchesDoc, usePatchesSync };
|