@syncular/client-react 0.0.3-3 → 0.0.3-6

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.
@@ -7,7 +7,7 @@
7
7
  * const syncular = createSyncularReact<MyDb>();
8
8
  * const { SyncProvider, useSyncQuery } = syncular;
9
9
  */
10
- import type { ClientTableRegistry, MutationReceipt, MutationsApi, SyncClientDb, SyncClientPlugin, SyncSubscriptionRequest, SyncTransport } from '@syncular/client';
10
+ import type { ClientTableRegistry, MutationReceipt, MutationsApi, SubscriptionState, SyncAwaitBootstrapOptions, SyncAwaitPhaseOptions, SyncClientDb, SyncClientPlugin, SyncDiagnostics, SyncProgress, SyncRepairOptions, SyncResetOptions, SyncResetResult, SyncSubscriptionRequest, SyncTransport, TransportHealth } from '@syncular/client';
11
11
  import { type ConflictInfo, type OutboxStats, type PresenceEntry, type QueryContext, type SyncConnectionState, SyncEngine, type SyncEngineState, type SyncError, type SyncResult, type SyncTransportMode } from '@syncular/client';
12
12
  import { type Kysely } from 'kysely';
13
13
  import { type ReactNode } from 'react';
@@ -54,17 +54,41 @@ export interface UseSyncEngineResult {
54
54
  disconnect: () => void;
55
55
  start: () => Promise<void>;
56
56
  resetLocalState: () => void;
57
+ getTransportHealth: () => Readonly<TransportHealth>;
58
+ getProgress: () => Promise<SyncProgress>;
59
+ getDiagnostics: () => Promise<SyncDiagnostics>;
60
+ listSubscriptionStates: (args?: {
61
+ stateId?: string;
62
+ table?: string;
63
+ status?: 'active' | 'revoked';
64
+ }) => Promise<SubscriptionState[]>;
65
+ getSubscriptionState: (subscriptionId: string, options?: {
66
+ stateId?: string;
67
+ }) => Promise<SubscriptionState | null>;
68
+ reset: (options: SyncResetOptions) => Promise<SyncResetResult>;
69
+ repair: (options: SyncRepairOptions) => Promise<SyncResetResult>;
70
+ awaitPhase: (phase: SyncProgress['channelPhase'], options?: SyncAwaitPhaseOptions) => Promise<SyncProgress>;
71
+ awaitBootstrapComplete: (options?: SyncAwaitBootstrapOptions) => Promise<SyncProgress>;
57
72
  }
58
73
  export interface SyncStatus {
59
74
  enabled: boolean;
60
75
  isOnline: boolean;
61
76
  isSyncing: boolean;
62
77
  lastSyncAt: number | null;
78
+ lastSyncAgeMs: number | null;
79
+ isStale: boolean;
63
80
  pendingCount: number;
64
81
  error: SyncError | null;
65
82
  isRetrying: boolean;
66
83
  retryCount: number;
67
84
  }
85
+ export interface UseSyncStatusOptions {
86
+ /**
87
+ * Mark status as stale when `Date.now() - lastSyncAt` exceeds this value.
88
+ * If omitted, `isStale` is always false.
89
+ */
90
+ staleAfterMs?: number;
91
+ }
68
92
  export interface UseSyncConnectionResult {
69
93
  state: SyncConnectionState;
70
94
  mode: SyncTransportMode;
@@ -73,6 +97,39 @@ export interface UseSyncConnectionResult {
73
97
  reconnect: () => void;
74
98
  disconnect: () => void;
75
99
  }
100
+ export interface UseTransportHealthResult {
101
+ health: TransportHealth;
102
+ }
103
+ export interface UseSyncProgressOptions {
104
+ /**
105
+ * Polling interval while bootstrapping.
106
+ * Set to 0 to disable interval refresh.
107
+ */
108
+ pollIntervalMs?: number;
109
+ }
110
+ export interface UseSyncProgressResult {
111
+ progress: SyncProgress | null;
112
+ isLoading: boolean;
113
+ error: Error | null;
114
+ refresh: () => Promise<void>;
115
+ }
116
+ export interface UseSyncSubscriptionsOptions {
117
+ stateId?: string;
118
+ table?: string;
119
+ status?: 'active' | 'revoked';
120
+ }
121
+ export interface UseSyncSubscriptionsResult {
122
+ subscriptions: SubscriptionState[];
123
+ isLoading: boolean;
124
+ error: Error | null;
125
+ refresh: () => Promise<void>;
126
+ }
127
+ export interface UseSyncSubscriptionResult {
128
+ subscription: SubscriptionState | null;
129
+ isLoading: boolean;
130
+ error: Error | null;
131
+ refresh: () => Promise<void>;
132
+ }
76
133
  export interface UseConflictsResult {
77
134
  conflicts: ConflictInfo[];
78
135
  count: number;
@@ -96,12 +153,17 @@ export interface UseSyncQueryResult<T> {
96
153
  data: T | undefined;
97
154
  isLoading: boolean;
98
155
  error: Error | null;
156
+ isStale: boolean;
157
+ lastSyncAt: number | null;
99
158
  refetch: () => Promise<void>;
100
159
  }
101
160
  export interface UseSyncQueryOptions {
102
161
  enabled?: boolean;
103
162
  deps?: unknown[];
104
163
  keyField?: string;
164
+ watchTables?: string[];
165
+ pollIntervalMs?: number;
166
+ staleAfterMs?: number;
105
167
  }
106
168
  export interface UseQueryResult<T> {
107
169
  data: T | undefined;
@@ -208,8 +270,14 @@ export declare function createSyncularReact<DB extends SyncClientDb>(): {
208
270
  readonly useSyncContext: () => SyncContextValue<DB>;
209
271
  readonly useEngine: () => SyncEngine<DB>;
210
272
  readonly useSyncEngine: () => UseSyncEngineResult;
211
- readonly useSyncStatus: () => SyncStatus;
273
+ readonly useSyncStatus: (options?: UseSyncStatusOptions) => SyncStatus;
212
274
  readonly useSyncConnection: () => UseSyncConnectionResult;
275
+ readonly useTransportHealth: () => UseTransportHealthResult;
276
+ readonly useSyncProgress: (options?: UseSyncProgressOptions) => UseSyncProgressResult;
277
+ readonly useSyncSubscriptions: (options?: UseSyncSubscriptionsOptions) => UseSyncSubscriptionsResult;
278
+ readonly useSyncSubscription: (subscriptionId: string, options?: {
279
+ stateId?: string | undefined;
280
+ }) => UseSyncSubscriptionResult;
213
281
  readonly useSyncQuery: <TResult>(queryFn: (ctx: QueryContext<DB>) => ExecutableQuery<TResult> | Promise<TResult>, options?: UseSyncQueryOptions) => UseSyncQueryResult<TResult>;
214
282
  readonly useQuery: <TResult>(queryFn: (ctx: QueryContext<DB>) => ExecutableQuery<TResult> | Promise<TResult>, options?: UseQueryOptions) => UseQueryResult<TResult>;
215
283
  readonly useMutation: <TTable extends keyof DB & string>(options: UseMutationOptions<TTable>) => UseMutationResult<TTable>;
@@ -1 +1 @@
1
- {"version":3,"file":"createSyncularReact.d.ts","sourceRoot":"","sources":["../src/createSyncularReact.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createSyncularReact.d.ts","sourceRoot":"","sources":["../src/createSyncularReact.tsx"],"names":[],"mappings":"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"}
@@ -167,6 +167,15 @@ export function createSyncularReact() {
167
167
  const disconnect = useCallback(() => engine.disconnect(), [engine]);
168
168
  const start = useCallback(() => engine.start(), [engine]);
169
169
  const resetLocalState = useCallback(() => engine.resetLocalState(), [engine]);
170
+ const getTransportHealth = useCallback(() => engine.getTransportHealth(), [engine]);
171
+ const getProgress = useCallback(() => engine.getProgress(), [engine]);
172
+ const getDiagnostics = useCallback(() => engine.getDiagnostics(), [engine]);
173
+ const listSubscriptionStates = useCallback((args) => engine.listSubscriptionStates(args), [engine]);
174
+ const getSubscriptionState = useCallback((subscriptionId, options) => engine.getSubscriptionState(subscriptionId, options), [engine]);
175
+ const reset = useCallback((options) => engine.reset(options), [engine]);
176
+ const repair = useCallback((options) => engine.repair(options), [engine]);
177
+ const awaitPhase = useCallback((phase, options) => engine.awaitPhase(phase, options), [engine]);
178
+ const awaitBootstrapComplete = useCallback((options) => engine.awaitBootstrapComplete(options), [engine]);
170
179
  return {
171
180
  state,
172
181
  sync,
@@ -174,21 +183,50 @@ export function createSyncularReact() {
174
183
  disconnect,
175
184
  start,
176
185
  resetLocalState,
186
+ getTransportHealth,
187
+ getProgress,
188
+ getDiagnostics,
189
+ listSubscriptionStates,
190
+ getSubscriptionState,
191
+ reset,
192
+ repair,
193
+ awaitPhase,
194
+ awaitBootstrapComplete,
177
195
  };
178
196
  }
179
- function useSyncStatus() {
197
+ function useSyncStatus(options = {}) {
198
+ const { staleAfterMs } = options;
180
199
  const engine = useEngine();
181
200
  const state = useSyncExternalStore(useCallback((callback) => engine.subscribe(callback), [engine]), useCallback(() => engine.getState(), [engine]), useCallback(() => engine.getState(), [engine]));
182
- return useMemo(() => ({
183
- enabled: state.enabled,
184
- isOnline: state.connectionState === 'connected',
185
- isSyncing: state.isSyncing,
186
- lastSyncAt: state.lastSyncAt,
187
- pendingCount: state.pendingCount,
188
- error: state.error,
189
- isRetrying: state.isRetrying,
190
- retryCount: state.retryCount,
191
- }), [state]);
201
+ const [staleClock, setStaleClock] = useState(Date.now());
202
+ useEffect(() => {
203
+ if (staleAfterMs === undefined || staleAfterMs <= 0)
204
+ return;
205
+ const intervalMs = Math.min(1000, Math.max(100, Math.floor(staleAfterMs / 2)));
206
+ const timer = setInterval(() => {
207
+ setStaleClock(Date.now());
208
+ }, intervalMs);
209
+ return () => clearInterval(timer);
210
+ }, [staleAfterMs]);
211
+ return useMemo(() => {
212
+ const now = staleAfterMs !== undefined ? staleClock : Date.now();
213
+ const lastSyncAgeMs = state.lastSyncAt === null ? null : Math.max(0, now - state.lastSyncAt);
214
+ const isStale = staleAfterMs !== undefined && staleAfterMs > 0
215
+ ? state.lastSyncAt === null || (lastSyncAgeMs ?? 0) > staleAfterMs
216
+ : false;
217
+ return {
218
+ enabled: state.enabled,
219
+ isOnline: state.connectionState === 'connected',
220
+ isSyncing: state.isSyncing,
221
+ lastSyncAt: state.lastSyncAt,
222
+ lastSyncAgeMs,
223
+ isStale,
224
+ pendingCount: state.pendingCount,
225
+ error: state.error,
226
+ isRetrying: state.isRetrying,
227
+ retryCount: state.retryCount,
228
+ };
229
+ }, [state, staleAfterMs, staleClock]);
192
230
  }
193
231
  function useSyncConnection() {
194
232
  const engine = useEngine();
@@ -209,6 +247,180 @@ export function createSyncularReact() {
209
247
  disconnect,
210
248
  ]);
211
249
  }
250
+ function useTransportHealth() {
251
+ const engine = useEngine();
252
+ const health = useSyncExternalStore(useCallback((callback) => {
253
+ const unsubscribers = [
254
+ engine.subscribe(callback),
255
+ engine.on('connection:change', callback),
256
+ engine.on('sync:complete', callback),
257
+ engine.on('sync:error', callback),
258
+ ];
259
+ return () => {
260
+ for (const unsubscribe of unsubscribers)
261
+ unsubscribe();
262
+ };
263
+ }, [engine]), useCallback(() => engine.getTransportHealth(), [engine]), useCallback(() => engine.getTransportHealth(), [engine]));
264
+ return useMemo(() => ({ health }), [health]);
265
+ }
266
+ function useSyncProgress(options = {}) {
267
+ const engine = useEngine();
268
+ const { pollIntervalMs = 500 } = options;
269
+ const [progress, setProgress] = useState(null);
270
+ const [isLoading, setIsLoading] = useState(true);
271
+ const [error, setError] = useState(null);
272
+ const loadedRef = useRef(false);
273
+ const refresh = useCallback(async () => {
274
+ if (!loadedRef.current) {
275
+ setIsLoading(true);
276
+ }
277
+ try {
278
+ const next = await engine.getProgress();
279
+ setProgress(next);
280
+ setError(null);
281
+ }
282
+ catch (err) {
283
+ setError(err instanceof Error ? err : new Error(String(err)));
284
+ }
285
+ finally {
286
+ loadedRef.current = true;
287
+ setIsLoading(false);
288
+ }
289
+ }, [engine]);
290
+ useEffect(() => {
291
+ void refresh();
292
+ }, [refresh]);
293
+ useEffect(() => {
294
+ const unsubscribers = [
295
+ engine.on('sync:start', refresh),
296
+ engine.on('sync:complete', refresh),
297
+ engine.on('sync:error', refresh),
298
+ engine.on('bootstrap:start', refresh),
299
+ engine.on('bootstrap:progress', refresh),
300
+ engine.on('bootstrap:complete', refresh),
301
+ ];
302
+ return () => {
303
+ for (const unsubscribe of unsubscribers)
304
+ unsubscribe();
305
+ };
306
+ }, [engine, refresh]);
307
+ useEffect(() => {
308
+ if (pollIntervalMs <= 0)
309
+ return;
310
+ if (progress?.channelPhase !== 'bootstrapping')
311
+ return;
312
+ const timer = setInterval(() => {
313
+ void refresh();
314
+ }, pollIntervalMs);
315
+ return () => clearInterval(timer);
316
+ }, [pollIntervalMs, progress?.channelPhase, refresh]);
317
+ return useMemo(() => ({
318
+ progress,
319
+ isLoading,
320
+ error,
321
+ refresh,
322
+ }), [progress, isLoading, error, refresh]);
323
+ }
324
+ function useSyncSubscriptions(options = {}) {
325
+ const engine = useEngine();
326
+ const { stateId, table, status } = options;
327
+ const [subscriptions, setSubscriptions] = useState([]);
328
+ const [isLoading, setIsLoading] = useState(true);
329
+ const [error, setError] = useState(null);
330
+ const loadedRef = useRef(false);
331
+ const refresh = useCallback(async () => {
332
+ if (!loadedRef.current) {
333
+ setIsLoading(true);
334
+ }
335
+ try {
336
+ const next = await engine.listSubscriptionStates({
337
+ stateId,
338
+ table,
339
+ status,
340
+ });
341
+ setSubscriptions(next);
342
+ setError(null);
343
+ }
344
+ catch (err) {
345
+ setError(err instanceof Error ? err : new Error(String(err)));
346
+ }
347
+ finally {
348
+ loadedRef.current = true;
349
+ setIsLoading(false);
350
+ }
351
+ }, [engine, stateId, table, status]);
352
+ useEffect(() => {
353
+ void refresh();
354
+ }, [refresh]);
355
+ useEffect(() => {
356
+ const unsubscribers = [
357
+ engine.on('sync:complete', refresh),
358
+ engine.on('sync:error', refresh),
359
+ engine.on('bootstrap:start', refresh),
360
+ engine.on('bootstrap:progress', refresh),
361
+ engine.on('bootstrap:complete', refresh),
362
+ ];
363
+ return () => {
364
+ for (const unsubscribe of unsubscribers)
365
+ unsubscribe();
366
+ };
367
+ }, [engine, refresh]);
368
+ return useMemo(() => ({
369
+ subscriptions,
370
+ isLoading,
371
+ error,
372
+ refresh,
373
+ }), [subscriptions, isLoading, error, refresh]);
374
+ }
375
+ function useSyncSubscription(subscriptionId, options = {}) {
376
+ const engine = useEngine();
377
+ const { stateId } = options;
378
+ const [subscription, setSubscription] = useState(null);
379
+ const [isLoading, setIsLoading] = useState(true);
380
+ const [error, setError] = useState(null);
381
+ const loadedRef = useRef(false);
382
+ const refresh = useCallback(async () => {
383
+ if (!loadedRef.current) {
384
+ setIsLoading(true);
385
+ }
386
+ try {
387
+ const next = await engine.getSubscriptionState(subscriptionId, {
388
+ stateId,
389
+ });
390
+ setSubscription(next);
391
+ setError(null);
392
+ }
393
+ catch (err) {
394
+ setError(err instanceof Error ? err : new Error(String(err)));
395
+ }
396
+ finally {
397
+ loadedRef.current = true;
398
+ setIsLoading(false);
399
+ }
400
+ }, [engine, stateId, subscriptionId]);
401
+ useEffect(() => {
402
+ void refresh();
403
+ }, [refresh]);
404
+ useEffect(() => {
405
+ const unsubscribers = [
406
+ engine.on('sync:complete', refresh),
407
+ engine.on('sync:error', refresh),
408
+ engine.on('bootstrap:start', refresh),
409
+ engine.on('bootstrap:progress', refresh),
410
+ engine.on('bootstrap:complete', refresh),
411
+ ];
412
+ return () => {
413
+ for (const unsubscribe of unsubscribers)
414
+ unsubscribe();
415
+ };
416
+ }, [engine, refresh]);
417
+ return useMemo(() => ({
418
+ subscription,
419
+ isLoading,
420
+ error,
421
+ refresh,
422
+ }), [subscription, isLoading, error, refresh]);
423
+ }
212
424
  function useConflicts() {
213
425
  const engine = useEngine();
214
426
  const [conflicts, setConflicts] = useState([]);
@@ -298,14 +510,17 @@ export function createSyncularReact() {
298
510
  }), [resolve, isPending, error, reset]);
299
511
  }
300
512
  function useSyncQuery(queryFn, options = {}) {
301
- const { enabled = true, deps = [], keyField = 'id' } = options;
513
+ const { enabled = true, deps = [], keyField = 'id', watchTables = [], pollIntervalMs, staleAfterMs, } = options;
302
514
  const { db } = useSyncContext();
303
515
  const engine = useEngine();
516
+ const watchTablesSet = useMemo(() => new Set(watchTables), [watchTables]);
304
517
  const queryFnRef = useRef(queryFn);
305
518
  queryFnRef.current = queryFn;
306
519
  const [data, setData] = useState(undefined);
307
520
  const [isLoading, setIsLoading] = useState(true);
308
521
  const [error, setError] = useState(null);
522
+ const [lastSyncAt, setLastSyncAt] = useState(() => engine.getState().lastSyncAt);
523
+ const [staleClock, setStaleClock] = useState(Date.now());
309
524
  const versionRef = useRef(0);
310
525
  const watchedScopesRef = useRef(new Set());
311
526
  const fingerprintCollectorRef = useRef(new FingerprintCollector());
@@ -317,6 +532,7 @@ export function createSyncularReact() {
317
532
  previousFingerprintRef.current = 'disabled';
318
533
  setData(undefined);
319
534
  }
535
+ setLastSyncAt(engine.getState().lastSyncAt);
320
536
  setIsLoading(false);
321
537
  hasLoadedRef.current = true;
322
538
  return;
@@ -335,6 +551,7 @@ export function createSyncularReact() {
335
551
  : await fnResult;
336
552
  if (version === versionRef.current) {
337
553
  watchedScopesRef.current = scopeCollector;
554
+ setLastSyncAt(engine.getState().lastSyncAt);
338
555
  const fingerprint = fingerprintCollectorRef.current.getCombined();
339
556
  if (fingerprint !== previousFingerprintRef.current ||
340
557
  fingerprint === '') {
@@ -360,11 +577,18 @@ export function createSyncularReact() {
360
577
  executeQuery();
361
578
  // eslint-disable-next-line react-hooks/exhaustive-deps
362
579
  }, [executeQuery, ...deps]);
580
+ useEffect(() => {
581
+ const unsubscribe = engine.subscribe(() => {
582
+ const nextLastSyncAt = engine.getState().lastSyncAt;
583
+ setLastSyncAt((previous) => previous === nextLastSyncAt ? previous : nextLastSyncAt);
584
+ });
585
+ return unsubscribe;
586
+ }, [engine]);
363
587
  useEffect(() => {
364
588
  if (!enabled)
365
589
  return;
366
590
  const unsubscribe = engine.on('sync:complete', () => {
367
- executeQuery();
591
+ void executeQuery();
368
592
  });
369
593
  return unsubscribe;
370
594
  }, [engine, enabled, executeQuery]);
@@ -372,26 +596,57 @@ export function createSyncularReact() {
372
596
  if (!enabled)
373
597
  return;
374
598
  const unsubscribe = engine.on('data:change', (event) => {
375
- const changedScopes = event.scopes || [];
599
+ const changedScopes = event.scopes ?? [];
376
600
  const watchedScopes = watchedScopesRef.current;
377
- if (watchedScopes.size > 0) {
378
- const hasWatchedScope = changedScopes.some((s) => watchedScopes.has(s));
379
- if (!hasWatchedScope)
601
+ const hasDynamicFilter = watchedScopes.size > 0;
602
+ const hasTableFilter = watchTablesSet.size > 0;
603
+ if (hasDynamicFilter || hasTableFilter) {
604
+ const matchesDynamic = changedScopes.some((scope) => watchedScopes.has(scope));
605
+ const matchesConfigured = changedScopes.some((scope) => watchTablesSet.has(scope));
606
+ if (!matchesDynamic && !matchesConfigured) {
380
607
  return;
608
+ }
381
609
  }
382
- executeQuery();
610
+ void executeQuery();
383
611
  });
384
612
  return unsubscribe;
385
- }, [engine, enabled, executeQuery]);
613
+ }, [engine, enabled, executeQuery, watchTablesSet]);
614
+ useEffect(() => {
615
+ if (!enabled)
616
+ return;
617
+ if (pollIntervalMs === undefined || pollIntervalMs <= 0)
618
+ return;
619
+ const timer = setInterval(() => {
620
+ void executeQuery();
621
+ }, pollIntervalMs);
622
+ return () => clearInterval(timer);
623
+ }, [enabled, pollIntervalMs, executeQuery]);
624
+ useEffect(() => {
625
+ if (staleAfterMs === undefined || staleAfterMs <= 0)
626
+ return;
627
+ const intervalMs = Math.min(1000, Math.max(100, Math.floor(staleAfterMs / 2)));
628
+ const timer = setInterval(() => {
629
+ setStaleClock(Date.now());
630
+ }, intervalMs);
631
+ return () => clearInterval(timer);
632
+ }, [staleAfterMs]);
386
633
  const refetch = useCallback(async () => {
387
634
  await executeQuery();
388
635
  }, [executeQuery]);
389
- return useMemo(() => ({
390
- data,
391
- isLoading,
392
- error,
393
- refetch,
394
- }), [data, isLoading, error, refetch]);
636
+ return useMemo(() => {
637
+ const now = staleAfterMs !== undefined ? staleClock : Date.now();
638
+ const isStale = staleAfterMs !== undefined && staleAfterMs > 0
639
+ ? lastSyncAt === null || now - lastSyncAt > staleAfterMs
640
+ : false;
641
+ return {
642
+ data,
643
+ isLoading,
644
+ error,
645
+ isStale,
646
+ lastSyncAt,
647
+ refetch,
648
+ };
649
+ }, [data, isLoading, error, staleAfterMs, staleClock, lastSyncAt, refetch]);
395
650
  }
396
651
  function useQuery(queryFn, options = {}) {
397
652
  const { enabled = true, deps = [], keyField = 'id' } = options;
@@ -801,6 +1056,10 @@ export function createSyncularReact() {
801
1056
  useSyncEngine,
802
1057
  useSyncStatus,
803
1058
  useSyncConnection,
1059
+ useTransportHealth,
1060
+ useSyncProgress,
1061
+ useSyncSubscriptions,
1062
+ useSyncSubscription,
804
1063
  useSyncQuery,
805
1064
  useQuery,
806
1065
  useMutation,