@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,213 @@
1
+ /**
2
+ * useRejectedDocuments - React hook to track and manage rejected sync documents.
3
+ *
4
+ * Provides real-time tracking of all documents that failed to sync,
5
+ * enabling sync error resolution UI.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { useRejectedDocuments } from '@fatagnus/dink-sync/react';
10
+ *
11
+ * function SyncErrorList() {
12
+ * const { rejectedDocuments, retryDocument, discardDocument } = useRejectedDocuments();
13
+ *
14
+ * if (rejectedDocuments.length === 0) {
15
+ * return <span>✓ All synced</span>;
16
+ * }
17
+ *
18
+ * return (
19
+ * <ul>
20
+ * {rejectedDocuments.map((doc) => (
21
+ * <li key={`${doc.collection}-${doc.docId}`}>
22
+ * <span>{doc.collection}/{doc.docId}: {doc.errorMessage}</span>
23
+ * <button onClick={() => retryDocument(doc.collection, doc.docId)}>
24
+ * Retry
25
+ * </button>
26
+ * <button onClick={() => discardDocument(doc.collection, doc.docId)}>
27
+ * Discard
28
+ * </button>
29
+ * </li>
30
+ * ))}
31
+ * </ul>
32
+ * );
33
+ * }
34
+ * ```
35
+ */
36
+ import { useState, useEffect, useContext, useCallback, useRef } from 'react';
37
+ import { OfflineEdgeContext } from './OfflineEdgeProvider.js';
38
+ import { SyncStatus } from '../client/types.js';
39
+ /**
40
+ * Hook to track and manage rejected sync documents.
41
+ *
42
+ * Provides:
43
+ * - Array of all rejected documents with error details
44
+ * - Helper function to retry syncing a document
45
+ * - Helper function to discard a document
46
+ *
47
+ * @throws Error if used outside of OfflineEdgeProvider
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * function SyncErrorBanner() {
52
+ * const { rejectedDocuments, retryDocument } = useRejectedDocuments();
53
+ *
54
+ * if (rejectedDocuments.length === 0) return null;
55
+ *
56
+ * return (
57
+ * <div className="error-banner">
58
+ * {rejectedDocuments.length} sync errors
59
+ * <button onClick={() => {
60
+ * rejectedDocuments.forEach(doc =>
61
+ * retryDocument(doc.collection, doc.docId)
62
+ * );
63
+ * }}>
64
+ * Retry All
65
+ * </button>
66
+ * </div>
67
+ * );
68
+ * }
69
+ * ```
70
+ */
71
+ export function useRejectedDocuments() {
72
+ const context = useContext(OfflineEdgeContext);
73
+ if (context === null) {
74
+ throw new Error('useRejectedDocuments must be used within an OfflineEdgeProvider');
75
+ }
76
+ const [rejectedDocuments, setRejectedDocuments] = useState([]);
77
+ // Track unsubscribe functions
78
+ const unsubscribesRef = useRef(new Map());
79
+ // Scan for rejected documents in a collection
80
+ const scanCollection = useCallback(async (collectionName) => {
81
+ if (!context.client)
82
+ return;
83
+ const collection = context.client.collection(collectionName);
84
+ const docs = await collection.list();
85
+ const rejected = [];
86
+ for (const doc of docs) {
87
+ if (doc._sync.status === SyncStatus.Rejected && doc._sync.error) {
88
+ rejected.push({
89
+ collection: collectionName,
90
+ docId: doc.id,
91
+ errorCode: doc._sync.error.code,
92
+ errorMessage: doc._sync.error.message,
93
+ timestamp: doc._sync.lastSyncAttempt ?? Date.now(),
94
+ field: doc._sync.error.field,
95
+ });
96
+ }
97
+ }
98
+ return rejected;
99
+ }, [context.client]);
100
+ // Subscribe to a collection for rejection events
101
+ const subscribeToCollection = useCallback((collectionName) => {
102
+ if (!context.client || unsubscribesRef.current.has(collectionName))
103
+ return;
104
+ const collection = context.client.collection(collectionName);
105
+ // Subscribe to rejection events
106
+ const unsubscribeRejected = collection.onSyncRejected((event) => {
107
+ setRejectedDocuments(prev => {
108
+ // Check if already exists
109
+ const existingIndex = prev.findIndex(d => d.collection === collectionName && d.docId === event.documentId);
110
+ const newRejected = {
111
+ collection: collectionName,
112
+ docId: event.documentId,
113
+ errorCode: event.error.code,
114
+ errorMessage: event.error.message,
115
+ timestamp: Date.now(),
116
+ field: event.error.field,
117
+ };
118
+ if (existingIndex >= 0) {
119
+ // Update existing
120
+ const updated = [...prev];
121
+ updated[existingIndex] = newRejected;
122
+ return updated;
123
+ }
124
+ else {
125
+ // Add new
126
+ return [...prev, newRejected];
127
+ }
128
+ });
129
+ });
130
+ // Subscribe to collection changes to detect when documents are retried/deleted
131
+ const unsubscribeChanges = collection.subscribe((docs) => {
132
+ setRejectedDocuments(prev => {
133
+ // Filter out documents that are no longer rejected
134
+ const docMap = new Map(docs.map(d => [d.id, d]));
135
+ return prev.filter(rejected => {
136
+ if (rejected.collection !== collectionName)
137
+ return true;
138
+ const doc = docMap.get(rejected.docId);
139
+ // Keep if doc still exists and is still rejected
140
+ return doc && doc._sync.status === SyncStatus.Rejected;
141
+ });
142
+ });
143
+ });
144
+ // Store combined unsubscribe
145
+ unsubscribesRef.current.set(collectionName, () => {
146
+ unsubscribeRejected();
147
+ unsubscribeChanges();
148
+ });
149
+ }, [context.client]);
150
+ // Initial scan and subscription setup
151
+ useEffect(() => {
152
+ if (!context.isReady || !context.client) {
153
+ setRejectedDocuments([]);
154
+ return;
155
+ }
156
+ // Note: Since OfflineEdgeClient doesn't expose a list of all collections,
157
+ // we'll need to track collections as they're used. For now, we start empty
158
+ // and collections get registered when retryDocument/discardDocument are called
159
+ // or when the hook user accesses collections through the client.
160
+ // We could also scan persistence for known collections, but the current
161
+ // implementation relies on collection subscription for real-time updates.
162
+ return () => {
163
+ // Cleanup all subscriptions
164
+ unsubscribesRef.current.forEach(unsub => unsub());
165
+ unsubscribesRef.current.clear();
166
+ };
167
+ }, [context.isReady, context.client]);
168
+ // Retry a document
169
+ const retryDocument = useCallback(async (collectionName, docId) => {
170
+ if (!context.client)
171
+ return;
172
+ // Ensure we're subscribed to this collection
173
+ subscribeToCollection(collectionName);
174
+ const collection = context.client.collection(collectionName);
175
+ await collection.retrySync(docId);
176
+ // Remove from rejected list immediately (subscription will handle if it fails again)
177
+ setRejectedDocuments(prev => prev.filter(d => !(d.collection === collectionName && d.docId === docId)));
178
+ }, [context.client, subscribeToCollection]);
179
+ // Discard a document
180
+ const discardDocument = useCallback(async (collectionName, docId) => {
181
+ if (!context.client)
182
+ return;
183
+ // Ensure we're subscribed to this collection
184
+ subscribeToCollection(collectionName);
185
+ const collection = context.client.collection(collectionName);
186
+ await collection.delete(docId);
187
+ // Remove from rejected list
188
+ setRejectedDocuments(prev => prev.filter(d => !(d.collection === collectionName && d.docId === docId)));
189
+ }, [context.client, subscribeToCollection]);
190
+ // Register a collection to track for rejected documents
191
+ const registerCollection = useCallback(async (collectionName) => {
192
+ if (!context.client)
193
+ return;
194
+ subscribeToCollection(collectionName);
195
+ // Scan for existing rejected documents
196
+ const rejected = await scanCollection(collectionName);
197
+ if (rejected && rejected.length > 0) {
198
+ setRejectedDocuments(prev => {
199
+ // Merge with existing, avoiding duplicates
200
+ const existingKeys = new Set(prev.map(d => `${d.collection}-${d.docId}`));
201
+ const newRejected = rejected.filter(r => !existingKeys.has(`${r.collection}-${r.docId}`));
202
+ return [...prev, ...newRejected];
203
+ });
204
+ }
205
+ }, [context.client, subscribeToCollection, scanCollection]);
206
+ return {
207
+ rejectedDocuments,
208
+ retryDocument,
209
+ discardDocument,
210
+ registerCollection,
211
+ };
212
+ }
213
+ //# sourceMappingURL=useRejectedDocuments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRejectedDocuments.js","sourceRoot":"","sources":["../../src/react/useRejectedDocuments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * useSyncControls - React hook for controlling sync operations.
3
+ *
4
+ * Provides a focused subset of sync control functions for common use cases:
5
+ * pausing, resuming, and flushing sync operations.
6
+ *
7
+ * Must be used within a SyncedOfflineEdgeProvider.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * import { useSyncControls } from '@fatagnus/dink-sync/react';
12
+ *
13
+ * function SyncControlPanel() {
14
+ * const { pause, resume, flush, isPaused } = useSyncControls();
15
+ *
16
+ * return (
17
+ * <div>
18
+ * <span>Sync is {isPaused ? 'paused' : 'active'}</span>
19
+ * <button onClick={isPaused ? resume : pause}>
20
+ * {isPaused ? 'Resume' : 'Pause'}
21
+ * </button>
22
+ * <button onClick={() => flush()}>Flush All</button>
23
+ * <button onClick={() => flush('tasks')}>Flush Tasks</button>
24
+ * </div>
25
+ * );
26
+ * }
27
+ * ```
28
+ */
29
+ /**
30
+ * Result of useSyncControls hook.
31
+ */
32
+ export interface UseSyncControlsResult {
33
+ /**
34
+ * Pause all automatic sync operations.
35
+ * Changes continue to be queued locally but won't be sent to the server.
36
+ * Manual flush() calls still work while paused.
37
+ */
38
+ pause: () => void;
39
+ /**
40
+ * Resume automatic sync operations after pausing.
41
+ * Triggers immediate flush of pending changes for auto-mode collections.
42
+ */
43
+ resume: () => void;
44
+ /**
45
+ * Immediately sync pending changes.
46
+ * @param collection - Optional collection name. If provided, syncs only that collection.
47
+ * If omitted, syncs all collections with pending changes.
48
+ * @returns Promise that resolves when sync completes
49
+ */
50
+ flush: (collection?: string) => Promise<void>;
51
+ /**
52
+ * Whether automatic sync is currently paused.
53
+ * This value is reactive and will trigger re-renders when pause/resume
54
+ * are called through this hook. For the most accurate state when multiple
55
+ * components control sync, use `edge.isPaused()` from useSyncedOfflineEdge.
56
+ */
57
+ isPaused: boolean;
58
+ }
59
+ /**
60
+ * Hook to access sync control functions.
61
+ *
62
+ * Provides a convenient subset of SyncedOfflineEdge functionality
63
+ * for controlling sync operations (pause, resume, flush).
64
+ *
65
+ * @throws Error if used outside of SyncedOfflineEdgeProvider
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * function BatchOperations() {
70
+ * const { pause, resume, flush, isPaused } = useSyncControls();
71
+ *
72
+ * const handleBatchImport = async (items: Item[]) => {
73
+ * // Pause sync during batch operations
74
+ * pause();
75
+ *
76
+ * try {
77
+ * for (const item of items) {
78
+ * await insertItem(item);
79
+ * }
80
+ * // Flush all changes at once
81
+ * await flush();
82
+ * } finally {
83
+ * resume();
84
+ * }
85
+ * };
86
+ *
87
+ * return (
88
+ * <div>
89
+ * <ImportButton onImport={handleBatchImport} disabled={isPaused} />
90
+ * </div>
91
+ * );
92
+ * }
93
+ * ```
94
+ */
95
+ export declare function useSyncControls(): UseSyncControlsResult;
96
+ //# sourceMappingURL=useSyncControls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSyncControls.d.ts","sourceRoot":"","sources":["../../src/react/useSyncControls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB;;;OAGG;IACH,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB;;;;;OAKG;IACH,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,eAAe,IAAI,qBAAqB,CAqDvD"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * useSyncControls - React hook for controlling sync operations.
3
+ *
4
+ * Provides a focused subset of sync control functions for common use cases:
5
+ * pausing, resuming, and flushing sync operations.
6
+ *
7
+ * Must be used within a SyncedOfflineEdgeProvider.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * import { useSyncControls } from '@fatagnus/dink-sync/react';
12
+ *
13
+ * function SyncControlPanel() {
14
+ * const { pause, resume, flush, isPaused } = useSyncControls();
15
+ *
16
+ * return (
17
+ * <div>
18
+ * <span>Sync is {isPaused ? 'paused' : 'active'}</span>
19
+ * <button onClick={isPaused ? resume : pause}>
20
+ * {isPaused ? 'Resume' : 'Pause'}
21
+ * </button>
22
+ * <button onClick={() => flush()}>Flush All</button>
23
+ * <button onClick={() => flush('tasks')}>Flush Tasks</button>
24
+ * </div>
25
+ * );
26
+ * }
27
+ * ```
28
+ */
29
+ import { useContext, useState, useCallback, useEffect, useRef } from 'react';
30
+ import { SyncedOfflineEdgeContext } from './SyncedOfflineEdgeProvider.js';
31
+ /**
32
+ * Hook to access sync control functions.
33
+ *
34
+ * Provides a convenient subset of SyncedOfflineEdge functionality
35
+ * for controlling sync operations (pause, resume, flush).
36
+ *
37
+ * @throws Error if used outside of SyncedOfflineEdgeProvider
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * function BatchOperations() {
42
+ * const { pause, resume, flush, isPaused } = useSyncControls();
43
+ *
44
+ * const handleBatchImport = async (items: Item[]) => {
45
+ * // Pause sync during batch operations
46
+ * pause();
47
+ *
48
+ * try {
49
+ * for (const item of items) {
50
+ * await insertItem(item);
51
+ * }
52
+ * // Flush all changes at once
53
+ * await flush();
54
+ * } finally {
55
+ * resume();
56
+ * }
57
+ * };
58
+ *
59
+ * return (
60
+ * <div>
61
+ * <ImportButton onImport={handleBatchImport} disabled={isPaused} />
62
+ * </div>
63
+ * );
64
+ * }
65
+ * ```
66
+ */
67
+ export function useSyncControls() {
68
+ const context = useContext(SyncedOfflineEdgeContext);
69
+ if (context === null) {
70
+ throw new Error('useSyncControls must be used within a SyncedOfflineEdgeProvider');
71
+ }
72
+ const { edge } = context;
73
+ // Track isPaused state reactively
74
+ const [isPaused, setIsPaused] = useState(() => edge?.isPaused() ?? false);
75
+ // Store edge reference for stable callbacks
76
+ const edgeRef = useRef(edge);
77
+ edgeRef.current = edge;
78
+ // Update isPaused when edge becomes available
79
+ useEffect(() => {
80
+ if (edge) {
81
+ setIsPaused(edge.isPaused());
82
+ }
83
+ }, [edge]);
84
+ // Stable pause callback that updates local state
85
+ const pause = useCallback(() => {
86
+ if (edgeRef.current) {
87
+ edgeRef.current.pause();
88
+ setIsPaused(true);
89
+ }
90
+ }, []);
91
+ // Stable resume callback that updates local state
92
+ const resume = useCallback(() => {
93
+ if (edgeRef.current) {
94
+ edgeRef.current.resume();
95
+ setIsPaused(false);
96
+ }
97
+ }, []);
98
+ // Stable flush callback
99
+ const flush = useCallback((collection) => {
100
+ if (edgeRef.current) {
101
+ return edgeRef.current.flush(collection);
102
+ }
103
+ return Promise.resolve();
104
+ }, []);
105
+ return {
106
+ pause,
107
+ resume,
108
+ flush,
109
+ isPaused,
110
+ };
111
+ }
112
+ //# sourceMappingURL=useSyncControls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSyncControls.js","sourceRoot":"","sources":["../../src/react/useSyncControls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAoC1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,UAAU,CAAC,wBAAwB,CAAC,CAAC;IAErD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAEzB,kCAAkC;IAClC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,KAAK,CAAC,CAAC;IAE1E,4CAA4C;IAC5C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAEvB,8CAA8C;IAC9C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,EAAE,CAAC;YACT,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,iDAAiD;IACjD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACxB,WAAW,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,kDAAkD;IAClD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QAC9B,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACzB,WAAW,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,wBAAwB;IACxB,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,UAAmB,EAAiB,EAAE;QAC/D,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO;QACL,KAAK;QACL,MAAM;QACN,KAAK;QACL,QAAQ;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * useSyncProgress - React hook to track sync progress.
3
+ *
4
+ * Provides real-time sync progress information for showing progress bars
5
+ * during initial sync or large batch syncs.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { useSyncProgress } from '@fatagnus/dink-sync/react';
10
+ *
11
+ * function SyncProgressBar() {
12
+ * const { progress, currentDocument, estimatedTimeRemaining } = useSyncProgress();
13
+ *
14
+ * if (progress.total === 0) {
15
+ * return <span>✓ All synced</span>;
16
+ * }
17
+ *
18
+ * return (
19
+ * <div>
20
+ * <progress value={progress.completed} max={progress.total} />
21
+ * <span>{progress.percent}% complete</span>
22
+ * {currentDocument && <span>Syncing: {currentDocument}</span>}
23
+ * {estimatedTimeRemaining && (
24
+ * <span>ETA: {Math.ceil(estimatedTimeRemaining / 1000)}s</span>
25
+ * )}
26
+ * </div>
27
+ * );
28
+ * }
29
+ * ```
30
+ */
31
+ /**
32
+ * Progress information for sync operations.
33
+ */
34
+ export interface SyncProgress {
35
+ /** Total number of items to sync */
36
+ total: number;
37
+ /** Number of items completed */
38
+ completed: number;
39
+ /** Percentage complete (0-100) */
40
+ percent: number;
41
+ }
42
+ /**
43
+ * Result of useSyncProgress hook.
44
+ */
45
+ export interface UseSyncProgressResult {
46
+ /** Progress information */
47
+ progress: SyncProgress;
48
+ /** Current document being synced, or null if none */
49
+ currentDocument: string | null;
50
+ /** Estimated time remaining in milliseconds, or null if unknown */
51
+ estimatedTimeRemaining: number | null;
52
+ }
53
+ /**
54
+ * Hook to track sync progress for UI indicators.
55
+ *
56
+ * Provides:
57
+ * - Progress counts (total, completed, percent)
58
+ * - Currently syncing document identifier
59
+ * - Estimated time remaining
60
+ *
61
+ * @throws Error if used outside of OfflineEdgeProvider
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * function SyncStatus() {
66
+ * const { progress, currentDocument } = useSyncProgress();
67
+ *
68
+ * return (
69
+ * <div>
70
+ * <span>{progress.completed}/{progress.total}</span>
71
+ * {currentDocument && <span>Syncing: {currentDocument}</span>}
72
+ * </div>
73
+ * );
74
+ * }
75
+ * ```
76
+ */
77
+ export declare function useSyncProgress(): UseSyncProgressResult;
78
+ //# sourceMappingURL=useSyncProgress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSyncProgress.d.ts","sourceRoot":"","sources":["../../src/react/useSyncProgress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,2BAA2B;IAC3B,QAAQ,EAAE,YAAY,CAAC;IACvB,qDAAqD;IACrD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mEAAmE;IACnE,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,IAAI,qBAAqB,CAsCvD"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * useSyncProgress - React hook to track sync progress.
3
+ *
4
+ * Provides real-time sync progress information for showing progress bars
5
+ * during initial sync or large batch syncs.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { useSyncProgress } from '@fatagnus/dink-sync/react';
10
+ *
11
+ * function SyncProgressBar() {
12
+ * const { progress, currentDocument, estimatedTimeRemaining } = useSyncProgress();
13
+ *
14
+ * if (progress.total === 0) {
15
+ * return <span>✓ All synced</span>;
16
+ * }
17
+ *
18
+ * return (
19
+ * <div>
20
+ * <progress value={progress.completed} max={progress.total} />
21
+ * <span>{progress.percent}% complete</span>
22
+ * {currentDocument && <span>Syncing: {currentDocument}</span>}
23
+ * {estimatedTimeRemaining && (
24
+ * <span>ETA: {Math.ceil(estimatedTimeRemaining / 1000)}s</span>
25
+ * )}
26
+ * </div>
27
+ * );
28
+ * }
29
+ * ```
30
+ */
31
+ import { useState, useEffect, useContext } from 'react';
32
+ import { OfflineEdgeContext } from './OfflineEdgeProvider.js';
33
+ /**
34
+ * Hook to track sync progress for UI indicators.
35
+ *
36
+ * Provides:
37
+ * - Progress counts (total, completed, percent)
38
+ * - Currently syncing document identifier
39
+ * - Estimated time remaining
40
+ *
41
+ * @throws Error if used outside of OfflineEdgeProvider
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * function SyncStatus() {
46
+ * const { progress, currentDocument } = useSyncProgress();
47
+ *
48
+ * return (
49
+ * <div>
50
+ * <span>{progress.completed}/{progress.total}</span>
51
+ * {currentDocument && <span>Syncing: {currentDocument}</span>}
52
+ * </div>
53
+ * );
54
+ * }
55
+ * ```
56
+ */
57
+ export function useSyncProgress() {
58
+ const context = useContext(OfflineEdgeContext);
59
+ if (context === null) {
60
+ throw new Error('useSyncProgress must be used within an OfflineEdgeProvider');
61
+ }
62
+ const [progress, setProgress] = useState({
63
+ total: 0,
64
+ completed: 0,
65
+ percent: 0,
66
+ });
67
+ const [currentDocument, setCurrentDocument] = useState(null);
68
+ const [estimatedTimeRemaining, setEstimatedTimeRemaining] = useState(null);
69
+ useEffect(() => {
70
+ // If client is not ready yet, return default values
71
+ if (!context.isReady || !context.client) {
72
+ setProgress({ total: 0, completed: 0, percent: 0 });
73
+ setCurrentDocument(null);
74
+ setEstimatedTimeRemaining(null);
75
+ return;
76
+ }
77
+ // Default: no sync progress tracked at client level
78
+ // A full implementation would subscribe to sync engine events
79
+ // to track progress of batch syncs
80
+ setProgress({ total: 0, completed: 0, percent: 0 });
81
+ setCurrentDocument(null);
82
+ setEstimatedTimeRemaining(null);
83
+ }, [context.isReady, context.client]);
84
+ return {
85
+ progress,
86
+ currentDocument,
87
+ estimatedTimeRemaining,
88
+ };
89
+ }
90
+ //# sourceMappingURL=useSyncProgress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSyncProgress.js","sourceRoot":"","sources":["../../src/react/useSyncProgress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AA0B9D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAC;IAE/C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAe;QACrD,KAAK,EAAE,CAAC;QACR,SAAS,EAAE,CAAC;QACZ,OAAO,EAAE,CAAC;KACX,CAAC,CAAC;IACH,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC5E,MAAM,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAE1F,SAAS,CAAC,GAAG,EAAE;QACb,oDAAoD;QACpD,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACxC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;YACpD,kBAAkB,CAAC,IAAI,CAAC,CAAC;YACzB,yBAAyB,CAAC,IAAI,CAAC,CAAC;YAChC,OAAO;QACT,CAAC;QAED,oDAAoD;QACpD,8DAA8D;QAC9D,mCAAmC;QACnC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACzB,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAElC,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAEtC,OAAO;QACL,QAAQ;QACR,eAAe;QACf,sBAAsB;KACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * useSyncRejected - React hook for handling sync rejection events.
3
+ */
4
+ import { type Collection, type SyncRejectedEvent } from '../client/types.js';
5
+ /**
6
+ * Options for useSyncRejected hook.
7
+ */
8
+ export interface UseSyncRejectedOptions<T extends {
9
+ id: string;
10
+ }> {
11
+ /** Called when a sync is rejected */
12
+ readonly onRejected?: (event: SyncRejectedEvent<T>) => void;
13
+ /** Called specifically for unique constraint violations */
14
+ readonly onUniqueViolation?: (event: SyncRejectedEvent<T>, field?: string) => void;
15
+ /** Called specifically for validation errors */
16
+ readonly onValidationError?: (event: SyncRejectedEvent<T>) => void;
17
+ /** Called specifically for permission errors */
18
+ readonly onPermissionDenied?: (event: SyncRejectedEvent<T>) => void;
19
+ }
20
+ /**
21
+ * Subscribe to sync rejection events from a collection.
22
+ *
23
+ * @param collection - The collection to listen for rejections
24
+ * @param options - Callbacks for handling different rejection types
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * function TaskList() {
29
+ * const tasks = client.collection<Task>('tasks');
30
+ *
31
+ * useSyncRejected(tasks, {
32
+ * onRejected: (event) => {
33
+ * toast.error(`Failed to sync: ${event.error.message}`);
34
+ * },
35
+ * onUniqueViolation: (event, field) => {
36
+ * toast.error(`Duplicate ${field || 'value'} detected`);
37
+ * },
38
+ * });
39
+ *
40
+ * // ... rest of component
41
+ * }
42
+ * ```
43
+ */
44
+ export declare function useSyncRejected<T extends {
45
+ id: string;
46
+ }>(collection: Collection<T>, options: UseSyncRejectedOptions<T>): void;
47
+ //# sourceMappingURL=useSyncRejected.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSyncRejected.d.ts","sourceRoot":"","sources":["../../src/react/useSyncRejected.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAiB,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5F;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;IAC9D,qCAAqC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5D,2DAA2D;IAC3D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnF,gDAAgD;IAChD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACnE,gDAAgD;IAChD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CACrE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACtD,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,GACjC,IAAI,CA4BN"}