@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,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useConnectionState - React hook for tracking connection state.
|
|
3
|
+
*/
|
|
4
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
5
|
+
import { ConnectionState } from '../client/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Subscribe to connection state changes from an OfflineEdgeClient.
|
|
8
|
+
*
|
|
9
|
+
* @param client - The offline edge client
|
|
10
|
+
* @returns Connection state with convenience boolean flags
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* function SyncIndicator() {
|
|
15
|
+
* const { isOnline, isConnecting, isOffline } = useConnectionState(client);
|
|
16
|
+
*
|
|
17
|
+
* if (isOnline) {
|
|
18
|
+
* return <Badge color="green">Online</Badge>;
|
|
19
|
+
* }
|
|
20
|
+
* if (isConnecting) {
|
|
21
|
+
* return <Badge color="yellow"><Spinner /> Connecting...</Badge>;
|
|
22
|
+
* }
|
|
23
|
+
* return <Badge color="gray">Offline</Badge>;
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function useConnectionState(client) {
|
|
28
|
+
// Support both OfflineEdgeClient and raw Signal
|
|
29
|
+
const signal = 'connectionState' in client ? client.connectionState : client;
|
|
30
|
+
const [state, setState] = useState(signal.value);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const unsubscribe = signal.subscribe((newState) => {
|
|
33
|
+
setState(newState);
|
|
34
|
+
});
|
|
35
|
+
return unsubscribe;
|
|
36
|
+
}, [signal]);
|
|
37
|
+
return useMemo(() => ({
|
|
38
|
+
state,
|
|
39
|
+
isOffline: state === ConnectionState.Offline,
|
|
40
|
+
isConnecting: state === ConnectionState.Connecting,
|
|
41
|
+
isOnline: state === ConnectionState.Online,
|
|
42
|
+
isReconnecting: state === ConnectionState.Reconnecting,
|
|
43
|
+
isConnectingAny: state === ConnectionState.Connecting || state === ConnectionState.Reconnecting,
|
|
44
|
+
}), [state]);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=useConnectionState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useConnectionState.js","sourceRoot":"","sources":["../../src/react/useConnectionState.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,eAAe,EAAuC,MAAM,oBAAoB,CAAC;AAoB1F;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAmD;IAEnD,gDAAgD;IAChD,MAAM,MAAM,GAAG,iBAAiB,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC;IAE7E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAkB,MAAM,CAAC,KAAK,CAAC,CAAC;IAElE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,EAAE;YAChD,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC;IACrB,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,OAAO,OAAO,CAAC,GAA0B,EAAE,CAAC,CAAC;QAC3C,KAAK;QACL,SAAS,EAAE,KAAK,KAAK,eAAe,CAAC,OAAO;QAC5C,YAAY,EAAE,KAAK,KAAK,eAAe,CAAC,UAAU;QAClD,QAAQ,EAAE,KAAK,KAAK,eAAe,CAAC,MAAM;QAC1C,cAAc,EAAE,KAAK,KAAK,eAAe,CAAC,YAAY;QACtD,eAAe,EAAE,KAAK,KAAK,eAAe,CAAC,UAAU,IAAI,KAAK,KAAK,eAAe,CAAC,YAAY;KAChG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useDocumentSyncStatus - React hook for watching a specific document's sync status.
|
|
3
|
+
*
|
|
4
|
+
* Unlike useSyncStatus which extracts status from a SyncedDocument you already have,
|
|
5
|
+
* this hook watches a document by ID and provides reactive updates.
|
|
6
|
+
*/
|
|
7
|
+
import { SyncStatus, type Collection, type SyncError } from '../client/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Result of useDocumentSyncStatus hook.
|
|
10
|
+
*/
|
|
11
|
+
export interface UseDocumentSyncStatusResult {
|
|
12
|
+
/** Current sync status */
|
|
13
|
+
readonly status: SyncStatus;
|
|
14
|
+
/** True if document has not been synced yet (local only) */
|
|
15
|
+
readonly isLocal: boolean;
|
|
16
|
+
/** True if document is currently being synced */
|
|
17
|
+
readonly isSyncing: boolean;
|
|
18
|
+
/** True if document is synced with server */
|
|
19
|
+
readonly isSynced: boolean;
|
|
20
|
+
/** True if sync was rejected by server */
|
|
21
|
+
readonly isRejected: boolean;
|
|
22
|
+
/** True if document has pending changes (local or syncing) */
|
|
23
|
+
readonly isPending: boolean;
|
|
24
|
+
/** Error details if sync was rejected */
|
|
25
|
+
readonly error?: SyncError;
|
|
26
|
+
/** Timestamp of last sync attempt */
|
|
27
|
+
readonly lastSyncAttempt?: number;
|
|
28
|
+
/** Timestamp of last successful sync */
|
|
29
|
+
readonly lastSynced?: number;
|
|
30
|
+
/** Number of retry attempts */
|
|
31
|
+
readonly retryCount: number;
|
|
32
|
+
/** True while loading status */
|
|
33
|
+
readonly isLoading: boolean;
|
|
34
|
+
/** True if document was not found */
|
|
35
|
+
readonly notFound: boolean;
|
|
36
|
+
/** Retry syncing the document */
|
|
37
|
+
readonly retry: () => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Watch a specific document's sync status by ID.
|
|
41
|
+
*
|
|
42
|
+
* @param collection - The collection containing the document
|
|
43
|
+
* @param documentId - The document ID to watch
|
|
44
|
+
* @returns Reactive sync status with retry function
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* function TaskSyncBadge({ taskId }: { taskId: string }) {
|
|
49
|
+
* const tasks = client.collection<Task>('tasks');
|
|
50
|
+
* const { isRejected, error, retry, isPending } = useDocumentSyncStatus(tasks, taskId);
|
|
51
|
+
*
|
|
52
|
+
* if (isPending) {
|
|
53
|
+
* return <Badge>Syncing...</Badge>;
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* if (isRejected) {
|
|
57
|
+
* return (
|
|
58
|
+
* <div>
|
|
59
|
+
* <Badge color="red">Sync failed: {error?.message}</Badge>
|
|
60
|
+
* <button onClick={retry}>Retry</button>
|
|
61
|
+
* </div>
|
|
62
|
+
* );
|
|
63
|
+
* }
|
|
64
|
+
*
|
|
65
|
+
* return <Badge color="green">Synced</Badge>;
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function useDocumentSyncStatus<T extends {
|
|
70
|
+
id: string;
|
|
71
|
+
}>(collection: Collection<T>, documentId: string): UseDocumentSyncStatusResult;
|
|
72
|
+
//# sourceMappingURL=useDocumentSyncStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDocumentSyncStatus.d.ts","sourceRoot":"","sources":["../../src/react/useDocumentSyncStatus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAqB,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpG;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,0BAA0B;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,qCAAqC;IACrC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,wCAAwC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,+BAA+B;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,gCAAgC;IAChC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAC5D,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,UAAU,EAAE,MAAM,GACjB,2BAA2B,CAgF7B"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useDocumentSyncStatus - React hook for watching a specific document's sync status.
|
|
3
|
+
*
|
|
4
|
+
* Unlike useSyncStatus which extracts status from a SyncedDocument you already have,
|
|
5
|
+
* this hook watches a document by ID and provides reactive updates.
|
|
6
|
+
*/
|
|
7
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
8
|
+
import { SyncStatus } from '../client/types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Watch a specific document's sync status by ID.
|
|
11
|
+
*
|
|
12
|
+
* @param collection - The collection containing the document
|
|
13
|
+
* @param documentId - The document ID to watch
|
|
14
|
+
* @returns Reactive sync status with retry function
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* function TaskSyncBadge({ taskId }: { taskId: string }) {
|
|
19
|
+
* const tasks = client.collection<Task>('tasks');
|
|
20
|
+
* const { isRejected, error, retry, isPending } = useDocumentSyncStatus(tasks, taskId);
|
|
21
|
+
*
|
|
22
|
+
* if (isPending) {
|
|
23
|
+
* return <Badge>Syncing...</Badge>;
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* if (isRejected) {
|
|
27
|
+
* return (
|
|
28
|
+
* <div>
|
|
29
|
+
* <Badge color="red">Sync failed: {error?.message}</Badge>
|
|
30
|
+
* <button onClick={retry}>Retry</button>
|
|
31
|
+
* </div>
|
|
32
|
+
* );
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* return <Badge color="green">Synced</Badge>;
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function useDocumentSyncStatus(collection, documentId) {
|
|
40
|
+
const [syncMeta, setSyncMeta] = useState(null);
|
|
41
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
42
|
+
const [notFound, setNotFound] = useState(false);
|
|
43
|
+
// Subscribe to collection changes and filter for our document
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
let isMounted = true;
|
|
46
|
+
// Initial load
|
|
47
|
+
collection.getSyncStatus(documentId).then((status) => {
|
|
48
|
+
if (isMounted) {
|
|
49
|
+
setSyncMeta(status);
|
|
50
|
+
setNotFound(status === null);
|
|
51
|
+
setIsLoading(false);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
// Subscribe to changes
|
|
55
|
+
const unsubscribe = collection.subscribe((docs) => {
|
|
56
|
+
if (!isMounted)
|
|
57
|
+
return;
|
|
58
|
+
const doc = docs.find(d => d.id === documentId);
|
|
59
|
+
if (doc) {
|
|
60
|
+
setSyncMeta(doc._sync);
|
|
61
|
+
setNotFound(false);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
setSyncMeta(null);
|
|
65
|
+
setNotFound(true);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return () => {
|
|
69
|
+
isMounted = false;
|
|
70
|
+
unsubscribe();
|
|
71
|
+
};
|
|
72
|
+
}, [collection, documentId]);
|
|
73
|
+
// Retry function
|
|
74
|
+
const retry = useMemo(() => async () => {
|
|
75
|
+
await collection.retrySync(documentId);
|
|
76
|
+
}, [collection, documentId]);
|
|
77
|
+
return useMemo(() => {
|
|
78
|
+
if (!syncMeta) {
|
|
79
|
+
return {
|
|
80
|
+
status: SyncStatus.Synced,
|
|
81
|
+
isLocal: false,
|
|
82
|
+
isSyncing: false,
|
|
83
|
+
isSynced: true,
|
|
84
|
+
isRejected: false,
|
|
85
|
+
isPending: false,
|
|
86
|
+
retryCount: 0,
|
|
87
|
+
isLoading,
|
|
88
|
+
notFound,
|
|
89
|
+
retry,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const status = syncMeta.status;
|
|
93
|
+
return {
|
|
94
|
+
status,
|
|
95
|
+
isLocal: status === SyncStatus.Local,
|
|
96
|
+
isSyncing: status === SyncStatus.Syncing,
|
|
97
|
+
isSynced: status === SyncStatus.Synced,
|
|
98
|
+
isRejected: status === SyncStatus.Rejected,
|
|
99
|
+
isPending: status === SyncStatus.Local || status === SyncStatus.Syncing,
|
|
100
|
+
error: syncMeta.error,
|
|
101
|
+
lastSyncAttempt: syncMeta.lastSyncAttempt,
|
|
102
|
+
lastSynced: syncMeta.lastSynced,
|
|
103
|
+
retryCount: syncMeta.retryCount ?? 0,
|
|
104
|
+
isLoading,
|
|
105
|
+
notFound,
|
|
106
|
+
retry,
|
|
107
|
+
};
|
|
108
|
+
}, [syncMeta, isLoading, notFound, retry]);
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=useDocumentSyncStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDocumentSyncStatus.js","sourceRoot":"","sources":["../../src/react/useDocumentSyncStatus.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useOfflineEdge - React hook to access the offline edge client from context.
|
|
3
|
+
*
|
|
4
|
+
* This hook provides access to the OfflineEdgeClient and its initialization state.
|
|
5
|
+
* Must be used within an OfflineEdgeProvider.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { useOfflineEdge } from '@fatagnus/dink-sync/react';
|
|
10
|
+
*
|
|
11
|
+
* function MyComponent() {
|
|
12
|
+
* const { client, isLoading, isReady, error } = useOfflineEdge();
|
|
13
|
+
*
|
|
14
|
+
* if (isLoading) return <Spinner />;
|
|
15
|
+
* if (error) return <Error message={error.message} />;
|
|
16
|
+
* if (!isReady || !client) return null;
|
|
17
|
+
*
|
|
18
|
+
* const tasks = client.collection<Task>('tasks');
|
|
19
|
+
* // Use tasks collection...
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import type { OfflineEdgeClient } from '../client/types.js';
|
|
24
|
+
/**
|
|
25
|
+
* Result of useOfflineEdge hook.
|
|
26
|
+
*/
|
|
27
|
+
export interface UseOfflineEdgeResult {
|
|
28
|
+
/** The initialized client, or null if not yet ready */
|
|
29
|
+
client: OfflineEdgeClient | null;
|
|
30
|
+
/** True while initializing */
|
|
31
|
+
isLoading: boolean;
|
|
32
|
+
/** True when client is ready to use */
|
|
33
|
+
isReady: boolean;
|
|
34
|
+
/** Error if initialization failed */
|
|
35
|
+
error: Error | null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Hook to access the full offline edge context state.
|
|
39
|
+
*
|
|
40
|
+
* Returns loading state, error state, and the client when ready.
|
|
41
|
+
*
|
|
42
|
+
* @throws Error if used outside of OfflineEdgeProvider
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* function MyComponent() {
|
|
47
|
+
* const { client, isLoading, isReady, error } = useOfflineEdge();
|
|
48
|
+
*
|
|
49
|
+
* if (isLoading) return <Spinner />;
|
|
50
|
+
* if (error) return <Error message={error.message} />;
|
|
51
|
+
* if (!isReady || !client) return null;
|
|
52
|
+
*
|
|
53
|
+
* // Use client...
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function useOfflineEdge(): UseOfflineEdgeResult;
|
|
58
|
+
//# sourceMappingURL=useOfflineEdge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOfflineEdge.d.ts","sourceRoot":"","sources":["../../src/react/useOfflineEdge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,8BAA8B;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,IAAI,oBAAoB,CAQrD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useOfflineEdge - React hook to access the offline edge client from context.
|
|
3
|
+
*
|
|
4
|
+
* This hook provides access to the OfflineEdgeClient and its initialization state.
|
|
5
|
+
* Must be used within an OfflineEdgeProvider.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { useOfflineEdge } from '@fatagnus/dink-sync/react';
|
|
10
|
+
*
|
|
11
|
+
* function MyComponent() {
|
|
12
|
+
* const { client, isLoading, isReady, error } = useOfflineEdge();
|
|
13
|
+
*
|
|
14
|
+
* if (isLoading) return <Spinner />;
|
|
15
|
+
* if (error) return <Error message={error.message} />;
|
|
16
|
+
* if (!isReady || !client) return null;
|
|
17
|
+
*
|
|
18
|
+
* const tasks = client.collection<Task>('tasks');
|
|
19
|
+
* // Use tasks collection...
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { useContext } from 'react';
|
|
24
|
+
// Import the context from OfflineEdgeProvider
|
|
25
|
+
// We use a lazy import pattern to avoid circular dependencies
|
|
26
|
+
import { OfflineEdgeContext } from './OfflineEdgeProvider.js';
|
|
27
|
+
/**
|
|
28
|
+
* Hook to access the full offline edge context state.
|
|
29
|
+
*
|
|
30
|
+
* Returns loading state, error state, and the client when ready.
|
|
31
|
+
*
|
|
32
|
+
* @throws Error if used outside of OfflineEdgeProvider
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* function MyComponent() {
|
|
37
|
+
* const { client, isLoading, isReady, error } = useOfflineEdge();
|
|
38
|
+
*
|
|
39
|
+
* if (isLoading) return <Spinner />;
|
|
40
|
+
* if (error) return <Error message={error.message} />;
|
|
41
|
+
* if (!isReady || !client) return null;
|
|
42
|
+
*
|
|
43
|
+
* // Use client...
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function useOfflineEdge() {
|
|
48
|
+
const context = useContext(OfflineEdgeContext);
|
|
49
|
+
if (context === null) {
|
|
50
|
+
throw new Error('useOfflineEdge must be used within an OfflineEdgeProvider');
|
|
51
|
+
}
|
|
52
|
+
return context;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=useOfflineEdge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOfflineEdge.js","sourceRoot":"","sources":["../../src/react/useOfflineEdge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAiBnC,8CAA8C;AAC9C,8DAA8D;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAC;IAE/C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* usePendingChanges - React hook to track pending sync changes.
|
|
3
|
+
*
|
|
4
|
+
* Provides real-time counts of pending changes across all collections,
|
|
5
|
+
* enabling sync status UI indicators.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { usePendingChanges } from '@fatagnus/dink-sync/react';
|
|
10
|
+
*
|
|
11
|
+
* function SyncStatusBar() {
|
|
12
|
+
* const { totalCount, byCollection, isSyncing } = usePendingChanges();
|
|
13
|
+
*
|
|
14
|
+
* return (
|
|
15
|
+
* <div>
|
|
16
|
+
* {isSyncing && <Spinner />}
|
|
17
|
+
* {totalCount > 0 && (
|
|
18
|
+
* <span>{totalCount} pending changes</span>
|
|
19
|
+
* )}
|
|
20
|
+
* {Object.entries(byCollection).map(([name, count]) => (
|
|
21
|
+
* <span key={name}>{name}: {count}</span>
|
|
22
|
+
* ))}
|
|
23
|
+
* </div>
|
|
24
|
+
* );
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Result of usePendingChanges hook.
|
|
30
|
+
*/
|
|
31
|
+
export interface UsePendingChangesResult {
|
|
32
|
+
/** Total count of pending changes across all collections */
|
|
33
|
+
totalCount: number;
|
|
34
|
+
/** Breakdown of pending changes by collection name */
|
|
35
|
+
byCollection: Record<string, number>;
|
|
36
|
+
/** True when sync is currently in progress */
|
|
37
|
+
isSyncing: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Hook to track pending sync changes across all collections.
|
|
41
|
+
*
|
|
42
|
+
* Provides:
|
|
43
|
+
* - Total count of pending changes
|
|
44
|
+
* - Breakdown by collection name
|
|
45
|
+
* - Syncing state indicator
|
|
46
|
+
*
|
|
47
|
+
* @throws Error if used outside of OfflineEdgeProvider
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* function SyncIndicator() {
|
|
52
|
+
* const { totalCount, isSyncing } = usePendingChanges();
|
|
53
|
+
*
|
|
54
|
+
* if (totalCount === 0) {
|
|
55
|
+
* return <span>✓ All synced</span>;
|
|
56
|
+
* }
|
|
57
|
+
*
|
|
58
|
+
* return (
|
|
59
|
+
* <span>
|
|
60
|
+
* {isSyncing ? 'Syncing...' : `${totalCount} pending`}
|
|
61
|
+
* </span>
|
|
62
|
+
* );
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function usePendingChanges(): UsePendingChangesResult;
|
|
67
|
+
//# sourceMappingURL=usePendingChanges.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePendingChanges.d.ts","sourceRoot":"","sources":["../../src/react/usePendingChanges.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,IAAI,uBAAuB,CAuC3D"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* usePendingChanges - React hook to track pending sync changes.
|
|
3
|
+
*
|
|
4
|
+
* Provides real-time counts of pending changes across all collections,
|
|
5
|
+
* enabling sync status UI indicators.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { usePendingChanges } from '@fatagnus/dink-sync/react';
|
|
10
|
+
*
|
|
11
|
+
* function SyncStatusBar() {
|
|
12
|
+
* const { totalCount, byCollection, isSyncing } = usePendingChanges();
|
|
13
|
+
*
|
|
14
|
+
* return (
|
|
15
|
+
* <div>
|
|
16
|
+
* {isSyncing && <Spinner />}
|
|
17
|
+
* {totalCount > 0 && (
|
|
18
|
+
* <span>{totalCount} pending changes</span>
|
|
19
|
+
* )}
|
|
20
|
+
* {Object.entries(byCollection).map(([name, count]) => (
|
|
21
|
+
* <span key={name}>{name}: {count}</span>
|
|
22
|
+
* ))}
|
|
23
|
+
* </div>
|
|
24
|
+
* );
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
import { useState, useEffect, useContext } from 'react';
|
|
29
|
+
import { OfflineEdgeContext } from './OfflineEdgeProvider.js';
|
|
30
|
+
/**
|
|
31
|
+
* Hook to track pending sync changes across all collections.
|
|
32
|
+
*
|
|
33
|
+
* Provides:
|
|
34
|
+
* - Total count of pending changes
|
|
35
|
+
* - Breakdown by collection name
|
|
36
|
+
* - Syncing state indicator
|
|
37
|
+
*
|
|
38
|
+
* @throws Error if used outside of OfflineEdgeProvider
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* function SyncIndicator() {
|
|
43
|
+
* const { totalCount, isSyncing } = usePendingChanges();
|
|
44
|
+
*
|
|
45
|
+
* if (totalCount === 0) {
|
|
46
|
+
* return <span>✓ All synced</span>;
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* return (
|
|
50
|
+
* <span>
|
|
51
|
+
* {isSyncing ? 'Syncing...' : `${totalCount} pending`}
|
|
52
|
+
* </span>
|
|
53
|
+
* );
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function usePendingChanges() {
|
|
58
|
+
const context = useContext(OfflineEdgeContext);
|
|
59
|
+
if (context === null) {
|
|
60
|
+
throw new Error('usePendingChanges must be used within an OfflineEdgeProvider');
|
|
61
|
+
}
|
|
62
|
+
const [totalCount, setTotalCount] = useState(0);
|
|
63
|
+
const [byCollection, setByCollection] = useState({});
|
|
64
|
+
const [isSyncing, setIsSyncing] = useState(false);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
// If client is not ready yet, return default values
|
|
67
|
+
if (!context.isReady || !context.client) {
|
|
68
|
+
setTotalCount(0);
|
|
69
|
+
setByCollection({});
|
|
70
|
+
setIsSyncing(false);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// For now, we return the default state since we don't have direct
|
|
74
|
+
// access to a global pending queue from the client interface.
|
|
75
|
+
// The sync engine manages pending changes internally per document.
|
|
76
|
+
// In a full implementation, the client would expose a way to
|
|
77
|
+
// subscribe to pending changes across all collections.
|
|
78
|
+
// Default: no pending changes tracked at client level
|
|
79
|
+
// Real implementation would subscribe to sync engine events
|
|
80
|
+
setTotalCount(0);
|
|
81
|
+
setByCollection({});
|
|
82
|
+
setIsSyncing(false);
|
|
83
|
+
}, [context.isReady, context.client]);
|
|
84
|
+
return {
|
|
85
|
+
totalCount,
|
|
86
|
+
byCollection,
|
|
87
|
+
isSyncing,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=usePendingChanges.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePendingChanges.js","sourceRoot":"","sources":["../../src/react/usePendingChanges.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAc9D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,OAAO,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAC;IAE/C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAyB,EAAE,CAAC,CAAC;IAC7E,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElD,SAAS,CAAC,GAAG,EAAE;QACb,oDAAoD;QACpD,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACxC,aAAa,CAAC,CAAC,CAAC,CAAC;YACjB,eAAe,CAAC,EAAE,CAAC,CAAC;YACpB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QAED,kEAAkE;QAClE,8DAA8D;QAC9D,mEAAmE;QACnE,6DAA6D;QAC7D,uDAAuD;QAEvD,sDAAsD;QACtD,4DAA4D;QAC5D,aAAa,CAAC,CAAC,CAAC,CAAC;QACjB,eAAe,CAAC,EAAE,CAAC,CAAC;QACpB,YAAY,CAAC,KAAK,CAAC,CAAC;IAEtB,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAEtC,OAAO;QACL,UAAU;QACV,YAAY;QACZ,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
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 { type SyncErrorCode } from '../client/types.js';
|
|
37
|
+
/**
|
|
38
|
+
* A rejected document with error details.
|
|
39
|
+
*/
|
|
40
|
+
export interface RejectedDocument {
|
|
41
|
+
/** Collection name */
|
|
42
|
+
readonly collection: string;
|
|
43
|
+
/** Document ID */
|
|
44
|
+
readonly docId: string;
|
|
45
|
+
/** Error code categorizing the failure */
|
|
46
|
+
readonly errorCode: SyncErrorCode;
|
|
47
|
+
/** Human-readable error message */
|
|
48
|
+
readonly errorMessage: string;
|
|
49
|
+
/** Timestamp when the rejection occurred */
|
|
50
|
+
readonly timestamp: number;
|
|
51
|
+
/** Field that caused the error (if applicable) */
|
|
52
|
+
readonly field?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Result of useRejectedDocuments hook.
|
|
56
|
+
*/
|
|
57
|
+
export interface UseRejectedDocumentsResult {
|
|
58
|
+
/** Array of rejected documents with error details */
|
|
59
|
+
readonly rejectedDocuments: RejectedDocument[];
|
|
60
|
+
/**
|
|
61
|
+
* Retry syncing a rejected document.
|
|
62
|
+
* @param collection - Collection name
|
|
63
|
+
* @param docId - Document ID
|
|
64
|
+
*/
|
|
65
|
+
readonly retryDocument: (collection: string, docId: string) => Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Discard a rejected document (delete it from local storage).
|
|
68
|
+
* @param collection - Collection name
|
|
69
|
+
* @param docId - Document ID
|
|
70
|
+
*/
|
|
71
|
+
readonly discardDocument: (collection: string, docId: string) => Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Register a collection to track for rejected documents.
|
|
74
|
+
* Call this for any collection you want to monitor.
|
|
75
|
+
* @param collection - Collection name
|
|
76
|
+
*/
|
|
77
|
+
readonly registerCollection: (collection: string) => Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Hook to track and manage rejected sync documents.
|
|
81
|
+
*
|
|
82
|
+
* Provides:
|
|
83
|
+
* - Array of all rejected documents with error details
|
|
84
|
+
* - Helper function to retry syncing a document
|
|
85
|
+
* - Helper function to discard a document
|
|
86
|
+
*
|
|
87
|
+
* @throws Error if used outside of OfflineEdgeProvider
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```tsx
|
|
91
|
+
* function SyncErrorBanner() {
|
|
92
|
+
* const { rejectedDocuments, retryDocument } = useRejectedDocuments();
|
|
93
|
+
*
|
|
94
|
+
* if (rejectedDocuments.length === 0) return null;
|
|
95
|
+
*
|
|
96
|
+
* return (
|
|
97
|
+
* <div className="error-banner">
|
|
98
|
+
* {rejectedDocuments.length} sync errors
|
|
99
|
+
* <button onClick={() => {
|
|
100
|
+
* rejectedDocuments.forEach(doc =>
|
|
101
|
+
* retryDocument(doc.collection, doc.docId)
|
|
102
|
+
* );
|
|
103
|
+
* }}>
|
|
104
|
+
* Retry All
|
|
105
|
+
* </button>
|
|
106
|
+
* </div>
|
|
107
|
+
* );
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export declare function useRejectedDocuments(): UseRejectedDocumentsResult;
|
|
112
|
+
//# sourceMappingURL=useRejectedDocuments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRejectedDocuments.d.ts","sourceRoot":"","sources":["../../src/react/useRejectedDocuments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,kBAAkB;IAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,mCAAmC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,qDAAqD;IACrD,QAAQ,CAAC,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,oBAAoB,IAAI,0BAA0B,CA4KjE"}
|