@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.
- package/README.md +312 -0
- package/dist/client/attachment.d.ts +225 -0
- package/dist/client/attachment.d.ts.map +1 -0
- package/dist/client/attachment.js +402 -0
- package/dist/client/attachment.js.map +1 -0
- package/dist/client/binary-encoding.d.ts +45 -0
- package/dist/client/binary-encoding.d.ts.map +1 -0
- package/dist/client/binary-encoding.js +90 -0
- package/dist/client/binary-encoding.js.map +1 -0
- package/dist/client/collection.d.ts +10 -0
- package/dist/client/collection.d.ts.map +1 -0
- package/dist/client/collection.js +924 -0
- package/dist/client/collection.js.map +1 -0
- package/dist/client/compression.d.ts +56 -0
- package/dist/client/compression.d.ts.map +1 -0
- package/dist/client/compression.js +173 -0
- package/dist/client/compression.js.map +1 -0
- package/dist/client/crdt/index.d.ts +2 -0
- package/dist/client/crdt/index.d.ts.map +1 -0
- package/dist/client/crdt/index.js +2 -0
- package/dist/client/crdt/index.js.map +1 -0
- package/dist/client/crdt/yjs-doc.d.ts +88 -0
- package/dist/client/crdt/yjs-doc.d.ts.map +1 -0
- package/dist/client/crdt/yjs-doc.js +123 -0
- package/dist/client/crdt/yjs-doc.js.map +1 -0
- package/dist/client/index.d.ts +66 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +233 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/mock-transport.d.ts +155 -0
- package/dist/client/mock-transport.d.ts.map +1 -0
- package/dist/client/mock-transport.js +292 -0
- package/dist/client/mock-transport.js.map +1 -0
- package/dist/client/network-detector.d.ts +65 -0
- package/dist/client/network-detector.d.ts.map +1 -0
- package/dist/client/network-detector.js +147 -0
- package/dist/client/network-detector.js.map +1 -0
- package/dist/client/provisioning.d.ts +126 -0
- package/dist/client/provisioning.d.ts.map +1 -0
- package/dist/client/provisioning.js +125 -0
- package/dist/client/provisioning.js.map +1 -0
- package/dist/client/signal.d.ts +13 -0
- package/dist/client/signal.d.ts.map +1 -0
- package/dist/client/signal.js +27 -0
- package/dist/client/signal.js.map +1 -0
- package/dist/client/sync-engine.d.ts +298 -0
- package/dist/client/sync-engine.d.ts.map +1 -0
- package/dist/client/sync-engine.js +904 -0
- package/dist/client/sync-engine.js.map +1 -0
- package/dist/client/synced-edge.d.ts +109 -0
- package/dist/client/synced-edge.d.ts.map +1 -0
- package/dist/client/synced-edge.js +179 -0
- package/dist/client/synced-edge.js.map +1 -0
- package/dist/client/synced-offline-edge-types.d.ts +540 -0
- package/dist/client/synced-offline-edge-types.d.ts.map +1 -0
- package/dist/client/synced-offline-edge-types.js +10 -0
- package/dist/client/synced-offline-edge-types.js.map +1 -0
- package/dist/client/synced-offline-edge.d.ts +54 -0
- package/dist/client/synced-offline-edge.d.ts.map +1 -0
- package/dist/client/synced-offline-edge.js +731 -0
- package/dist/client/synced-offline-edge.js.map +1 -0
- package/dist/client/transport.d.ts +202 -0
- package/dist/client/transport.d.ts.map +1 -0
- package/dist/client/transport.js +409 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/client/types.d.ts +622 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +60 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/validation.d.ts +61 -0
- package/dist/client/validation.d.ts.map +1 -0
- package/dist/client/validation.js +57 -0
- package/dist/client/validation.js.map +1 -0
- package/dist/client/versioning.d.ts +134 -0
- package/dist/client/versioning.d.ts.map +1 -0
- package/dist/client/versioning.js +304 -0
- package/dist/client/versioning.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/persistence/encryption.d.ts +114 -0
- package/dist/persistence/encryption.d.ts.map +1 -0
- package/dist/persistence/encryption.js +286 -0
- package/dist/persistence/encryption.js.map +1 -0
- package/dist/persistence/index.d.ts +21 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +20 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/memory.d.ts +32 -0
- package/dist/persistence/memory.d.ts.map +1 -0
- package/dist/persistence/memory.js +57 -0
- package/dist/persistence/memory.js.map +1 -0
- package/dist/persistence/migrations.d.ts +106 -0
- package/dist/persistence/migrations.d.ts.map +1 -0
- package/dist/persistence/migrations.js +176 -0
- package/dist/persistence/migrations.js.map +1 -0
- package/dist/persistence/pending-queue.d.ts +109 -0
- package/dist/persistence/pending-queue.d.ts.map +1 -0
- package/dist/persistence/pending-queue.js +249 -0
- package/dist/persistence/pending-queue.js.map +1 -0
- package/dist/persistence/pglite.d.ts +72 -0
- package/dist/persistence/pglite.d.ts.map +1 -0
- package/dist/persistence/pglite.js +126 -0
- package/dist/persistence/pglite.js.map +1 -0
- package/dist/persistence/quota-manager.d.ts +134 -0
- package/dist/persistence/quota-manager.d.ts.map +1 -0
- package/dist/persistence/quota-manager.js +242 -0
- package/dist/persistence/quota-manager.js.map +1 -0
- package/dist/persistence/types.d.ts +54 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +2 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/react/OfflineEdgeProvider.d.ts +91 -0
- package/dist/react/OfflineEdgeProvider.d.ts.map +1 -0
- package/dist/react/OfflineEdgeProvider.js +127 -0
- package/dist/react/OfflineEdgeProvider.js.map +1 -0
- package/dist/react/SyncedOfflineEdgeProvider.d.ts +105 -0
- package/dist/react/SyncedOfflineEdgeProvider.d.ts.map +1 -0
- package/dist/react/SyncedOfflineEdgeProvider.js +138 -0
- package/dist/react/SyncedOfflineEdgeProvider.js.map +1 -0
- package/dist/react/index.d.ts +50 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +51 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/useCollection.d.ts +77 -0
- package/dist/react/useCollection.d.ts.map +1 -0
- package/dist/react/useCollection.js +113 -0
- package/dist/react/useCollection.js.map +1 -0
- package/dist/react/useCollectionSyncMode.d.ts +61 -0
- package/dist/react/useCollectionSyncMode.d.ts.map +1 -0
- package/dist/react/useCollectionSyncMode.js +93 -0
- package/dist/react/useCollectionSyncMode.js.map +1 -0
- package/dist/react/useConnectionState.d.ts +44 -0
- package/dist/react/useConnectionState.d.ts.map +1 -0
- package/dist/react/useConnectionState.js +46 -0
- package/dist/react/useConnectionState.js.map +1 -0
- package/dist/react/useDocumentSyncStatus.d.ts +72 -0
- package/dist/react/useDocumentSyncStatus.d.ts.map +1 -0
- package/dist/react/useDocumentSyncStatus.js +110 -0
- package/dist/react/useDocumentSyncStatus.js.map +1 -0
- package/dist/react/useOfflineEdge.d.ts +58 -0
- package/dist/react/useOfflineEdge.d.ts.map +1 -0
- package/dist/react/useOfflineEdge.js +54 -0
- package/dist/react/useOfflineEdge.js.map +1 -0
- package/dist/react/usePendingChanges.d.ts +67 -0
- package/dist/react/usePendingChanges.d.ts.map +1 -0
- package/dist/react/usePendingChanges.js +90 -0
- package/dist/react/usePendingChanges.js.map +1 -0
- package/dist/react/useRejectedDocuments.d.ts +112 -0
- package/dist/react/useRejectedDocuments.d.ts.map +1 -0
- package/dist/react/useRejectedDocuments.js +213 -0
- package/dist/react/useRejectedDocuments.js.map +1 -0
- package/dist/react/useSyncControls.d.ts +96 -0
- package/dist/react/useSyncControls.d.ts.map +1 -0
- package/dist/react/useSyncControls.js +112 -0
- package/dist/react/useSyncControls.js.map +1 -0
- package/dist/react/useSyncProgress.d.ts +78 -0
- package/dist/react/useSyncProgress.d.ts.map +1 -0
- package/dist/react/useSyncProgress.js +90 -0
- package/dist/react/useSyncProgress.js.map +1 -0
- package/dist/react/useSyncRejected.d.ts +47 -0
- package/dist/react/useSyncRejected.d.ts.map +1 -0
- package/dist/react/useSyncRejected.js +55 -0
- package/dist/react/useSyncRejected.js.map +1 -0
- package/dist/react/useSyncStatus.d.ts +56 -0
- package/dist/react/useSyncStatus.d.ts.map +1 -0
- package/dist/react/useSyncStatus.js +59 -0
- package/dist/react/useSyncStatus.js.map +1 -0
- package/dist/react/useSyncedOfflineEdge.d.ts +69 -0
- package/dist/react/useSyncedOfflineEdge.d.ts.map +1 -0
- package/dist/react/useSyncedOfflineEdge.js +65 -0
- package/dist/react/useSyncedOfflineEdge.js.map +1 -0
- package/dist/service-worker/index.d.ts +7 -0
- package/dist/service-worker/index.d.ts.map +1 -0
- package/dist/service-worker/index.js +7 -0
- package/dist/service-worker/index.js.map +1 -0
- package/dist/service-worker/sync-worker.d.ts +230 -0
- package/dist/service-worker/sync-worker.d.ts.map +1 -0
- package/dist/service-worker/sync-worker.js +471 -0
- package/dist/service-worker/sync-worker.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- 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"}
|