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