@fatagnus/dink-sync 1.0.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 (186) hide show
  1. package/README.md +312 -0
  2. package/dist/client/attachment.d.ts +225 -0
  3. package/dist/client/attachment.d.ts.map +1 -0
  4. package/dist/client/attachment.js +402 -0
  5. package/dist/client/attachment.js.map +1 -0
  6. package/dist/client/binary-encoding.d.ts +45 -0
  7. package/dist/client/binary-encoding.d.ts.map +1 -0
  8. package/dist/client/binary-encoding.js +90 -0
  9. package/dist/client/binary-encoding.js.map +1 -0
  10. package/dist/client/collection.d.ts +10 -0
  11. package/dist/client/collection.d.ts.map +1 -0
  12. package/dist/client/collection.js +924 -0
  13. package/dist/client/collection.js.map +1 -0
  14. package/dist/client/compression.d.ts +56 -0
  15. package/dist/client/compression.d.ts.map +1 -0
  16. package/dist/client/compression.js +173 -0
  17. package/dist/client/compression.js.map +1 -0
  18. package/dist/client/crdt/index.d.ts +2 -0
  19. package/dist/client/crdt/index.d.ts.map +1 -0
  20. package/dist/client/crdt/index.js +2 -0
  21. package/dist/client/crdt/index.js.map +1 -0
  22. package/dist/client/crdt/yjs-doc.d.ts +88 -0
  23. package/dist/client/crdt/yjs-doc.d.ts.map +1 -0
  24. package/dist/client/crdt/yjs-doc.js +123 -0
  25. package/dist/client/crdt/yjs-doc.js.map +1 -0
  26. package/dist/client/index.d.ts +66 -0
  27. package/dist/client/index.d.ts.map +1 -0
  28. package/dist/client/index.js +233 -0
  29. package/dist/client/index.js.map +1 -0
  30. package/dist/client/mock-transport.d.ts +155 -0
  31. package/dist/client/mock-transport.d.ts.map +1 -0
  32. package/dist/client/mock-transport.js +292 -0
  33. package/dist/client/mock-transport.js.map +1 -0
  34. package/dist/client/network-detector.d.ts +65 -0
  35. package/dist/client/network-detector.d.ts.map +1 -0
  36. package/dist/client/network-detector.js +147 -0
  37. package/dist/client/network-detector.js.map +1 -0
  38. package/dist/client/provisioning.d.ts +126 -0
  39. package/dist/client/provisioning.d.ts.map +1 -0
  40. package/dist/client/provisioning.js +125 -0
  41. package/dist/client/provisioning.js.map +1 -0
  42. package/dist/client/signal.d.ts +13 -0
  43. package/dist/client/signal.d.ts.map +1 -0
  44. package/dist/client/signal.js +27 -0
  45. package/dist/client/signal.js.map +1 -0
  46. package/dist/client/sync-engine.d.ts +298 -0
  47. package/dist/client/sync-engine.d.ts.map +1 -0
  48. package/dist/client/sync-engine.js +904 -0
  49. package/dist/client/sync-engine.js.map +1 -0
  50. package/dist/client/synced-edge.d.ts +109 -0
  51. package/dist/client/synced-edge.d.ts.map +1 -0
  52. package/dist/client/synced-edge.js +179 -0
  53. package/dist/client/synced-edge.js.map +1 -0
  54. package/dist/client/synced-offline-edge-types.d.ts +540 -0
  55. package/dist/client/synced-offline-edge-types.d.ts.map +1 -0
  56. package/dist/client/synced-offline-edge-types.js +10 -0
  57. package/dist/client/synced-offline-edge-types.js.map +1 -0
  58. package/dist/client/synced-offline-edge.d.ts +54 -0
  59. package/dist/client/synced-offline-edge.d.ts.map +1 -0
  60. package/dist/client/synced-offline-edge.js +731 -0
  61. package/dist/client/synced-offline-edge.js.map +1 -0
  62. package/dist/client/transport.d.ts +202 -0
  63. package/dist/client/transport.d.ts.map +1 -0
  64. package/dist/client/transport.js +409 -0
  65. package/dist/client/transport.js.map +1 -0
  66. package/dist/client/types.d.ts +622 -0
  67. package/dist/client/types.d.ts.map +1 -0
  68. package/dist/client/types.js +60 -0
  69. package/dist/client/types.js.map +1 -0
  70. package/dist/client/validation.d.ts +61 -0
  71. package/dist/client/validation.d.ts.map +1 -0
  72. package/dist/client/validation.js +57 -0
  73. package/dist/client/validation.js.map +1 -0
  74. package/dist/client/versioning.d.ts +134 -0
  75. package/dist/client/versioning.d.ts.map +1 -0
  76. package/dist/client/versioning.js +304 -0
  77. package/dist/client/versioning.js.map +1 -0
  78. package/dist/index.d.ts +40 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +51 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/persistence/encryption.d.ts +114 -0
  83. package/dist/persistence/encryption.d.ts.map +1 -0
  84. package/dist/persistence/encryption.js +286 -0
  85. package/dist/persistence/encryption.js.map +1 -0
  86. package/dist/persistence/index.d.ts +21 -0
  87. package/dist/persistence/index.d.ts.map +1 -0
  88. package/dist/persistence/index.js +20 -0
  89. package/dist/persistence/index.js.map +1 -0
  90. package/dist/persistence/memory.d.ts +32 -0
  91. package/dist/persistence/memory.d.ts.map +1 -0
  92. package/dist/persistence/memory.js +57 -0
  93. package/dist/persistence/memory.js.map +1 -0
  94. package/dist/persistence/migrations.d.ts +106 -0
  95. package/dist/persistence/migrations.d.ts.map +1 -0
  96. package/dist/persistence/migrations.js +176 -0
  97. package/dist/persistence/migrations.js.map +1 -0
  98. package/dist/persistence/pending-queue.d.ts +109 -0
  99. package/dist/persistence/pending-queue.d.ts.map +1 -0
  100. package/dist/persistence/pending-queue.js +249 -0
  101. package/dist/persistence/pending-queue.js.map +1 -0
  102. package/dist/persistence/pglite.d.ts +72 -0
  103. package/dist/persistence/pglite.d.ts.map +1 -0
  104. package/dist/persistence/pglite.js +126 -0
  105. package/dist/persistence/pglite.js.map +1 -0
  106. package/dist/persistence/quota-manager.d.ts +134 -0
  107. package/dist/persistence/quota-manager.d.ts.map +1 -0
  108. package/dist/persistence/quota-manager.js +242 -0
  109. package/dist/persistence/quota-manager.js.map +1 -0
  110. package/dist/persistence/types.d.ts +54 -0
  111. package/dist/persistence/types.d.ts.map +1 -0
  112. package/dist/persistence/types.js +2 -0
  113. package/dist/persistence/types.js.map +1 -0
  114. package/dist/react/OfflineEdgeProvider.d.ts +91 -0
  115. package/dist/react/OfflineEdgeProvider.d.ts.map +1 -0
  116. package/dist/react/OfflineEdgeProvider.js +127 -0
  117. package/dist/react/OfflineEdgeProvider.js.map +1 -0
  118. package/dist/react/SyncedOfflineEdgeProvider.d.ts +105 -0
  119. package/dist/react/SyncedOfflineEdgeProvider.d.ts.map +1 -0
  120. package/dist/react/SyncedOfflineEdgeProvider.js +138 -0
  121. package/dist/react/SyncedOfflineEdgeProvider.js.map +1 -0
  122. package/dist/react/index.d.ts +50 -0
  123. package/dist/react/index.d.ts.map +1 -0
  124. package/dist/react/index.js +51 -0
  125. package/dist/react/index.js.map +1 -0
  126. package/dist/react/useCollection.d.ts +77 -0
  127. package/dist/react/useCollection.d.ts.map +1 -0
  128. package/dist/react/useCollection.js +113 -0
  129. package/dist/react/useCollection.js.map +1 -0
  130. package/dist/react/useCollectionSyncMode.d.ts +61 -0
  131. package/dist/react/useCollectionSyncMode.d.ts.map +1 -0
  132. package/dist/react/useCollectionSyncMode.js +93 -0
  133. package/dist/react/useCollectionSyncMode.js.map +1 -0
  134. package/dist/react/useConnectionState.d.ts +44 -0
  135. package/dist/react/useConnectionState.d.ts.map +1 -0
  136. package/dist/react/useConnectionState.js +46 -0
  137. package/dist/react/useConnectionState.js.map +1 -0
  138. package/dist/react/useDocumentSyncStatus.d.ts +72 -0
  139. package/dist/react/useDocumentSyncStatus.d.ts.map +1 -0
  140. package/dist/react/useDocumentSyncStatus.js +110 -0
  141. package/dist/react/useDocumentSyncStatus.js.map +1 -0
  142. package/dist/react/useOfflineEdge.d.ts +58 -0
  143. package/dist/react/useOfflineEdge.d.ts.map +1 -0
  144. package/dist/react/useOfflineEdge.js +54 -0
  145. package/dist/react/useOfflineEdge.js.map +1 -0
  146. package/dist/react/usePendingChanges.d.ts +67 -0
  147. package/dist/react/usePendingChanges.d.ts.map +1 -0
  148. package/dist/react/usePendingChanges.js +90 -0
  149. package/dist/react/usePendingChanges.js.map +1 -0
  150. package/dist/react/useRejectedDocuments.d.ts +112 -0
  151. package/dist/react/useRejectedDocuments.d.ts.map +1 -0
  152. package/dist/react/useRejectedDocuments.js +213 -0
  153. package/dist/react/useRejectedDocuments.js.map +1 -0
  154. package/dist/react/useSyncControls.d.ts +96 -0
  155. package/dist/react/useSyncControls.d.ts.map +1 -0
  156. package/dist/react/useSyncControls.js +112 -0
  157. package/dist/react/useSyncControls.js.map +1 -0
  158. package/dist/react/useSyncProgress.d.ts +78 -0
  159. package/dist/react/useSyncProgress.d.ts.map +1 -0
  160. package/dist/react/useSyncProgress.js +90 -0
  161. package/dist/react/useSyncProgress.js.map +1 -0
  162. package/dist/react/useSyncRejected.d.ts +47 -0
  163. package/dist/react/useSyncRejected.d.ts.map +1 -0
  164. package/dist/react/useSyncRejected.js +55 -0
  165. package/dist/react/useSyncRejected.js.map +1 -0
  166. package/dist/react/useSyncStatus.d.ts +56 -0
  167. package/dist/react/useSyncStatus.d.ts.map +1 -0
  168. package/dist/react/useSyncStatus.js +59 -0
  169. package/dist/react/useSyncStatus.js.map +1 -0
  170. package/dist/react/useSyncedOfflineEdge.d.ts +69 -0
  171. package/dist/react/useSyncedOfflineEdge.d.ts.map +1 -0
  172. package/dist/react/useSyncedOfflineEdge.js +65 -0
  173. package/dist/react/useSyncedOfflineEdge.js.map +1 -0
  174. package/dist/service-worker/index.d.ts +7 -0
  175. package/dist/service-worker/index.d.ts.map +1 -0
  176. package/dist/service-worker/index.js +7 -0
  177. package/dist/service-worker/index.js.map +1 -0
  178. package/dist/service-worker/sync-worker.d.ts +230 -0
  179. package/dist/service-worker/sync-worker.d.ts.map +1 -0
  180. package/dist/service-worker/sync-worker.js +471 -0
  181. package/dist/service-worker/sync-worker.js.map +1 -0
  182. package/dist/types.d.ts +6 -0
  183. package/dist/types.d.ts.map +1 -0
  184. package/dist/types.js +3 -0
  185. package/dist/types.js.map +1 -0
  186. package/package.json +95 -0
@@ -0,0 +1,138 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * SyncedOfflineEdgeProvider - React context provider for SyncedOfflineEdge.
4
+ *
5
+ * Provides the SyncedOfflineEdge instance to the component tree and handles
6
+ * initialization lifecycle including loading and error states.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * import { SyncedOfflineEdgeProvider, useSyncedOfflineEdgeClient } from '@fatagnus/dink-sync/react';
11
+ * import { MemoryPersistence } from '@fatagnus/dink-sync/persistence';
12
+ *
13
+ * function App() {
14
+ * return (
15
+ * <SyncedOfflineEdgeProvider
16
+ * config={{ serverUrl: 'nats://localhost:4222', apiKey: 'your-api-key' }}
17
+ * persistence={new MemoryPersistence()}
18
+ * syncConfig={{ tasks: 'auto', drafts: 'manual' }}
19
+ * >
20
+ * <TaskList />
21
+ * </SyncedOfflineEdgeProvider>
22
+ * );
23
+ * }
24
+ *
25
+ * function TaskList() {
26
+ * const { edge, isReady } = useSyncedOfflineEdgeClient();
27
+ * if (!isReady) return <Loading />;
28
+ *
29
+ * const tasks = edge.collection<Task>('tasks');
30
+ * // ...
31
+ * }
32
+ * ```
33
+ */
34
+ import { createContext, useContext, useState, useEffect, useRef, } from 'react';
35
+ import { createSyncedOfflineEdge } from '../client/synced-offline-edge.js';
36
+ /**
37
+ * Internal context for the SyncedOfflineEdge provider.
38
+ * Exported for use by custom hooks.
39
+ */
40
+ export const SyncedOfflineEdgeContext = createContext(null);
41
+ /**
42
+ * SyncedOfflineEdgeProvider component.
43
+ *
44
+ * Wraps your app to provide the SyncedOfflineEdge to all child components.
45
+ * Handles initialization on mount and cleanup on unmount.
46
+ */
47
+ export function SyncedOfflineEdgeProvider({ config, persistence, syncConfig, hooks, children, onError, transport, }) {
48
+ const [state, setState] = useState({
49
+ edge: null,
50
+ isLoading: true,
51
+ isReady: false,
52
+ error: null,
53
+ });
54
+ // Track the edge instance for cleanup
55
+ const edgeRef = useRef(null);
56
+ const mountedRef = useRef(true);
57
+ useEffect(() => {
58
+ mountedRef.current = true;
59
+ const initializeEdge = async () => {
60
+ try {
61
+ // Create the SyncedOfflineEdge instance
62
+ const edge = await createSyncedOfflineEdge({
63
+ persistence,
64
+ config,
65
+ syncConfig,
66
+ hooks,
67
+ transport,
68
+ });
69
+ edgeRef.current = edge;
70
+ if (mountedRef.current) {
71
+ setState({
72
+ edge,
73
+ isLoading: false,
74
+ isReady: true,
75
+ error: null,
76
+ });
77
+ }
78
+ else {
79
+ // Component unmounted during initialization, cleanup
80
+ await edge.destroy();
81
+ }
82
+ }
83
+ catch (err) {
84
+ const error = err instanceof Error ? err : new Error(String(err));
85
+ if (mountedRef.current) {
86
+ setState({
87
+ edge: null,
88
+ isLoading: false,
89
+ isReady: false,
90
+ error,
91
+ });
92
+ onError?.(error);
93
+ }
94
+ }
95
+ };
96
+ initializeEdge();
97
+ // Cleanup on unmount
98
+ return () => {
99
+ mountedRef.current = false;
100
+ if (edgeRef.current) {
101
+ edgeRef.current.destroy().catch((err) => {
102
+ console.error('Error destroying SyncedOfflineEdge:', err);
103
+ });
104
+ edgeRef.current = null;
105
+ }
106
+ };
107
+ }, [config, persistence, syncConfig, hooks, onError, transport]);
108
+ return (_jsx(SyncedOfflineEdgeContext.Provider, { value: state, children: children }));
109
+ }
110
+ /**
111
+ * Hook to access the SyncedOfflineEdge context.
112
+ *
113
+ * Returns the full context state including loading state, error, and the edge instance.
114
+ *
115
+ * @throws Error if used outside of SyncedOfflineEdgeProvider
116
+ *
117
+ * @example
118
+ * ```tsx
119
+ * function TaskList() {
120
+ * const { edge, isLoading, isReady, error } = useSyncedOfflineEdgeClient();
121
+ *
122
+ * if (isLoading) return <Loading />;
123
+ * if (error) return <Error error={error} />;
124
+ * if (!isReady) return null;
125
+ *
126
+ * const tasks = edge.collection<Task>('tasks');
127
+ * // ...
128
+ * }
129
+ * ```
130
+ */
131
+ export function useSyncedOfflineEdgeClient() {
132
+ const context = useContext(SyncedOfflineEdgeContext);
133
+ if (context === null) {
134
+ throw new Error('useSyncedOfflineEdgeClient must be used within a SyncedOfflineEdgeProvider');
135
+ }
136
+ return context;
137
+ }
138
+ //# sourceMappingURL=SyncedOfflineEdgeProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SyncedOfflineEdgeProvider.js","sourceRoot":"","sources":["../../src/react/SyncedOfflineEdgeProvider.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * React hooks for the dink offline-first sync SDK.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * import { useCollection, useSyncStatus, useConnectionState } from '@fatagnus/dink-sync/react';
7
+ *
8
+ * function TaskList() {
9
+ * const { data: tasks, isLoading } = useCollection<Task>(tasksCollection);
10
+ * const connectionState = useConnectionState(client);
11
+ *
12
+ * return (
13
+ * <div>
14
+ * <SyncIndicator state={connectionState} />
15
+ * {tasks.map(task => (
16
+ * <TaskItem key={task.id} task={task} />
17
+ * ))}
18
+ * </div>
19
+ * );
20
+ * }
21
+ *
22
+ * function TaskItem({ task }: { task: SyncedDocument<Task> }) {
23
+ * const syncStatus = useSyncStatus(task);
24
+ *
25
+ * return (
26
+ * <div className={syncStatus.isRejected ? 'error' : ''}>
27
+ * {task.title}
28
+ * {syncStatus.isPending && <Spinner />}
29
+ * {syncStatus.isRejected && <ErrorBadge error={syncStatus.error} />}
30
+ * </div>
31
+ * );
32
+ * }
33
+ * ```
34
+ */
35
+ export { useCollection, type UseCollectionResult, type UseCollectionOptions, } from './useCollection.js';
36
+ export { useSyncStatus, type SyncStatusResult, } from './useSyncStatus.js';
37
+ export { useConnectionState, type ConnectionStateResult, } from './useConnectionState.js';
38
+ export { useSyncRejected, type UseSyncRejectedOptions, } from './useSyncRejected.js';
39
+ export { useDocumentSyncStatus, type UseDocumentSyncStatusResult, } from './useDocumentSyncStatus.js';
40
+ export { usePendingChanges, type UsePendingChangesResult, } from './usePendingChanges.js';
41
+ export { useSyncProgress, type UseSyncProgressResult, type SyncProgress, } from './useSyncProgress.js';
42
+ export { useRejectedDocuments, type UseRejectedDocumentsResult, type RejectedDocument, } from './useRejectedDocuments.js';
43
+ export { OfflineEdgeProvider, useOfflineEdgeClient, type OfflineEdgeProviderProps, } from './OfflineEdgeProvider.js';
44
+ export { SyncedOfflineEdgeProvider, SyncedOfflineEdgeContext, useSyncedOfflineEdgeClient, type SyncedOfflineEdgeProviderProps, } from './SyncedOfflineEdgeProvider.js';
45
+ export { useOfflineEdge, type UseOfflineEdgeResult, } from './useOfflineEdge.js';
46
+ export { useSyncedOfflineEdge, type UseSyncedOfflineEdgeResult, } from './useSyncedOfflineEdge.js';
47
+ export { useSyncControls, type UseSyncControlsResult, } from './useSyncControls.js';
48
+ export { useCollectionSyncMode, type UseCollectionSyncModeResult, } from './useCollectionSyncMode.js';
49
+ export { SyncStatus, SyncErrorCode, ConnectionState, type SyncMetadata, type SyncError, type SyncedDocument, type SyncRejectedEvent, type ConflictInfo, type ConflictDetectedEvent, type Collection, type OfflineEdgeClient, } from '../client/types.js';
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EACL,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,aAAa,EACb,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,EAClB,KAAK,qBAAqB,GAC3B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,GAC5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,qBAAqB,EACrB,KAAK,2BAA2B,GACjC,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,iBAAiB,EACjB,KAAK,uBAAuB,GAC7B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,wBAAwB,GAC9B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,KAAK,8BAA8B,GACpC,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,cAAc,EACd,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,oBAAoB,EACpB,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,qBAAqB,EACrB,KAAK,2BAA2B,GACjC,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,EACf,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,UAAU,EACf,KAAK,iBAAiB,GACvB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * React hooks for the dink offline-first sync SDK.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * import { useCollection, useSyncStatus, useConnectionState } from '@fatagnus/dink-sync/react';
7
+ *
8
+ * function TaskList() {
9
+ * const { data: tasks, isLoading } = useCollection<Task>(tasksCollection);
10
+ * const connectionState = useConnectionState(client);
11
+ *
12
+ * return (
13
+ * <div>
14
+ * <SyncIndicator state={connectionState} />
15
+ * {tasks.map(task => (
16
+ * <TaskItem key={task.id} task={task} />
17
+ * ))}
18
+ * </div>
19
+ * );
20
+ * }
21
+ *
22
+ * function TaskItem({ task }: { task: SyncedDocument<Task> }) {
23
+ * const syncStatus = useSyncStatus(task);
24
+ *
25
+ * return (
26
+ * <div className={syncStatus.isRejected ? 'error' : ''}>
27
+ * {task.title}
28
+ * {syncStatus.isPending && <Spinner />}
29
+ * {syncStatus.isRejected && <ErrorBadge error={syncStatus.error} />}
30
+ * </div>
31
+ * );
32
+ * }
33
+ * ```
34
+ */
35
+ export { useCollection, } from './useCollection.js';
36
+ export { useSyncStatus, } from './useSyncStatus.js';
37
+ export { useConnectionState, } from './useConnectionState.js';
38
+ export { useSyncRejected, } from './useSyncRejected.js';
39
+ export { useDocumentSyncStatus, } from './useDocumentSyncStatus.js';
40
+ export { usePendingChanges, } from './usePendingChanges.js';
41
+ export { useSyncProgress, } from './useSyncProgress.js';
42
+ export { useRejectedDocuments, } from './useRejectedDocuments.js';
43
+ export { OfflineEdgeProvider, useOfflineEdgeClient, } from './OfflineEdgeProvider.js';
44
+ export { SyncedOfflineEdgeProvider, SyncedOfflineEdgeContext, useSyncedOfflineEdgeClient, } from './SyncedOfflineEdgeProvider.js';
45
+ export { useOfflineEdge, } from './useOfflineEdge.js';
46
+ export { useSyncedOfflineEdge, } from './useSyncedOfflineEdge.js';
47
+ export { useSyncControls, } from './useSyncControls.js';
48
+ export { useCollectionSyncMode, } from './useCollectionSyncMode.js';
49
+ // Re-export types that are useful for React consumers
50
+ export { SyncStatus, SyncErrorCode, ConnectionState, } from '../client/types.js';
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EACL,aAAa,GAGd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,aAAa,GAEd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,kBAAkB,GAEnB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,GAEhB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,qBAAqB,GAEtB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,iBAAiB,GAElB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,eAAe,GAGhB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,oBAAoB,GAGrB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,mBAAmB,EACnB,oBAAoB,GAErB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,GAE3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,cAAc,GAEf,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,oBAAoB,GAErB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,eAAe,GAEhB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,qBAAqB,GAEtB,MAAM,4BAA4B,CAAC;AAEpC,sDAAsD;AACtD,OAAO,EACL,UAAU,EACV,aAAa,EACb,eAAe,GAShB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * useCollection - React hook for subscribing to a collection with reactive updates.
3
+ */
4
+ import { type Collection, type SyncedDocument } from '../client/types.js';
5
+ /**
6
+ * Options for useCollection hook.
7
+ */
8
+ export interface UseCollectionOptions {
9
+ /** If true, don't subscribe to updates (useful for one-time fetches) */
10
+ readonly skip?: boolean;
11
+ }
12
+ /**
13
+ * Result of useCollection hook.
14
+ */
15
+ export interface UseCollectionResult<T extends {
16
+ id: string;
17
+ }> {
18
+ /** Array of documents with sync metadata */
19
+ readonly data: SyncedDocument<T>[];
20
+ /** True while initial data is loading */
21
+ readonly isLoading: boolean;
22
+ /** Error if initial load failed */
23
+ readonly error: Error | null;
24
+ /** Refetch data manually */
25
+ readonly refetch: () => Promise<void>;
26
+ /** Count of documents with local (unsynced) changes */
27
+ readonly pendingCount: number;
28
+ /** Count of documents with rejected sync */
29
+ readonly rejectedCount: number;
30
+ /** True if any documents have pending changes */
31
+ readonly hasPendingChanges: boolean;
32
+ /** True if any documents have rejected syncs */
33
+ readonly hasRejectedSyncs: boolean;
34
+ /** Get a specific document by ID */
35
+ readonly getById: (id: string) => SyncedDocument<T> | undefined;
36
+ }
37
+ /**
38
+ * Subscribe to a collection with reactive updates.
39
+ *
40
+ * @param collection - The collection to subscribe to
41
+ * @param options - Optional configuration
42
+ * @returns Reactive collection data with loading state and utilities
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * function TaskList() {
47
+ * const tasks = client.collection<Task>('tasks');
48
+ * const {
49
+ * data,
50
+ * isLoading,
51
+ * hasPendingChanges,
52
+ * hasRejectedSyncs,
53
+ * pendingCount
54
+ * } = useCollection(tasks);
55
+ *
56
+ * if (isLoading) return <Spinner />;
57
+ *
58
+ * return (
59
+ * <div>
60
+ * {hasPendingChanges && (
61
+ * <Banner>Syncing {pendingCount} changes...</Banner>
62
+ * )}
63
+ * {hasRejectedSyncs && (
64
+ * <Banner variant="error">Some changes failed to sync</Banner>
65
+ * )}
66
+ * {data.map(task => (
67
+ * <TaskItem key={task.id} task={task} />
68
+ * ))}
69
+ * </div>
70
+ * );
71
+ * }
72
+ * ```
73
+ */
74
+ export declare function useCollection<T extends {
75
+ id: string;
76
+ }>(collection: Collection<T>, options?: UseCollectionOptions): UseCollectionResult<T>;
77
+ //# sourceMappingURL=useCollection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCollection.d.ts","sourceRoot":"","sources":["../../src/react/useCollection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAc,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEtF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;IAC3D,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IACnC,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B,4BAA4B;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,uDAAuD;IACvD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,iDAAiD;IACjD,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,gDAAgD;IAChD,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACpD,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,mBAAmB,CAAC,CAAC,CAAC,CA+ExB"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * useCollection - React hook for subscribing to a collection with reactive updates.
3
+ */
4
+ import { useState, useEffect, useCallback, useMemo } from 'react';
5
+ import { SyncStatus } from '../client/types.js';
6
+ /**
7
+ * Subscribe to a collection with reactive updates.
8
+ *
9
+ * @param collection - The collection to subscribe to
10
+ * @param options - Optional configuration
11
+ * @returns Reactive collection data with loading state and utilities
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * function TaskList() {
16
+ * const tasks = client.collection<Task>('tasks');
17
+ * const {
18
+ * data,
19
+ * isLoading,
20
+ * hasPendingChanges,
21
+ * hasRejectedSyncs,
22
+ * pendingCount
23
+ * } = useCollection(tasks);
24
+ *
25
+ * if (isLoading) return <Spinner />;
26
+ *
27
+ * return (
28
+ * <div>
29
+ * {hasPendingChanges && (
30
+ * <Banner>Syncing {pendingCount} changes...</Banner>
31
+ * )}
32
+ * {hasRejectedSyncs && (
33
+ * <Banner variant="error">Some changes failed to sync</Banner>
34
+ * )}
35
+ * {data.map(task => (
36
+ * <TaskItem key={task.id} task={task} />
37
+ * ))}
38
+ * </div>
39
+ * );
40
+ * }
41
+ * ```
42
+ */
43
+ export function useCollection(collection, options) {
44
+ const [data, setData] = useState([]);
45
+ const [isLoading, setIsLoading] = useState(true);
46
+ const [error, setError] = useState(null);
47
+ const skip = options?.skip ?? false;
48
+ // Subscribe to collection changes
49
+ useEffect(() => {
50
+ if (skip) {
51
+ setIsLoading(false);
52
+ return;
53
+ }
54
+ let isMounted = true;
55
+ const unsubscribe = collection.subscribe((docs) => {
56
+ if (isMounted) {
57
+ setData(docs);
58
+ setIsLoading(false);
59
+ setError(null);
60
+ }
61
+ });
62
+ return () => {
63
+ isMounted = false;
64
+ unsubscribe();
65
+ };
66
+ }, [collection, skip]);
67
+ // Refetch function
68
+ const refetch = useCallback(async () => {
69
+ try {
70
+ setIsLoading(true);
71
+ const docs = await collection.list();
72
+ setData(docs);
73
+ setError(null);
74
+ }
75
+ catch (err) {
76
+ setError(err instanceof Error ? err : new Error(String(err)));
77
+ }
78
+ finally {
79
+ setIsLoading(false);
80
+ }
81
+ }, [collection]);
82
+ // Compute derived values
83
+ const { pendingCount, rejectedCount } = useMemo(() => {
84
+ let pending = 0;
85
+ let rejected = 0;
86
+ for (const doc of data) {
87
+ const status = doc._sync?.status;
88
+ if (status === SyncStatus.Local || status === SyncStatus.Syncing) {
89
+ pending++;
90
+ }
91
+ if (status === SyncStatus.Rejected) {
92
+ rejected++;
93
+ }
94
+ }
95
+ return { pendingCount: pending, rejectedCount: rejected };
96
+ }, [data]);
97
+ // Get document by ID
98
+ const getById = useCallback((id) => {
99
+ return data.find(doc => doc.id === id);
100
+ }, [data]);
101
+ return {
102
+ data,
103
+ isLoading,
104
+ error,
105
+ refetch,
106
+ pendingCount,
107
+ rejectedCount,
108
+ hasPendingChanges: pendingCount > 0,
109
+ hasRejectedSyncs: rejectedCount > 0,
110
+ getById,
111
+ };
112
+ }
113
+ //# sourceMappingURL=useCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCollection.js","sourceRoot":"","sources":["../../src/react/useCollection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,UAAU,EAAwC,MAAM,oBAAoB,CAAC;AAkCtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,aAAa,CAC3B,UAAyB,EACzB,OAA8B;IAE9B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAsB,EAAE,CAAC,CAAC;IAC1D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEvD,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,KAAK,CAAC;IAEpC,kCAAkC;IAClC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,EAAE,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QAED,IAAI,SAAS,GAAG,IAAI,CAAC;QAErB,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE;YAChD,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,CAAC;gBACd,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,QAAQ,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,KAAK,CAAC;YAClB,WAAW,EAAE,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;IAEvB,mBAAmB;IACnB,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,IAAI,CAAC;YACH,YAAY,CAAC,IAAI,CAAC,CAAC;YACnB,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChE,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,yBAAyB;IACzB,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;QACnD,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC;YACjC,IAAI,MAAM,KAAK,UAAU,CAAC,KAAK,IAAI,MAAM,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;gBACjE,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,IAAI,MAAM,KAAK,UAAU,CAAC,QAAQ,EAAE,CAAC;gBACnC,QAAQ,EAAE,CAAC;YACb,CAAC;QACH,CAAC;QACD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;IAC5D,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,qBAAqB;IACrB,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,EAAU,EAAiC,EAAE;QAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACzC,CAAC,EACD,CAAC,IAAI,CAAC,CACP,CAAC;IAEF,OAAO;QACL,IAAI;QACJ,SAAS;QACT,KAAK;QACL,OAAO;QACP,YAAY;QACZ,aAAa;QACb,iBAAiB,EAAE,YAAY,GAAG,CAAC;QACnC,gBAAgB,EAAE,aAAa,GAAG,CAAC;QACnC,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * useCollectionSyncMode - React hook for getting and setting sync mode for a collection.
3
+ *
4
+ * Provides a useState-like API for controlling per-collection sync behavior.
5
+ *
6
+ * Must be used within a SyncedOfflineEdgeProvider.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * import { useCollectionSyncMode } from '@fatagnus/dink-sync/react';
11
+ *
12
+ * function SyncModeSelector({ collection }: { collection: string }) {
13
+ * const [mode, setMode] = useCollectionSyncMode(collection);
14
+ *
15
+ * return (
16
+ * <select value={mode} onChange={(e) => setMode(e.target.value as SyncMode)}>
17
+ * <option value="auto">Auto</option>
18
+ * <option value="manual">Manual</option>
19
+ * <option value="disabled">Disabled</option>
20
+ * </select>
21
+ * );
22
+ * }
23
+ * ```
24
+ */
25
+ import type { SyncMode } from '../client/synced-offline-edge-types.js';
26
+ /**
27
+ * Result of useCollectionSyncMode hook.
28
+ * A tuple similar to useState: [currentMode, setMode]
29
+ */
30
+ export type UseCollectionSyncModeResult = [SyncMode, (mode: SyncMode) => void];
31
+ /**
32
+ * Hook to get and set sync mode for a specific collection.
33
+ *
34
+ * Returns a tuple [mode, setMode] similar to useState, where:
35
+ * - mode: The current sync mode for the collection ('auto' | 'manual' | 'disabled')
36
+ * - setMode: Function to update the sync mode
37
+ *
38
+ * The mode is reactive and will trigger re-renders when the sync mode changes,
39
+ * whether changed through this hook or via the edge directly.
40
+ *
41
+ * @param collection - The name of the collection
42
+ * @throws Error if used outside of SyncedOfflineEdgeProvider
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * function TaskSyncSettings() {
47
+ * const [mode, setMode] = useCollectionSyncMode('tasks');
48
+ *
49
+ * return (
50
+ * <div>
51
+ * <p>Current mode: {mode}</p>
52
+ * <button onClick={() => setMode('manual')}>Switch to Manual</button>
53
+ * <button onClick={() => setMode('auto')}>Switch to Auto</button>
54
+ * <button onClick={() => setMode('disabled')}>Disable Sync</button>
55
+ * </div>
56
+ * );
57
+ * }
58
+ * ```
59
+ */
60
+ export declare function useCollectionSyncMode(collection: string): UseCollectionSyncModeResult;
61
+ //# sourceMappingURL=useCollectionSyncMode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCollectionSyncMode.d.ts","sourceRoot":"","sources":["../../src/react/useCollectionSyncMode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AAEvE;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,2BAA2B,CA6CrF"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * useCollectionSyncMode - React hook for getting and setting sync mode for a collection.
3
+ *
4
+ * Provides a useState-like API for controlling per-collection sync behavior.
5
+ *
6
+ * Must be used within a SyncedOfflineEdgeProvider.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * import { useCollectionSyncMode } from '@fatagnus/dink-sync/react';
11
+ *
12
+ * function SyncModeSelector({ collection }: { collection: string }) {
13
+ * const [mode, setMode] = useCollectionSyncMode(collection);
14
+ *
15
+ * return (
16
+ * <select value={mode} onChange={(e) => setMode(e.target.value as SyncMode)}>
17
+ * <option value="auto">Auto</option>
18
+ * <option value="manual">Manual</option>
19
+ * <option value="disabled">Disabled</option>
20
+ * </select>
21
+ * );
22
+ * }
23
+ * ```
24
+ */
25
+ import { useContext, useState, useCallback, useEffect, useRef } from 'react';
26
+ import { SyncedOfflineEdgeContext } from './SyncedOfflineEdgeProvider.js';
27
+ /**
28
+ * Hook to get and set sync mode for a specific collection.
29
+ *
30
+ * Returns a tuple [mode, setMode] similar to useState, where:
31
+ * - mode: The current sync mode for the collection ('auto' | 'manual' | 'disabled')
32
+ * - setMode: Function to update the sync mode
33
+ *
34
+ * The mode is reactive and will trigger re-renders when the sync mode changes,
35
+ * whether changed through this hook or via the edge directly.
36
+ *
37
+ * @param collection - The name of the collection
38
+ * @throws Error if used outside of SyncedOfflineEdgeProvider
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * function TaskSyncSettings() {
43
+ * const [mode, setMode] = useCollectionSyncMode('tasks');
44
+ *
45
+ * return (
46
+ * <div>
47
+ * <p>Current mode: {mode}</p>
48
+ * <button onClick={() => setMode('manual')}>Switch to Manual</button>
49
+ * <button onClick={() => setMode('auto')}>Switch to Auto</button>
50
+ * <button onClick={() => setMode('disabled')}>Disable Sync</button>
51
+ * </div>
52
+ * );
53
+ * }
54
+ * ```
55
+ */
56
+ export function useCollectionSyncMode(collection) {
57
+ const context = useContext(SyncedOfflineEdgeContext);
58
+ if (context === null) {
59
+ throw new Error('useCollectionSyncMode must be used within a SyncedOfflineEdgeProvider');
60
+ }
61
+ const { edge } = context;
62
+ // Track the current mode reactively
63
+ const [mode, setModeState] = useState(() => edge?.getSyncMode(collection) ?? 'auto');
64
+ // Store edge reference for stable callbacks
65
+ const edgeRef = useRef(edge);
66
+ edgeRef.current = edge;
67
+ // Update mode when edge becomes available or collection changes
68
+ useEffect(() => {
69
+ if (edge) {
70
+ setModeState(edge.getSyncMode(collection));
71
+ }
72
+ }, [edge, collection]);
73
+ // Subscribe to sync mode changes
74
+ useEffect(() => {
75
+ if (!edge)
76
+ return;
77
+ const unsubscribe = edge.onSyncModeChange((changedCollection, newMode) => {
78
+ if (changedCollection === collection) {
79
+ setModeState(newMode);
80
+ }
81
+ });
82
+ return unsubscribe;
83
+ }, [edge, collection]);
84
+ // Stable setMode callback that updates both edge and local state
85
+ const setMode = useCallback((newMode) => {
86
+ if (edgeRef.current) {
87
+ edgeRef.current.setSyncMode(collection, newMode);
88
+ // State will be updated via the onSyncModeChange subscription
89
+ }
90
+ }, [collection]);
91
+ return [mode, setMode];
92
+ }
93
+ //# sourceMappingURL=useCollectionSyncMode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCollectionSyncMode.js","sourceRoot":"","sources":["../../src/react/useCollectionSyncMode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAS1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAAkB;IACtD,MAAM,OAAO,GAAG,UAAU,CAAC,wBAAwB,CAAC,CAAC;IAErD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC3F,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAEzB,oCAAoC;IACpC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAW,GAAG,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,CAAC;IAE/F,4CAA4C;IAC5C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAEvB,gEAAgE;IAChE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,EAAE,CAAC;YACT,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAEvB,iCAAiC;IACjC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,iBAAiB,EAAE,OAAO,EAAE,EAAE;YACvE,IAAI,iBAAiB,KAAK,UAAU,EAAE,CAAC;gBACrC,YAAY,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC;IACrB,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAEvB,iEAAiE;IACjE,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,OAAiB,EAAE,EAAE;QAChD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACjD,8DAA8D;QAChE,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * useConnectionState - React hook for tracking connection state.
3
+ */
4
+ import { ConnectionState, type OfflineEdgeClient, type Signal } from '../client/types.js';
5
+ /**
6
+ * Result of useConnectionState with derived boolean flags.
7
+ */
8
+ export interface ConnectionStateResult {
9
+ /** Current connection state */
10
+ readonly state: ConnectionState;
11
+ /** True if fully offline */
12
+ readonly isOffline: boolean;
13
+ /** True if attempting to connect */
14
+ readonly isConnecting: boolean;
15
+ /** True if connected and online */
16
+ readonly isOnline: boolean;
17
+ /** True if reconnecting after disconnect */
18
+ readonly isReconnecting: boolean;
19
+ /** True if in any connecting state (connecting or reconnecting) */
20
+ readonly isConnectingAny: boolean;
21
+ }
22
+ /**
23
+ * Subscribe to connection state changes from an OfflineEdgeClient.
24
+ *
25
+ * @param client - The offline edge client
26
+ * @returns Connection state with convenience boolean flags
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * function SyncIndicator() {
31
+ * const { isOnline, isConnecting, isOffline } = useConnectionState(client);
32
+ *
33
+ * if (isOnline) {
34
+ * return <Badge color="green">Online</Badge>;
35
+ * }
36
+ * if (isConnecting) {
37
+ * return <Badge color="yellow"><Spinner /> Connecting...</Badge>;
38
+ * }
39
+ * return <Badge color="gray">Offline</Badge>;
40
+ * }
41
+ * ```
42
+ */
43
+ export declare function useConnectionState(client: OfflineEdgeClient | Signal<ConnectionState>): ConnectionStateResult;
44
+ //# sourceMappingURL=useConnectionState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConnectionState.d.ts","sourceRoot":"","sources":["../../src/react/useConnectionState.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,4BAA4B;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,4CAA4C;IAC5C,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,mEAAmE;IACnE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAAC,eAAe,CAAC,GAClD,qBAAqB,CAsBvB"}