@stratasync/react 0.2.0

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 (42) hide show
  1. package/README.md +91 -0
  2. package/dist/context.d.ts +21 -0
  3. package/dist/context.d.ts.map +1 -0
  4. package/dist/context.js +27 -0
  5. package/dist/context.js.map +1 -0
  6. package/dist/hooks/use-connection-state.d.ts +55 -0
  7. package/dist/hooks/use-connection-state.d.ts.map +1 -0
  8. package/dist/hooks/use-connection-state.js +90 -0
  9. package/dist/hooks/use-connection-state.js.map +1 -0
  10. package/dist/hooks/use-model.d.ts +22 -0
  11. package/dist/hooks/use-model.d.ts.map +1 -0
  12. package/dist/hooks/use-model.js +162 -0
  13. package/dist/hooks/use-model.js.map +1 -0
  14. package/dist/hooks/use-query.d.ts +35 -0
  15. package/dist/hooks/use-query.d.ts.map +1 -0
  16. package/dist/hooks/use-query.js +302 -0
  17. package/dist/hooks/use-query.js.map +1 -0
  18. package/dist/hooks/use-sync-client.d.ts +25 -0
  19. package/dist/hooks/use-sync-client.d.ts.map +1 -0
  20. package/dist/hooks/use-sync-client.js +49 -0
  21. package/dist/hooks/use-sync-client.js.map +1 -0
  22. package/dist/hooks/use-yjs-document.d.ts +108 -0
  23. package/dist/hooks/use-yjs-document.d.ts.map +1 -0
  24. package/dist/hooks/use-yjs-document.js +202 -0
  25. package/dist/hooks/use-yjs-document.js.map +1 -0
  26. package/dist/hooks/use-yjs-presence.d.ts +62 -0
  27. package/dist/hooks/use-yjs-presence.d.ts.map +1 -0
  28. package/dist/hooks/use-yjs-presence.js +230 -0
  29. package/dist/hooks/use-yjs-presence.js.map +1 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +9 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/provider.d.ts +7 -0
  35. package/dist/provider.d.ts.map +1 -0
  36. package/dist/provider.js +117 -0
  37. package/dist/provider.js.map +1 -0
  38. package/dist/types.d.ts +107 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +53 -0
@@ -0,0 +1,302 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { useSyncClientInstance, useSyncReady } from "./use-sync-client.js";
3
+ const captureSnapshots = (items) => items.map((item) => {
4
+ const record = item;
5
+ return { id: record.id, updatedAt: record.updatedAt };
6
+ });
7
+ /**
8
+ * Compares new query result items against previously-captured snapshots.
9
+ * Returns true if every item matches its snapshot by id + updatedAt.
10
+ *
11
+ * Unlike a direct reference comparison, this detects in-place mutations
12
+ * because the snapshot stores *copied* values from the time data was last
13
+ * committed to React state.
14
+ */
15
+ const isQueryResultEqual = (snapshots, next) => {
16
+ if (snapshots.length !== next.length) {
17
+ return false;
18
+ }
19
+ for (let i = 0; i < snapshots.length; i += 1) {
20
+ const s = snapshots[i];
21
+ const n = next[i];
22
+ if (s?.id !== n.id) {
23
+ return false;
24
+ }
25
+ if (s?.updatedAt !== n.updatedAt) {
26
+ return false;
27
+ }
28
+ }
29
+ return true;
30
+ };
31
+ /**
32
+ * Default empty state for a query that hasn't loaded data yet.
33
+ */
34
+ const emptyQueryState = (isLoading) => ({
35
+ data: [],
36
+ hasMore: false,
37
+ isLoading,
38
+ totalCount: undefined,
39
+ });
40
+ /**
41
+ * Cast UseQueryOptions<T> to QueryOptions compatible with the identity map's
42
+ * `T & Record<string, unknown>` shape.
43
+ */
44
+ const buildSyncQueryOptions = (opts) => ({
45
+ includeArchived: opts.includeArchived,
46
+ limit: opts.limit,
47
+ offset: opts.offset,
48
+ orderBy: opts.orderBy,
49
+ where: opts.where,
50
+ });
51
+ /**
52
+ * Synchronously query from a raw Map (avoids flash of empty state)
53
+ */
54
+ const querySyncFromMap = (map, options = {}) => {
55
+ let results = [...map.values()];
56
+ if (options.where) {
57
+ results = results.filter(options.where);
58
+ }
59
+ if (!options.includeArchived) {
60
+ results = results.filter((item) => !item.archivedAt);
61
+ }
62
+ const totalCount = results.length;
63
+ if (options.orderBy) {
64
+ results = results.toSorted(options.orderBy);
65
+ }
66
+ if (options.offset && options.offset > 0) {
67
+ results = results.slice(options.offset);
68
+ }
69
+ let hasMore = false;
70
+ if (options.limit && options.limit > 0) {
71
+ hasMore = results.length > options.limit;
72
+ results = results.slice(0, options.limit);
73
+ }
74
+ return { data: results, hasMore, totalCount };
75
+ };
76
+ /**
77
+ * Hook to query models with filtering, sorting, and pagination
78
+ *
79
+ * @param modelName - Name of the model to query
80
+ * @param options - Query options including filters, sorting, and pagination
81
+ * @returns UseQueryResult with data array, loading state, and metadata
82
+ *
83
+ * @example
84
+ * ```tsx
85
+ * function TaskList({ projectId }: { projectId: string }) {
86
+ * const { data: tasks, isLoading, hasMore } = useQuery<Task>('Task', {
87
+ * where: (task) => task.projectId === projectId,
88
+ * orderBy: (a, b) => a.createdAt - b.createdAt,
89
+ * limit: 20,
90
+ * });
91
+ *
92
+ * if (isLoading) return <Spinner />;
93
+ *
94
+ * return (
95
+ * <ul>
96
+ * {tasks.map((task) => (
97
+ * <li key={task.id}>{task.title}</li>
98
+ * ))}
99
+ * </ul>
100
+ * );
101
+ * }
102
+ * ```
103
+ */
104
+ export const useQuery = (modelName, options = {}) => {
105
+ const client = useSyncClientInstance();
106
+ const isReady = useSyncReady();
107
+ // Compute initial state from identity map (only runs on mount via lazy useState)
108
+ const computeState = () => {
109
+ if (options.skip) {
110
+ return emptyQueryState(false);
111
+ }
112
+ if (!isReady) {
113
+ return emptyQueryState(true);
114
+ }
115
+ const map = client.getIdentityMap(modelName);
116
+ if (map.size === 0) {
117
+ return emptyQueryState(true);
118
+ }
119
+ const result = querySyncFromMap(map, buildSyncQueryOptions(options));
120
+ return {
121
+ data: result.data,
122
+ hasMore: result.hasMore,
123
+ isLoading: false,
124
+ totalCount: result.totalCount,
125
+ };
126
+ };
127
+ // Lazy initializers — only run on mount, preventing MobX tracking on re-renders
128
+ const initialRef = useRef(null);
129
+ if (initialRef.current === null) {
130
+ initialRef.current = computeState();
131
+ }
132
+ const initial = initialRef.current;
133
+ const [data, setData] = useState(initial.data);
134
+ const [isLoading, setIsLoading] = useState(initial.isLoading);
135
+ const [error, setError] = useState(null);
136
+ const [totalCount, setTotalCount] = useState(initial.totalCount);
137
+ const [hasMore, setHasMore] = useState(initial.hasMore);
138
+ // Use ref to track options to avoid infinite loops
139
+ const optionsRef = useRef(options);
140
+ optionsRef.current = options;
141
+ // Track current data for structural equality checks (avoids unnecessary re-renders)
142
+ const dataRef = useRef(initial.data);
143
+ // Snapshot of id+updatedAt per item — detects in-place identity-map mutations
144
+ const snapshotsRef = useRef(captureSnapshots(initial.data));
145
+ // Track if we have data to avoid setting loading state when refreshing cached data
146
+ const hasDataRef = useRef(initial.data.length > 0);
147
+ // Ref mirrors for metadata state — only call setters when values actually change
148
+ const totalCountRef = useRef(initial.totalCount);
149
+ const hasMoreRef = useRef(initial.hasMore);
150
+ const isLoadingRef = useRef(initial.isLoading);
151
+ const errorRef = useRef(null);
152
+ // Microtask debounce flag — coalesces rapid modelChange events into one refresh
153
+ const pendingRefreshRef = useRef(false);
154
+ const requestVersionRef = useRef(0);
155
+ /**
156
+ * Apply a query result to React state. Only calls setters when values
157
+ * actually changed, preventing unnecessary re-renders.
158
+ */
159
+ const applyResult = useCallback((resultData, resultTotalCount, resultHasMore, applyOptions = {}) => {
160
+ if (applyOptions.forceDataUpdate ||
161
+ !isQueryResultEqual(snapshotsRef.current, resultData)) {
162
+ snapshotsRef.current = captureSnapshots(resultData);
163
+ dataRef.current = resultData;
164
+ setData(resultData);
165
+ }
166
+ if (resultTotalCount !== totalCountRef.current) {
167
+ totalCountRef.current = resultTotalCount;
168
+ setTotalCount(resultTotalCount);
169
+ }
170
+ if (resultHasMore !== hasMoreRef.current) {
171
+ hasMoreRef.current = resultHasMore;
172
+ setHasMore(resultHasMore);
173
+ }
174
+ hasDataRef.current = resultData.length > 0;
175
+ if (isLoadingRef.current !== false) {
176
+ isLoadingRef.current = false;
177
+ setIsLoading(false);
178
+ }
179
+ if (errorRef.current !== null) {
180
+ errorRef.current = null;
181
+ setError(null);
182
+ }
183
+ }, []);
184
+ /** Clear data and stop loading (used when query is skipped). */
185
+ const clearSkipped = useCallback(() => {
186
+ if (dataRef.current.length > 0) {
187
+ dataRef.current = [];
188
+ snapshotsRef.current = [];
189
+ setData([]);
190
+ }
191
+ if (isLoadingRef.current !== false) {
192
+ isLoadingRef.current = false;
193
+ setIsLoading(false);
194
+ }
195
+ }, []);
196
+ const executeQuery = useCallback(async () => {
197
+ if (optionsRef.current.skip) {
198
+ requestVersionRef.current += 1;
199
+ clearSkipped();
200
+ return;
201
+ }
202
+ requestVersionRef.current += 1;
203
+ const requestVersion = requestVersionRef.current;
204
+ // Only show loading if we don't already have cached data
205
+ // This prevents the flash of empty state when refreshing
206
+ if (!hasDataRef.current) {
207
+ isLoadingRef.current = true;
208
+ setIsLoading(true);
209
+ }
210
+ if (errorRef.current !== null) {
211
+ errorRef.current = null;
212
+ setError(null);
213
+ }
214
+ try {
215
+ const queryOptions = {
216
+ includeArchived: optionsRef.current.includeArchived,
217
+ limit: optionsRef.current.limit,
218
+ offset: optionsRef.current.offset,
219
+ orderBy: optionsRef.current.orderBy,
220
+ where: optionsRef.current.where,
221
+ };
222
+ const result = await client.query(modelName, queryOptions);
223
+ if (requestVersion !== requestVersionRef.current) {
224
+ return;
225
+ }
226
+ applyResult(result.data, result.totalCount, result.hasMore);
227
+ }
228
+ catch (queryError) {
229
+ if (requestVersion !== requestVersionRef.current) {
230
+ return;
231
+ }
232
+ const newError = queryError instanceof Error
233
+ ? queryError
234
+ : new Error(String(queryError));
235
+ errorRef.current = newError;
236
+ setError(newError);
237
+ // Preserve stale data on refresh errors to avoid UI flash.
238
+ hasDataRef.current = dataRef.current.length > 0;
239
+ if (isLoadingRef.current !== false) {
240
+ isLoadingRef.current = false;
241
+ setIsLoading(false);
242
+ }
243
+ }
244
+ }, [client, modelName, applyResult, clearSkipped]);
245
+ // Synchronous refresh — reads identity map and updates React state immediately
246
+ const refreshSync = useCallback(() => {
247
+ if (optionsRef.current.skip) {
248
+ clearSkipped();
249
+ return;
250
+ }
251
+ const map = client.getIdentityMap(modelName);
252
+ const result = querySyncFromMap(map, buildSyncQueryOptions(optionsRef.current));
253
+ applyResult(result.data, result.totalCount, result.hasMore);
254
+ }, [client, modelName, applyResult, clearSkipped]);
255
+ useEffect(() => {
256
+ if (isReady && !options.skip) {
257
+ executeQuery();
258
+ }
259
+ else if (options.skip) {
260
+ requestVersionRef.current += 1;
261
+ clearSkipped();
262
+ }
263
+ }, [isReady, options.skip, executeQuery, clearSkipped]);
264
+ useEffect(() => () => {
265
+ requestVersionRef.current += 1;
266
+ }, []);
267
+ useEffect(() => {
268
+ if (!isReady || options.skip) {
269
+ return;
270
+ }
271
+ const unsubscribe = client.onEvent((event) => {
272
+ // Coalesce rapid modelChange events (e.g. a delta packet with many
273
+ // actions for the same model type) into a single refreshSync call.
274
+ if (event.type === "modelChange" &&
275
+ event.modelName === modelName &&
276
+ !pendingRefreshRef.current) {
277
+ pendingRefreshRef.current = true;
278
+ queueMicrotask(() => {
279
+ pendingRefreshRef.current = false;
280
+ refreshSync();
281
+ });
282
+ }
283
+ });
284
+ return () => {
285
+ unsubscribe();
286
+ pendingRefreshRef.current = false;
287
+ };
288
+ }, [client, modelName, isReady, options.skip, refreshSync]);
289
+ return {
290
+ data,
291
+ error,
292
+ hasMore,
293
+ isLoading,
294
+ refresh: executeQuery,
295
+ totalCount,
296
+ };
297
+ };
298
+ /**
299
+ * Hook to query all models of a type
300
+ */
301
+ export const useQueryAll = (modelName, options = {}) => useQuery(modelName, options);
302
+ //# sourceMappingURL=use-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-query.js","sourceRoot":"","sources":["../../src/hooks/use-query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { SyncContextValue, SyncStatusContextValue } from "../types.js";
2
+ export declare const useSyncStatusValue: () => SyncStatusContextValue;
3
+ export declare const useSyncBacklogValue: () => number;
4
+ /**
5
+ * Hook to access the sync client from context
6
+ * @throws Error if used outside of SyncProvider
7
+ */
8
+ export declare const useSyncClient: () => SyncContextValue;
9
+ /**
10
+ * Hook to access just the sync client instance
11
+ */
12
+ export declare const useSyncClientInstance: () => SyncContextValue["client"];
13
+ /**
14
+ * Hook to check if the sync client is ready
15
+ */
16
+ export declare const useSyncReady: () => boolean;
17
+ /**
18
+ * Hook to get the current sync state
19
+ */
20
+ export declare const useSyncState: () => SyncContextValue["state"];
21
+ /**
22
+ * Hook to get the latest sync error.
23
+ */
24
+ export declare const useSyncError: () => Error | null;
25
+ //# sourceMappingURL=use-sync-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-sync-client.d.ts","sourceRoot":"","sources":["../../src/hooks/use-sync-client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAY5E,eAAO,MAAM,kBAAkB,QAAO,sBAMrC,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAO,MAItC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,QAAO,gBAMhC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAO,gBAAgB,CAAC,QAAQ,CACzC,CAAC;AAE1B;;GAEG;AACH,eAAO,MAAM,YAAY,QAAO,OAAuC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,YAAY,QAAO,gBAAgB,CAAC,OAAO,CAC5B,CAAC;AAE7B;;GAEG;AACH,eAAO,MAAM,YAAY,QAAO,KAAK,GAAG,IAAkC,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { useContext } from "react";
2
+ import { SyncBacklogContext, SyncClientContext, SyncStatusContext, } from "../context.js";
3
+ const SYNC_PROVIDER_ERROR = "useSyncClient must be used within a SyncProvider";
4
+ const useRequiredSyncClient = () => {
5
+ const client = useContext(SyncClientContext);
6
+ if (!client) {
7
+ throw new Error(SYNC_PROVIDER_ERROR);
8
+ }
9
+ return client;
10
+ };
11
+ export const useSyncStatusValue = () => {
12
+ const status = useContext(SyncStatusContext);
13
+ if (!status) {
14
+ throw new Error(SYNC_PROVIDER_ERROR);
15
+ }
16
+ return status;
17
+ };
18
+ export const useSyncBacklogValue = () => {
19
+ // Ensure hooks still throw a clear provider error when used outside context.
20
+ useRequiredSyncClient();
21
+ return useContext(SyncBacklogContext);
22
+ };
23
+ /**
24
+ * Hook to access the sync client from context
25
+ * @throws Error if used outside of SyncProvider
26
+ */
27
+ export const useSyncClient = () => {
28
+ const client = useRequiredSyncClient();
29
+ const status = useSyncStatusValue();
30
+ const backlog = useSyncBacklogValue();
31
+ return { backlog, client, ...status };
32
+ };
33
+ /**
34
+ * Hook to access just the sync client instance
35
+ */
36
+ export const useSyncClientInstance = () => useRequiredSyncClient();
37
+ /**
38
+ * Hook to check if the sync client is ready
39
+ */
40
+ export const useSyncReady = () => useSyncStatusValue().isReady;
41
+ /**
42
+ * Hook to get the current sync state
43
+ */
44
+ export const useSyncState = () => useSyncStatusValue().state;
45
+ /**
46
+ * Hook to get the latest sync error.
47
+ */
48
+ export const useSyncError = () => useSyncStatusValue().error;
49
+ //# sourceMappingURL=use-sync-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-sync-client.js","sourceRoot":"","sources":["../../src/hooks/use-sync-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,MAAM,mBAAmB,GAAG,kDAAkD,CAAC;AAE/E,MAAM,qBAAqB,GAAG,GAA+B,EAAE;IAC7D,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAA2B,EAAE;IAC7D,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAW,EAAE;IAC9C,6EAA6E;IAC7E,qBAAqB,EAAE,CAAC;IACxB,OAAO,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAqB,EAAE;IAClD,MAAM,MAAM,GAAG,qBAAqB,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,mBAAmB,EAAE,CAAC;IAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;AACxC,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAA+B,EAAE,CACpE,qBAAqB,EAAE,CAAC;AAE1B;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAY,EAAE,CAAC,kBAAkB,EAAE,CAAC,OAAO,CAAC;AAExE;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAA8B,EAAE,CAC1D,kBAAkB,EAAE,CAAC,KAAK,CAAC;AAE7B;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAiB,EAAE,CAAC,kBAAkB,EAAE,CAAC,KAAK,CAAC"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * React hook for Yjs collaborative document editing.
3
+ */
4
+ import type { Doc as YDoc } from "yjs";
5
+ /**
6
+ * Document key identifying a collaborative field.
7
+ */
8
+ export interface DocumentKey {
9
+ entityType: string;
10
+ entityId: string;
11
+ fieldName: string;
12
+ }
13
+ /**
14
+ * Connection state for Yjs document.
15
+ */
16
+ export type YjsConnectionState = "disconnected" | "connecting" | "syncing" | "connected";
17
+ /**
18
+ * Session participant information.
19
+ */
20
+ interface SessionParticipant {
21
+ userId: string;
22
+ isEditing: boolean;
23
+ }
24
+ /**
25
+ * Session state for a collaborative document.
26
+ */
27
+ export interface YjsSessionState {
28
+ active: boolean;
29
+ participants: SessionParticipant[];
30
+ }
31
+ /**
32
+ * Options for useYjsDocument hook.
33
+ */
34
+ export interface UseYjsDocumentOptions {
35
+ /** Initial content if document is empty */
36
+ initialContent?: string;
37
+ /** Whether to automatically connect when component mounts */
38
+ autoConnect?: boolean;
39
+ /** Whether to start in editing mode */
40
+ editing?: boolean;
41
+ /** Skip connecting (useful for conditional loading) */
42
+ skip?: boolean;
43
+ }
44
+ /**
45
+ * Result of useYjsDocument hook.
46
+ */
47
+ export interface UseYjsDocumentResult {
48
+ /** The Yjs document instance */
49
+ doc: YDoc | null;
50
+ /** Current connection state */
51
+ connectionState: YjsConnectionState;
52
+ /** Whether the document is connected for collaboration */
53
+ isConnected: boolean;
54
+ /** Whether there's an active collaborative session */
55
+ isSessionActive: boolean;
56
+ /** Current session participants */
57
+ participants: SessionParticipant[];
58
+ /** Current content of the document */
59
+ content: string;
60
+ /** Connect to the document */
61
+ connect: () => void;
62
+ /** Disconnect from the document */
63
+ disconnect: () => void;
64
+ /** Signal editing focus */
65
+ focus: () => void;
66
+ /** Signal editing blur */
67
+ blur: () => void;
68
+ /** Any error that occurred */
69
+ error: Error | null;
70
+ }
71
+ /**
72
+ * Hook to manage a Yjs collaborative document.
73
+ *
74
+ * @param docKey - Document key identifying the field
75
+ * @param options - Hook options
76
+ * @returns UseYjsDocumentResult with document, connection state, and controls
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * function TaskDescriptionEditor({ taskId }: { taskId: string }) {
81
+ * const {
82
+ * doc,
83
+ * isSessionActive,
84
+ * participants,
85
+ * } = useYjsDocument(
86
+ * { entityType: 'Task', entityId: taskId, fieldName: 'description' },
87
+ * { autoConnect: true, initialContent: task.description }
88
+ * );
89
+ *
90
+ * // doc becomes available after the first render (even offline).
91
+ * // Initial content is seeded into the local Y.Doc during connect(),
92
+ * // so the editor shows content immediately without waiting for the server.
93
+ * return (
94
+ * <div>
95
+ * {isSessionActive && (
96
+ * <div>
97
+ * Collaborating with: {participants.map(p => p.userId).join(', ')}
98
+ * </div>
99
+ * )}
100
+ * {doc && <TiptapEditor doc={doc} />}
101
+ * </div>
102
+ * );
103
+ * }
104
+ * ```
105
+ */
106
+ export declare const useYjsDocument: (docKey: DocumentKey, options?: UseYjsDocumentOptions) => UseYjsDocumentResult;
107
+ export {};
108
+ //# sourceMappingURL=use-yjs-document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-yjs-document.d.ts","sourceRoot":"","sources":["../../src/hooks/use-yjs-document.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAC;AAIvC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,YAAY,GACZ,SAAS,GACT,WAAW,CAAC;AAEhB;;GAEG;AACH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,kBAAkB,EAAE,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,gCAAgC;IAChC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACjB,+BAA+B;IAC/B,eAAe,EAAE,kBAAkB,CAAC;IACpC,0DAA0D;IAC1D,WAAW,EAAE,OAAO,CAAC;IACrB,sDAAsD;IACtD,eAAe,EAAE,OAAO,CAAC;IACzB,mCAAmC;IACnC,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,mCAAmC;IACnC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,WAAW,EACnB,UAAS,qBAA0B,KAClC,oBAuMF,CAAC"}