@syncular/client-react 0.0.2-2 → 0.0.3-14

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, SyncInspectorOptions, SyncInspectorSnapshot, 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,42 @@ 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
+ getInspectorSnapshot: (options?: SyncInspectorOptions) => Promise<SyncInspectorSnapshot>;
61
+ listSubscriptionStates: (args?: {
62
+ stateId?: string;
63
+ table?: string;
64
+ status?: 'active' | 'revoked';
65
+ }) => Promise<SubscriptionState[]>;
66
+ getSubscriptionState: (subscriptionId: string, options?: {
67
+ stateId?: string;
68
+ }) => Promise<SubscriptionState | null>;
69
+ reset: (options: SyncResetOptions) => Promise<SyncResetResult>;
70
+ repair: (options: SyncRepairOptions) => Promise<SyncResetResult>;
71
+ awaitPhase: (phase: SyncProgress['channelPhase'], options?: SyncAwaitPhaseOptions) => Promise<SyncProgress>;
72
+ awaitBootstrapComplete: (options?: SyncAwaitBootstrapOptions) => Promise<SyncProgress>;
57
73
  }
58
74
  export interface SyncStatus {
59
75
  enabled: boolean;
60
76
  isOnline: boolean;
61
77
  isSyncing: boolean;
62
78
  lastSyncAt: number | null;
79
+ lastSyncAgeMs: number | null;
80
+ isStale: boolean;
63
81
  pendingCount: number;
64
82
  error: SyncError | null;
65
83
  isRetrying: boolean;
66
84
  retryCount: number;
67
85
  }
86
+ export interface UseSyncStatusOptions {
87
+ /**
88
+ * Mark status as stale when `Date.now() - lastSyncAt` exceeds this value.
89
+ * If omitted, `isStale` is always false.
90
+ */
91
+ staleAfterMs?: number;
92
+ }
68
93
  export interface UseSyncConnectionResult {
69
94
  state: SyncConnectionState;
70
95
  mode: SyncTransportMode;
@@ -73,6 +98,56 @@ export interface UseSyncConnectionResult {
73
98
  reconnect: () => void;
74
99
  disconnect: () => void;
75
100
  }
101
+ export interface UseTransportHealthResult {
102
+ health: TransportHealth;
103
+ }
104
+ export interface UseSyncProgressOptions {
105
+ /**
106
+ * Polling interval while bootstrapping.
107
+ * Set to 0 to disable interval refresh.
108
+ */
109
+ pollIntervalMs?: number;
110
+ }
111
+ export interface UseSyncProgressResult {
112
+ progress: SyncProgress | null;
113
+ isLoading: boolean;
114
+ error: Error | null;
115
+ refresh: () => Promise<void>;
116
+ }
117
+ export interface UseSyncInspectorOptions {
118
+ /**
119
+ * Polling interval for refreshing inspector snapshots.
120
+ * Set to 0 to disable interval refresh.
121
+ */
122
+ pollIntervalMs?: number;
123
+ /**
124
+ * Max number of recent events in the snapshot.
125
+ */
126
+ eventLimit?: number;
127
+ }
128
+ export interface UseSyncInspectorResult {
129
+ snapshot: SyncInspectorSnapshot | null;
130
+ isLoading: boolean;
131
+ error: Error | null;
132
+ refresh: () => Promise<void>;
133
+ }
134
+ export interface UseSyncSubscriptionsOptions {
135
+ stateId?: string;
136
+ table?: string;
137
+ status?: 'active' | 'revoked';
138
+ }
139
+ export interface UseSyncSubscriptionsResult {
140
+ subscriptions: SubscriptionState[];
141
+ isLoading: boolean;
142
+ error: Error | null;
143
+ refresh: () => Promise<void>;
144
+ }
145
+ export interface UseSyncSubscriptionResult {
146
+ subscription: SubscriptionState | null;
147
+ isLoading: boolean;
148
+ error: Error | null;
149
+ refresh: () => Promise<void>;
150
+ }
76
151
  export interface UseConflictsResult {
77
152
  conflicts: ConflictInfo[];
78
153
  count: number;
@@ -96,12 +171,17 @@ export interface UseSyncQueryResult<T> {
96
171
  data: T | undefined;
97
172
  isLoading: boolean;
98
173
  error: Error | null;
174
+ isStale: boolean;
175
+ lastSyncAt: number | null;
99
176
  refetch: () => Promise<void>;
100
177
  }
101
178
  export interface UseSyncQueryOptions {
102
179
  enabled?: boolean;
103
180
  deps?: unknown[];
104
181
  keyField?: string;
182
+ watchTables?: string[];
183
+ pollIntervalMs?: number;
184
+ staleAfterMs?: number;
105
185
  }
106
186
  export interface UseQueryResult<T> {
107
187
  data: T | undefined;
@@ -208,8 +288,15 @@ export declare function createSyncularReact<DB extends SyncClientDb>(): {
208
288
  readonly useSyncContext: () => SyncContextValue<DB>;
209
289
  readonly useEngine: () => SyncEngine<DB>;
210
290
  readonly useSyncEngine: () => UseSyncEngineResult;
211
- readonly useSyncStatus: () => SyncStatus;
291
+ readonly useSyncStatus: (options?: UseSyncStatusOptions) => SyncStatus;
212
292
  readonly useSyncConnection: () => UseSyncConnectionResult;
293
+ readonly useTransportHealth: () => UseTransportHealthResult;
294
+ readonly useSyncProgress: (options?: UseSyncProgressOptions) => UseSyncProgressResult;
295
+ readonly useSyncInspector: (options?: UseSyncInspectorOptions) => UseSyncInspectorResult;
296
+ readonly useSyncSubscriptions: (options?: UseSyncSubscriptionsOptions) => UseSyncSubscriptionsResult;
297
+ readonly useSyncSubscription: (subscriptionId: string, options?: {
298
+ stateId?: string | undefined;
299
+ }) => UseSyncSubscriptionResult;
213
300
  readonly useSyncQuery: <TResult>(queryFn: (ctx: QueryContext<DB>) => ExecutableQuery<TResult> | Promise<TResult>, options?: UseSyncQueryOptions) => UseSyncQueryResult<TResult>;
214
301
  readonly useQuery: <TResult>(queryFn: (ctx: QueryContext<DB>) => ExecutableQuery<TResult> | Promise<TResult>, options?: UseQueryOptions) => UseQueryResult<TResult>;
215
302
  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,16 @@ 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 getInspectorSnapshot = useCallback((options) => engine.getInspectorSnapshot(options), [engine]);
174
+ const listSubscriptionStates = useCallback((args) => engine.listSubscriptionStates(args), [engine]);
175
+ const getSubscriptionState = useCallback((subscriptionId, options) => engine.getSubscriptionState(subscriptionId, options), [engine]);
176
+ const reset = useCallback((options) => engine.reset(options), [engine]);
177
+ const repair = useCallback((options) => engine.repair(options), [engine]);
178
+ const awaitPhase = useCallback((phase, options) => engine.awaitPhase(phase, options), [engine]);
179
+ const awaitBootstrapComplete = useCallback((options) => engine.awaitBootstrapComplete(options), [engine]);
170
180
  return {
171
181
  state,
172
182
  sync,
@@ -174,21 +184,51 @@ export function createSyncularReact() {
174
184
  disconnect,
175
185
  start,
176
186
  resetLocalState,
187
+ getTransportHealth,
188
+ getProgress,
189
+ getDiagnostics,
190
+ getInspectorSnapshot,
191
+ listSubscriptionStates,
192
+ getSubscriptionState,
193
+ reset,
194
+ repair,
195
+ awaitPhase,
196
+ awaitBootstrapComplete,
177
197
  };
178
198
  }
179
- function useSyncStatus() {
199
+ function useSyncStatus(options = {}) {
200
+ const { staleAfterMs } = options;
180
201
  const engine = useEngine();
181
202
  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]);
203
+ const [staleClock, setStaleClock] = useState(Date.now());
204
+ useEffect(() => {
205
+ if (staleAfterMs === undefined || staleAfterMs <= 0)
206
+ return;
207
+ const intervalMs = Math.min(1000, Math.max(100, Math.floor(staleAfterMs / 2)));
208
+ const timer = setInterval(() => {
209
+ setStaleClock(Date.now());
210
+ }, intervalMs);
211
+ return () => clearInterval(timer);
212
+ }, [staleAfterMs]);
213
+ return useMemo(() => {
214
+ const now = staleAfterMs !== undefined ? staleClock : Date.now();
215
+ const lastSyncAgeMs = state.lastSyncAt === null ? null : Math.max(0, now - state.lastSyncAt);
216
+ const isStale = staleAfterMs !== undefined && staleAfterMs > 0
217
+ ? state.lastSyncAt === null || (lastSyncAgeMs ?? 0) > staleAfterMs
218
+ : false;
219
+ return {
220
+ enabled: state.enabled,
221
+ isOnline: state.connectionState === 'connected',
222
+ isSyncing: state.isSyncing,
223
+ lastSyncAt: state.lastSyncAt,
224
+ lastSyncAgeMs,
225
+ isStale,
226
+ pendingCount: state.pendingCount,
227
+ error: state.error,
228
+ isRetrying: state.isRetrying,
229
+ retryCount: state.retryCount,
230
+ };
231
+ }, [state, staleAfterMs, staleClock]);
192
232
  }
193
233
  function useSyncConnection() {
194
234
  const engine = useEngine();
@@ -209,6 +249,239 @@ export function createSyncularReact() {
209
249
  disconnect,
210
250
  ]);
211
251
  }
252
+ function useTransportHealth() {
253
+ const engine = useEngine();
254
+ const health = useSyncExternalStore(useCallback((callback) => {
255
+ const unsubscribers = [
256
+ engine.subscribe(callback),
257
+ engine.on('connection:change', callback),
258
+ engine.on('sync:complete', callback),
259
+ engine.on('sync:error', callback),
260
+ ];
261
+ return () => {
262
+ for (const unsubscribe of unsubscribers)
263
+ unsubscribe();
264
+ };
265
+ }, [engine]), useCallback(() => engine.getTransportHealth(), [engine]), useCallback(() => engine.getTransportHealth(), [engine]));
266
+ return useMemo(() => ({ health }), [health]);
267
+ }
268
+ function useSyncProgress(options = {}) {
269
+ const engine = useEngine();
270
+ const { pollIntervalMs = 500 } = options;
271
+ const [progress, setProgress] = useState(null);
272
+ const [isLoading, setIsLoading] = useState(true);
273
+ const [error, setError] = useState(null);
274
+ const loadedRef = useRef(false);
275
+ const refresh = useCallback(async () => {
276
+ if (!loadedRef.current) {
277
+ setIsLoading(true);
278
+ }
279
+ try {
280
+ const next = await engine.getProgress();
281
+ setProgress(next);
282
+ setError(null);
283
+ }
284
+ catch (err) {
285
+ setError(err instanceof Error ? err : new Error(String(err)));
286
+ }
287
+ finally {
288
+ loadedRef.current = true;
289
+ setIsLoading(false);
290
+ }
291
+ }, [engine]);
292
+ useEffect(() => {
293
+ void refresh();
294
+ }, [refresh]);
295
+ useEffect(() => {
296
+ const unsubscribers = [
297
+ engine.on('sync:start', refresh),
298
+ engine.on('sync:complete', refresh),
299
+ engine.on('sync:error', refresh),
300
+ engine.on('bootstrap:start', refresh),
301
+ engine.on('bootstrap:progress', refresh),
302
+ engine.on('bootstrap:complete', refresh),
303
+ ];
304
+ return () => {
305
+ for (const unsubscribe of unsubscribers)
306
+ unsubscribe();
307
+ };
308
+ }, [engine, refresh]);
309
+ useEffect(() => {
310
+ if (pollIntervalMs <= 0)
311
+ return;
312
+ if (progress?.channelPhase !== 'bootstrapping')
313
+ return;
314
+ const timer = setInterval(() => {
315
+ void refresh();
316
+ }, pollIntervalMs);
317
+ return () => clearInterval(timer);
318
+ }, [pollIntervalMs, progress?.channelPhase, refresh]);
319
+ return useMemo(() => ({
320
+ progress,
321
+ isLoading,
322
+ error,
323
+ refresh,
324
+ }), [progress, isLoading, error, refresh]);
325
+ }
326
+ function useSyncInspector(options = {}) {
327
+ const engine = useEngine();
328
+ const { pollIntervalMs = 2_000, eventLimit } = options;
329
+ const [snapshot, setSnapshot] = useState(null);
330
+ const [isLoading, setIsLoading] = useState(true);
331
+ const [error, setError] = useState(null);
332
+ const loadedRef = useRef(false);
333
+ const refresh = useCallback(async () => {
334
+ if (!loadedRef.current) {
335
+ setIsLoading(true);
336
+ }
337
+ try {
338
+ const next = await engine.getInspectorSnapshot({ eventLimit });
339
+ setSnapshot(next);
340
+ setError(null);
341
+ }
342
+ catch (err) {
343
+ setError(err instanceof Error ? err : new Error(String(err)));
344
+ }
345
+ finally {
346
+ loadedRef.current = true;
347
+ setIsLoading(false);
348
+ }
349
+ }, [engine, eventLimit]);
350
+ useEffect(() => {
351
+ void refresh();
352
+ }, [refresh]);
353
+ useEffect(() => {
354
+ const unsubscribers = [
355
+ engine.on('sync:start', refresh),
356
+ engine.on('sync:complete', refresh),
357
+ engine.on('sync:error', refresh),
358
+ engine.on('bootstrap:start', refresh),
359
+ engine.on('bootstrap:progress', refresh),
360
+ engine.on('bootstrap:complete', refresh),
361
+ engine.on('connection:change', refresh),
362
+ engine.on('outbox:change', refresh),
363
+ engine.on('data:change', refresh),
364
+ ];
365
+ return () => {
366
+ for (const unsubscribe of unsubscribers)
367
+ unsubscribe();
368
+ };
369
+ }, [engine, refresh]);
370
+ useEffect(() => {
371
+ if (pollIntervalMs <= 0)
372
+ return;
373
+ const timer = setInterval(() => {
374
+ void refresh();
375
+ }, pollIntervalMs);
376
+ return () => clearInterval(timer);
377
+ }, [pollIntervalMs, refresh]);
378
+ return useMemo(() => ({
379
+ snapshot,
380
+ isLoading,
381
+ error,
382
+ refresh,
383
+ }), [snapshot, isLoading, error, refresh]);
384
+ }
385
+ function useSyncSubscriptions(options = {}) {
386
+ const engine = useEngine();
387
+ const { stateId, table, status } = options;
388
+ const [subscriptions, setSubscriptions] = useState([]);
389
+ const [isLoading, setIsLoading] = useState(true);
390
+ const [error, setError] = useState(null);
391
+ const loadedRef = useRef(false);
392
+ const refresh = useCallback(async () => {
393
+ if (!loadedRef.current) {
394
+ setIsLoading(true);
395
+ }
396
+ try {
397
+ const next = await engine.listSubscriptionStates({
398
+ stateId,
399
+ table,
400
+ status,
401
+ });
402
+ setSubscriptions(next);
403
+ setError(null);
404
+ }
405
+ catch (err) {
406
+ setError(err instanceof Error ? err : new Error(String(err)));
407
+ }
408
+ finally {
409
+ loadedRef.current = true;
410
+ setIsLoading(false);
411
+ }
412
+ }, [engine, stateId, table, status]);
413
+ useEffect(() => {
414
+ void refresh();
415
+ }, [refresh]);
416
+ useEffect(() => {
417
+ const unsubscribers = [
418
+ engine.on('sync:complete', refresh),
419
+ engine.on('sync:error', refresh),
420
+ engine.on('bootstrap:start', refresh),
421
+ engine.on('bootstrap:progress', refresh),
422
+ engine.on('bootstrap:complete', refresh),
423
+ ];
424
+ return () => {
425
+ for (const unsubscribe of unsubscribers)
426
+ unsubscribe();
427
+ };
428
+ }, [engine, refresh]);
429
+ return useMemo(() => ({
430
+ subscriptions,
431
+ isLoading,
432
+ error,
433
+ refresh,
434
+ }), [subscriptions, isLoading, error, refresh]);
435
+ }
436
+ function useSyncSubscription(subscriptionId, options = {}) {
437
+ const engine = useEngine();
438
+ const { stateId } = options;
439
+ const [subscription, setSubscription] = useState(null);
440
+ const [isLoading, setIsLoading] = useState(true);
441
+ const [error, setError] = useState(null);
442
+ const loadedRef = useRef(false);
443
+ const refresh = useCallback(async () => {
444
+ if (!loadedRef.current) {
445
+ setIsLoading(true);
446
+ }
447
+ try {
448
+ const next = await engine.getSubscriptionState(subscriptionId, {
449
+ stateId,
450
+ });
451
+ setSubscription(next);
452
+ setError(null);
453
+ }
454
+ catch (err) {
455
+ setError(err instanceof Error ? err : new Error(String(err)));
456
+ }
457
+ finally {
458
+ loadedRef.current = true;
459
+ setIsLoading(false);
460
+ }
461
+ }, [engine, stateId, subscriptionId]);
462
+ useEffect(() => {
463
+ void refresh();
464
+ }, [refresh]);
465
+ useEffect(() => {
466
+ const unsubscribers = [
467
+ engine.on('sync:complete', refresh),
468
+ engine.on('sync:error', refresh),
469
+ engine.on('bootstrap:start', refresh),
470
+ engine.on('bootstrap:progress', refresh),
471
+ engine.on('bootstrap:complete', refresh),
472
+ ];
473
+ return () => {
474
+ for (const unsubscribe of unsubscribers)
475
+ unsubscribe();
476
+ };
477
+ }, [engine, refresh]);
478
+ return useMemo(() => ({
479
+ subscription,
480
+ isLoading,
481
+ error,
482
+ refresh,
483
+ }), [subscription, isLoading, error, refresh]);
484
+ }
212
485
  function useConflicts() {
213
486
  const engine = useEngine();
214
487
  const [conflicts, setConflicts] = useState([]);
@@ -298,14 +571,17 @@ export function createSyncularReact() {
298
571
  }), [resolve, isPending, error, reset]);
299
572
  }
300
573
  function useSyncQuery(queryFn, options = {}) {
301
- const { enabled = true, deps = [], keyField = 'id' } = options;
574
+ const { enabled = true, deps = [], keyField = 'id', watchTables = [], pollIntervalMs, staleAfterMs, } = options;
302
575
  const { db } = useSyncContext();
303
576
  const engine = useEngine();
577
+ const watchTablesSet = useMemo(() => new Set(watchTables), [watchTables]);
304
578
  const queryFnRef = useRef(queryFn);
305
579
  queryFnRef.current = queryFn;
306
580
  const [data, setData] = useState(undefined);
307
581
  const [isLoading, setIsLoading] = useState(true);
308
582
  const [error, setError] = useState(null);
583
+ const [lastSyncAt, setLastSyncAt] = useState(() => engine.getState().lastSyncAt);
584
+ const [staleClock, setStaleClock] = useState(Date.now());
309
585
  const versionRef = useRef(0);
310
586
  const watchedScopesRef = useRef(new Set());
311
587
  const fingerprintCollectorRef = useRef(new FingerprintCollector());
@@ -317,6 +593,7 @@ export function createSyncularReact() {
317
593
  previousFingerprintRef.current = 'disabled';
318
594
  setData(undefined);
319
595
  }
596
+ setLastSyncAt(engine.getState().lastSyncAt);
320
597
  setIsLoading(false);
321
598
  hasLoadedRef.current = true;
322
599
  return;
@@ -335,6 +612,7 @@ export function createSyncularReact() {
335
612
  : await fnResult;
336
613
  if (version === versionRef.current) {
337
614
  watchedScopesRef.current = scopeCollector;
615
+ setLastSyncAt(engine.getState().lastSyncAt);
338
616
  const fingerprint = fingerprintCollectorRef.current.getCombined();
339
617
  if (fingerprint !== previousFingerprintRef.current ||
340
618
  fingerprint === '') {
@@ -360,11 +638,18 @@ export function createSyncularReact() {
360
638
  executeQuery();
361
639
  // eslint-disable-next-line react-hooks/exhaustive-deps
362
640
  }, [executeQuery, ...deps]);
641
+ useEffect(() => {
642
+ const unsubscribe = engine.subscribe(() => {
643
+ const nextLastSyncAt = engine.getState().lastSyncAt;
644
+ setLastSyncAt((previous) => previous === nextLastSyncAt ? previous : nextLastSyncAt);
645
+ });
646
+ return unsubscribe;
647
+ }, [engine]);
363
648
  useEffect(() => {
364
649
  if (!enabled)
365
650
  return;
366
651
  const unsubscribe = engine.on('sync:complete', () => {
367
- executeQuery();
652
+ void executeQuery();
368
653
  });
369
654
  return unsubscribe;
370
655
  }, [engine, enabled, executeQuery]);
@@ -372,26 +657,57 @@ export function createSyncularReact() {
372
657
  if (!enabled)
373
658
  return;
374
659
  const unsubscribe = engine.on('data:change', (event) => {
375
- const changedScopes = event.scopes || [];
660
+ const changedScopes = event.scopes ?? [];
376
661
  const watchedScopes = watchedScopesRef.current;
377
- if (watchedScopes.size > 0) {
378
- const hasWatchedScope = changedScopes.some((s) => watchedScopes.has(s));
379
- if (!hasWatchedScope)
662
+ const hasDynamicFilter = watchedScopes.size > 0;
663
+ const hasTableFilter = watchTablesSet.size > 0;
664
+ if (hasDynamicFilter || hasTableFilter) {
665
+ const matchesDynamic = changedScopes.some((scope) => watchedScopes.has(scope));
666
+ const matchesConfigured = changedScopes.some((scope) => watchTablesSet.has(scope));
667
+ if (!matchesDynamic && !matchesConfigured) {
380
668
  return;
669
+ }
381
670
  }
382
- executeQuery();
671
+ void executeQuery();
383
672
  });
384
673
  return unsubscribe;
385
- }, [engine, enabled, executeQuery]);
674
+ }, [engine, enabled, executeQuery, watchTablesSet]);
675
+ useEffect(() => {
676
+ if (!enabled)
677
+ return;
678
+ if (pollIntervalMs === undefined || pollIntervalMs <= 0)
679
+ return;
680
+ const timer = setInterval(() => {
681
+ void executeQuery();
682
+ }, pollIntervalMs);
683
+ return () => clearInterval(timer);
684
+ }, [enabled, pollIntervalMs, executeQuery]);
685
+ useEffect(() => {
686
+ if (staleAfterMs === undefined || staleAfterMs <= 0)
687
+ return;
688
+ const intervalMs = Math.min(1000, Math.max(100, Math.floor(staleAfterMs / 2)));
689
+ const timer = setInterval(() => {
690
+ setStaleClock(Date.now());
691
+ }, intervalMs);
692
+ return () => clearInterval(timer);
693
+ }, [staleAfterMs]);
386
694
  const refetch = useCallback(async () => {
387
695
  await executeQuery();
388
696
  }, [executeQuery]);
389
- return useMemo(() => ({
390
- data,
391
- isLoading,
392
- error,
393
- refetch,
394
- }), [data, isLoading, error, refetch]);
697
+ return useMemo(() => {
698
+ const now = staleAfterMs !== undefined ? staleClock : Date.now();
699
+ const isStale = staleAfterMs !== undefined && staleAfterMs > 0
700
+ ? lastSyncAt === null || now - lastSyncAt > staleAfterMs
701
+ : false;
702
+ return {
703
+ data,
704
+ isLoading,
705
+ error,
706
+ isStale,
707
+ lastSyncAt,
708
+ refetch,
709
+ };
710
+ }, [data, isLoading, error, staleAfterMs, staleClock, lastSyncAt, refetch]);
395
711
  }
396
712
  function useQuery(queryFn, options = {}) {
397
713
  const { enabled = true, deps = [], keyField = 'id' } = options;
@@ -801,6 +1117,11 @@ export function createSyncularReact() {
801
1117
  useSyncEngine,
802
1118
  useSyncStatus,
803
1119
  useSyncConnection,
1120
+ useTransportHealth,
1121
+ useSyncProgress,
1122
+ useSyncInspector,
1123
+ useSyncSubscriptions,
1124
+ useSyncSubscription,
804
1125
  useSyncQuery,
805
1126
  useQuery,
806
1127
  useMutation,