@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.
- package/dist/createSyncularReact.d.ts +70 -2
- package/dist/createSyncularReact.d.ts.map +1 -1
- package/dist/createSyncularReact.js +284 -25
- package/dist/createSyncularReact.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/useSyncGroup.d.ts +42 -0
- package/dist/useSyncGroup.d.ts.map +1 -0
- package/dist/useSyncGroup.js +74 -0
- package/dist/useSyncGroup.js.map +1 -0
- package/package.json +4 -4
- package/src/__tests__/SyncEngine.test.ts +64 -0
- package/src/__tests__/hooks.test.tsx +120 -0
- package/src/createSyncularReact.tsx +458 -20
- package/src/index.ts +14 -0
- package/src/useSyncGroup.ts +169 -0
|
@@ -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":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,YAAY,EAIZ,YAAY,EACZ,gBAAgB,
|
|
1
|
+
{"version":3,"file":"createSyncularReact.d.ts","sourceRoot":"","sources":["../src/createSyncularReact.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EACf,YAAY,EAIZ,iBAAiB,EACjB,yBAAyB,EACzB,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAEf,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,YAAY,EAMjB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EAEjB,KAAK,mBAAmB,EACxB,UAAU,EAEV,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,MAAM,EAAO,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAEL,KAAK,SAAS,EAQf,MAAM,OAAO,CAAC;AAMf,KAAK,eAAe,CAAC,OAAO,IAAI;IAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC,CAAC;AAyBF,MAAM,WAAW,gBAAgB,CAAC,EAAE,SAAS,YAAY;IACvD,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACf,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,mBAAmB,CAAC,EAAE,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,iBAAiB,CAAC,EAAE,SAAS,YAAY;IACxD,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACf,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,mBAAmB,CAAC,EAAE,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,uBAAuB,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC1C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC9C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,4FAA4F;IAC5F,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,eAAe,CAAC;IACvB,IAAI,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,kBAAkB,EAAE,MAAM,QAAQ,CAAC,eAAe,CAAC,CAAC;IACpD,WAAW,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IACzC,cAAc,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/C,sBAAsB,EAAE,CAAC,IAAI,CAAC,EAAE;QAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;KAC/B,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACnC,oBAAoB,EAAE,CACpB,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAC3B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACvC,KAAK,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/D,MAAM,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IACjE,UAAU,EAAE,CACV,KAAK,EAAE,YAAY,CAAC,cAAc,CAAC,EACnC,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC,YAAY,CAAC,CAAC;IAC3B,sBAAsB,EAAE,CACtB,OAAO,CAAC,EAAE,yBAAyB,KAChC,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,IAAI,EAAE,iBAAiB,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,YAAY,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE/D,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,CACP,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACjC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,CAAC,MAAM,SAAS,MAAM,IAC3C;IACE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD;IACE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEN,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc,CAAC,MAAM,SAAS,MAAM;IACnD,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxD,MAAM,EAAE,CACN,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KACtC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,MAAM,EAAE,CACN,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KACtC,OAAO,CAAC,cAAc,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB,CAAC,MAAM,SAAS,MAAM;IACtD,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAC/B,UAAU,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB,CAAC,MAAM,SAAS,MAAM;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,KAAK,QAAQ,GAAG,YAAY,GAAG,OAAO,GAAG,KAAK,CAAC;AAE/C,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACpE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,MAAM,aAAa,CAAC,EAAE,SAAS,YAAY,IAAI,YAAY,CAC/D,EAAE,EACF;IAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,CACpB,GAAG;IACF,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IACnD,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpE,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B,CACzC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEnC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAC5E,SAAQ,iBAAiB,CAAC,SAAS,CAAC;IACpC,cAAc,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,IAAI,CAAC;IAC9C,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,YAAY;;;;;;;;;;;;;4BAwpBnC,OAAO;wBAiMX,OAAO;2BA0FJ,MAAM;;;;;2BAwWN,SAAS;mCAwBD,SAAS;EAyGvC"}
|
|
@@ -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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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,
|