@pol-studios/powersync 1.0.1 → 1.0.2
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/attachments/index.js +1 -1
- package/dist/{chunk-PANEMMTU.js → chunk-3AYXHQ4W.js} +17 -11
- package/dist/chunk-3AYXHQ4W.js.map +1 -0
- package/dist/{chunk-BJ36QDFN.js → chunk-7EMDVIZX.js} +1 -1
- package/dist/chunk-7EMDVIZX.js.map +1 -0
- package/dist/{chunk-MB2RC3NS.js → chunk-C2RSTGDC.js} +129 -89
- package/dist/chunk-C2RSTGDC.js.map +1 -0
- package/dist/{chunk-NPNBGCRC.js → chunk-EJ23MXPQ.js} +1 -1
- package/dist/{chunk-NPNBGCRC.js.map → chunk-EJ23MXPQ.js.map} +1 -1
- package/dist/{chunk-CHRTN5PF.js → chunk-FPTDATY5.js} +1 -1
- package/dist/chunk-FPTDATY5.js.map +1 -0
- package/dist/chunk-GMFDCVMZ.js +1285 -0
- package/dist/chunk-GMFDCVMZ.js.map +1 -0
- package/dist/chunk-OLHGI472.js +1 -0
- package/dist/chunk-OLHGI472.js.map +1 -0
- package/dist/{chunk-CFCK2LHI.js → chunk-OTJXIRWX.js} +45 -40
- package/dist/chunk-OTJXIRWX.js.map +1 -0
- package/dist/{chunk-GBGATW2S.js → chunk-V6LJ6MR2.js} +86 -95
- package/dist/chunk-V6LJ6MR2.js.map +1 -0
- package/dist/connector/index.d.ts +1 -1
- package/dist/connector/index.js +2 -2
- package/dist/core/index.js +2 -2
- package/dist/{index-D952Qr38.d.ts → index-Cb-NI0Ct.d.ts} +9 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -9
- package/dist/index.native.d.ts +1 -1
- package/dist/index.native.js +9 -10
- package/dist/index.web.d.ts +1 -1
- package/dist/index.web.js +9 -10
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/index.native.js +1 -1
- package/dist/platform/index.web.js +1 -1
- package/dist/provider/index.d.ts +6 -1
- package/dist/provider/index.js +6 -6
- package/dist/sync/index.js +3 -3
- package/package.json +3 -1
- package/dist/chunk-42IJ25Q4.js +0 -45
- package/dist/chunk-42IJ25Q4.js.map +0 -1
- package/dist/chunk-BJ36QDFN.js.map +0 -1
- package/dist/chunk-CFCK2LHI.js.map +0 -1
- package/dist/chunk-CHRTN5PF.js.map +0 -1
- package/dist/chunk-GBGATW2S.js.map +0 -1
- package/dist/chunk-H7HZMI4H.js +0 -925
- package/dist/chunk-H7HZMI4H.js.map +0 -1
- package/dist/chunk-MB2RC3NS.js.map +0 -1
- package/dist/chunk-PANEMMTU.js.map +0 -1
|
@@ -0,0 +1,1285 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AttachmentQueue
|
|
3
|
+
} from "./chunk-V6LJ6MR2.js";
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_CONNECTION_HEALTH,
|
|
6
|
+
DEFAULT_SYNC_METRICS,
|
|
7
|
+
DEFAULT_SYNC_STATUS,
|
|
8
|
+
HealthMonitor,
|
|
9
|
+
MetricsCollector,
|
|
10
|
+
SyncStatusTracker
|
|
11
|
+
} from "./chunk-OTJXIRWX.js";
|
|
12
|
+
import {
|
|
13
|
+
SupabaseConnector
|
|
14
|
+
} from "./chunk-C2RSTGDC.js";
|
|
15
|
+
import {
|
|
16
|
+
createSyncError,
|
|
17
|
+
extractEntityIds,
|
|
18
|
+
extractTableNames
|
|
19
|
+
} from "./chunk-FPTDATY5.js";
|
|
20
|
+
|
|
21
|
+
// src/provider/context.ts
|
|
22
|
+
import { createContext } from "react";
|
|
23
|
+
var PowerSyncContext = createContext(null);
|
|
24
|
+
PowerSyncContext.displayName = "PowerSyncContext";
|
|
25
|
+
var SyncStatusContext = createContext(null);
|
|
26
|
+
SyncStatusContext.displayName = "SyncStatusContext";
|
|
27
|
+
var ConnectionHealthContext = createContext(null);
|
|
28
|
+
ConnectionHealthContext.displayName = "ConnectionHealthContext";
|
|
29
|
+
var SyncMetricsContext = createContext(null);
|
|
30
|
+
SyncMetricsContext.displayName = "SyncMetricsContext";
|
|
31
|
+
var AttachmentQueueContext = createContext(null);
|
|
32
|
+
AttachmentQueueContext.displayName = "AttachmentQueueContext";
|
|
33
|
+
|
|
34
|
+
// src/provider/PowerSyncProvider.tsx
|
|
35
|
+
import { useEffect, useState, useRef, useMemo, useCallback } from "react";
|
|
36
|
+
|
|
37
|
+
// src/conflicts/conflict-bus.ts
|
|
38
|
+
var ConflictBus = class {
|
|
39
|
+
conflictListeners = /* @__PURE__ */ new Set();
|
|
40
|
+
resolutionListeners = /* @__PURE__ */ new Set();
|
|
41
|
+
/**
|
|
42
|
+
* Subscribe to conflict detection events.
|
|
43
|
+
* @returns Unsubscribe function
|
|
44
|
+
*/
|
|
45
|
+
onConflict(listener) {
|
|
46
|
+
this.conflictListeners.add(listener);
|
|
47
|
+
return () => this.conflictListeners.delete(listener);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Subscribe to resolution events.
|
|
51
|
+
* @returns Unsubscribe function
|
|
52
|
+
*/
|
|
53
|
+
onResolution(listener) {
|
|
54
|
+
this.resolutionListeners.add(listener);
|
|
55
|
+
return () => this.resolutionListeners.delete(listener);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Emit a conflict detection event (called by connector).
|
|
59
|
+
*/
|
|
60
|
+
emitConflict(conflict) {
|
|
61
|
+
this.conflictListeners.forEach((listener) => listener(conflict));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Emit a resolution event (called by UI/ConflictContext).
|
|
65
|
+
*/
|
|
66
|
+
emitResolution(table, recordId, resolution) {
|
|
67
|
+
this.resolutionListeners.forEach((listener) => listener(table, recordId, resolution));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Clear all listeners (for cleanup).
|
|
71
|
+
*/
|
|
72
|
+
destroy() {
|
|
73
|
+
this.conflictListeners.clear();
|
|
74
|
+
this.resolutionListeners.clear();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/provider/PowerSyncProvider.tsx
|
|
79
|
+
import { jsx } from "react/jsx-runtime";
|
|
80
|
+
function PowerSyncProvider({
|
|
81
|
+
config,
|
|
82
|
+
children,
|
|
83
|
+
onReady,
|
|
84
|
+
onError,
|
|
85
|
+
onSyncStatusChange
|
|
86
|
+
}) {
|
|
87
|
+
const {
|
|
88
|
+
platform,
|
|
89
|
+
schema,
|
|
90
|
+
powerSyncUrl,
|
|
91
|
+
supabaseClient,
|
|
92
|
+
dbFilename = "powersync.db",
|
|
93
|
+
connector: connectorConfig,
|
|
94
|
+
attachments: attachmentConfig,
|
|
95
|
+
sync: syncConfig
|
|
96
|
+
} = config;
|
|
97
|
+
const logger = platform.logger;
|
|
98
|
+
const mergedSyncConfig = {
|
|
99
|
+
autoConnect: syncConfig?.autoConnect ?? true,
|
|
100
|
+
syncInterval: syncConfig?.syncInterval ?? 0,
|
|
101
|
+
enableHealthMonitoring: syncConfig?.enableHealthMonitoring ?? true,
|
|
102
|
+
enableMetrics: syncConfig?.enableMetrics ?? true
|
|
103
|
+
};
|
|
104
|
+
const [db, setDb] = useState(null);
|
|
105
|
+
const [connector, setConnector] = useState(null);
|
|
106
|
+
const [attachmentQueue, setAttachmentQueue] = useState(null);
|
|
107
|
+
const [isReady, setIsReady] = useState(false);
|
|
108
|
+
const [isInitializing, setIsInitializing] = useState(true);
|
|
109
|
+
const [error, setError] = useState(null);
|
|
110
|
+
const [session, setSession] = useState(null);
|
|
111
|
+
const conflictBusRef = useRef(null);
|
|
112
|
+
if (!conflictBusRef.current) {
|
|
113
|
+
conflictBusRef.current = new ConflictBus();
|
|
114
|
+
}
|
|
115
|
+
const conflictBus = conflictBusRef.current;
|
|
116
|
+
const [syncStatus, setSyncStatus] = useState(DEFAULT_SYNC_STATUS);
|
|
117
|
+
const [pendingMutations, setPendingMutations] = useState([]);
|
|
118
|
+
const [syncModeState, setSyncModeState] = useState({
|
|
119
|
+
loaded: false,
|
|
120
|
+
mode: "push-pull"
|
|
121
|
+
});
|
|
122
|
+
const [lastSyncedAt, setLastSyncedAt] = useState(null);
|
|
123
|
+
const [connectionError, setConnectionError] = useState(null);
|
|
124
|
+
const [failedTransactions, setFailedTransactions] = useState([]);
|
|
125
|
+
const [completedTransactions, setCompletedTransactions] = useState([]);
|
|
126
|
+
const [connectionHealth, setConnectionHealth] = useState(DEFAULT_CONNECTION_HEALTH);
|
|
127
|
+
const [syncMetrics, setSyncMetrics] = useState(DEFAULT_SYNC_METRICS);
|
|
128
|
+
const statusTrackerRef = useRef(null);
|
|
129
|
+
const metricsCollectorRef = useRef(null);
|
|
130
|
+
const healthMonitorRef = useRef(null);
|
|
131
|
+
const attachmentQueueRef = useRef(null);
|
|
132
|
+
const listenerUnsubscribeRef = useRef(null);
|
|
133
|
+
const wasSyncingRef = useRef(false);
|
|
134
|
+
const initializingRef = useRef(false);
|
|
135
|
+
const dbClosedRef = useRef(false);
|
|
136
|
+
const connectorRef = useRef(null);
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
const statusTracker = new SyncStatusTracker(platform.storage, logger, {
|
|
139
|
+
onStatusChange: (status) => {
|
|
140
|
+
setSyncStatus(status);
|
|
141
|
+
setLastSyncedAt(status.lastSyncedAt);
|
|
142
|
+
onSyncStatusChange?.(status);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
statusTrackerRef.current = statusTracker;
|
|
146
|
+
const metricsCollector = new MetricsCollector(platform.storage, logger, {
|
|
147
|
+
onMetricsChange: setSyncMetrics
|
|
148
|
+
});
|
|
149
|
+
metricsCollectorRef.current = metricsCollector;
|
|
150
|
+
const healthMonitor = new HealthMonitor(logger, {
|
|
151
|
+
onHealthChange: setConnectionHealth
|
|
152
|
+
});
|
|
153
|
+
healthMonitorRef.current = healthMonitor;
|
|
154
|
+
const initPromise = Promise.all([statusTracker.init(), metricsCollector.init()]);
|
|
155
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
logger.warn("[PowerSyncProvider] Sync mode state load timed out, using default (push-pull)");
|
|
158
|
+
resolve([void 0, void 0]);
|
|
159
|
+
}, 5e3);
|
|
160
|
+
});
|
|
161
|
+
Promise.race([initPromise, timeoutPromise]).then(() => {
|
|
162
|
+
logger.info("[PowerSyncProvider] Sync mode state loaded:", {
|
|
163
|
+
mode: statusTracker.getSyncMode()
|
|
164
|
+
});
|
|
165
|
+
setSyncModeState({
|
|
166
|
+
loaded: true,
|
|
167
|
+
mode: statusTracker.getSyncMode()
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
return () => {
|
|
171
|
+
statusTracker.dispose();
|
|
172
|
+
metricsCollector.dispose();
|
|
173
|
+
healthMonitor.dispose();
|
|
174
|
+
};
|
|
175
|
+
}, [platform, logger, onSyncStatusChange]);
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
logger.debug("[PowerSyncProvider] Setting up auth listener");
|
|
178
|
+
supabaseClient.auth.getSession().then(({
|
|
179
|
+
data: {
|
|
180
|
+
session: initialSession
|
|
181
|
+
}
|
|
182
|
+
}) => {
|
|
183
|
+
logger.debug("[PowerSyncProvider] Initial session:", !!initialSession);
|
|
184
|
+
setSession(initialSession);
|
|
185
|
+
});
|
|
186
|
+
const {
|
|
187
|
+
data: {
|
|
188
|
+
subscription
|
|
189
|
+
}
|
|
190
|
+
} = supabaseClient.auth.onAuthStateChange((_event, newSession) => {
|
|
191
|
+
logger.debug("[PowerSyncProvider] Auth state changed, hasSession:", !!newSession);
|
|
192
|
+
setSession(newSession);
|
|
193
|
+
});
|
|
194
|
+
return () => {
|
|
195
|
+
subscription.unsubscribe();
|
|
196
|
+
};
|
|
197
|
+
}, [supabaseClient, logger]);
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
if (initializingRef.current) {
|
|
200
|
+
logger.debug("[PowerSyncProvider] Already initializing, skipping...");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
initializingRef.current = true;
|
|
204
|
+
const controller = {
|
|
205
|
+
cancelled: false
|
|
206
|
+
};
|
|
207
|
+
const initDatabase = async () => {
|
|
208
|
+
try {
|
|
209
|
+
logger.info("[PowerSyncProvider] Initializing database...");
|
|
210
|
+
const database = await platform.createDatabase({
|
|
211
|
+
dbFilename,
|
|
212
|
+
schema
|
|
213
|
+
});
|
|
214
|
+
if (controller.cancelled) {
|
|
215
|
+
logger.debug("[PowerSyncProvider] Init cancelled, closing database...");
|
|
216
|
+
await database.close();
|
|
217
|
+
initializingRef.current = false;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
logger.info("[PowerSyncProvider] Database initialized");
|
|
221
|
+
setDb(database);
|
|
222
|
+
setIsReady(true);
|
|
223
|
+
setIsInitializing(false);
|
|
224
|
+
healthMonitorRef.current?.setDatabase(database);
|
|
225
|
+
if (mergedSyncConfig.enableHealthMonitoring) {
|
|
226
|
+
healthMonitorRef.current?.start();
|
|
227
|
+
}
|
|
228
|
+
onReady?.();
|
|
229
|
+
} catch (err) {
|
|
230
|
+
const initError = err instanceof Error ? err : new Error(String(err));
|
|
231
|
+
logger.error("[PowerSyncProvider] Initialization failed:", initError);
|
|
232
|
+
if (!controller.cancelled) {
|
|
233
|
+
setError(initError);
|
|
234
|
+
setIsInitializing(false);
|
|
235
|
+
onError?.(initError);
|
|
236
|
+
}
|
|
237
|
+
initializingRef.current = false;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
initDatabase();
|
|
241
|
+
return () => {
|
|
242
|
+
controller.cancelled = true;
|
|
243
|
+
initializingRef.current = false;
|
|
244
|
+
};
|
|
245
|
+
}, [platform, dbFilename, schema, logger, mergedSyncConfig.enableHealthMonitoring, onReady, onError]);
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (__DEV__) {
|
|
248
|
+
console.log("[PowerSyncProvider] Connect effect triggered:", {
|
|
249
|
+
hasDb: !!db,
|
|
250
|
+
hasSession: !!session,
|
|
251
|
+
autoConnect: mergedSyncConfig.autoConnect,
|
|
252
|
+
syncModeState
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
logger.info("[PowerSyncProvider] Connect effect - db:", !!db, "session:", !!session, "autoConnect:", mergedSyncConfig.autoConnect, "syncModeLoaded:", syncModeState.loaded, "syncMode:", syncModeState.mode);
|
|
256
|
+
if (!db) {
|
|
257
|
+
logger.debug("[PowerSyncProvider] Connect effect - waiting for db");
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (!session) {
|
|
261
|
+
logger.debug("[PowerSyncProvider] Connect effect - no session");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (!mergedSyncConfig.autoConnect) {
|
|
265
|
+
logger.debug("[PowerSyncProvider] Connect effect - autoConnect disabled");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (!syncModeState.loaded) {
|
|
269
|
+
logger.info("[PowerSyncProvider] Waiting for sync mode state to load...");
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (syncModeState.mode === "offline") {
|
|
273
|
+
logger.debug("[PowerSyncProvider] Skipping connect - offline mode");
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const connectPowerSync = async () => {
|
|
277
|
+
try {
|
|
278
|
+
setConnectionError(null);
|
|
279
|
+
connectorRef.current?.destroy();
|
|
280
|
+
const statusTracker_0 = statusTrackerRef.current;
|
|
281
|
+
const newConnector = new SupabaseConnector({
|
|
282
|
+
supabaseClient,
|
|
283
|
+
powerSyncUrl,
|
|
284
|
+
schemaRouter: connectorConfig?.schemaRouter,
|
|
285
|
+
crudHandler: connectorConfig?.crudHandler,
|
|
286
|
+
logger,
|
|
287
|
+
// Conflict detection - enabled by default
|
|
288
|
+
conflictDetection: {
|
|
289
|
+
enabled: true
|
|
290
|
+
},
|
|
291
|
+
conflictBus,
|
|
292
|
+
// Check if uploads should be performed based on sync mode
|
|
293
|
+
shouldUpload: () => statusTrackerRef.current?.shouldUpload() ?? true,
|
|
294
|
+
// Clear failures when transaction succeeds
|
|
295
|
+
onTransactionSuccess: (entries) => {
|
|
296
|
+
if (!statusTracker_0) return;
|
|
297
|
+
const entityIds = extractEntityIds(entries);
|
|
298
|
+
entityIds.forEach((id) => {
|
|
299
|
+
const failures = statusTracker_0.getFailuresForEntity(id);
|
|
300
|
+
failures.forEach((f) => statusTracker_0.clearFailure(f.id));
|
|
301
|
+
});
|
|
302
|
+
setFailedTransactions(statusTracker_0.getFailedTransactions());
|
|
303
|
+
},
|
|
304
|
+
// Record failures when transaction fails
|
|
305
|
+
onTransactionFailure: (entries_0, error_0, classified) => {
|
|
306
|
+
if (!statusTracker_0) return;
|
|
307
|
+
statusTracker_0.recordTransactionFailure(entries_0, createSyncError(classified, error_0.message), classified.isPermanent, extractEntityIds(entries_0), extractTableNames(entries_0));
|
|
308
|
+
setFailedTransactions(statusTracker_0.getFailedTransactions());
|
|
309
|
+
},
|
|
310
|
+
// Record completed transactions
|
|
311
|
+
onTransactionComplete: (entries_1) => {
|
|
312
|
+
if (!statusTracker_0) return;
|
|
313
|
+
statusTracker_0.recordTransactionComplete(entries_1);
|
|
314
|
+
setCompletedTransactions(statusTracker_0.getCompletedTransactions());
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
setConnector(newConnector);
|
|
318
|
+
connectorRef.current = newConnector;
|
|
319
|
+
if (db.connected) {
|
|
320
|
+
logger.debug("[PowerSyncProvider] Already connected, reconnecting...");
|
|
321
|
+
await db.disconnect();
|
|
322
|
+
}
|
|
323
|
+
logger.info("[PowerSyncProvider] Connecting to PowerSync...");
|
|
324
|
+
await db.connect(newConnector);
|
|
325
|
+
logger.info("[PowerSyncProvider] Connected successfully");
|
|
326
|
+
healthMonitorRef.current?.resetReconnectAttempts();
|
|
327
|
+
} catch (err_0) {
|
|
328
|
+
const connectError = err_0 instanceof Error ? err_0 : new Error(String(err_0));
|
|
329
|
+
logger.error("[PowerSyncProvider] Connection failed:", connectError);
|
|
330
|
+
setConnectionError(connectError);
|
|
331
|
+
healthMonitorRef.current?.recordReconnectAttempt();
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
connectPowerSync();
|
|
335
|
+
}, [db, session, powerSyncUrl, supabaseClient, connectorConfig, conflictBus, mergedSyncConfig.autoConnect, syncModeState, logger]);
|
|
336
|
+
useEffect(() => {
|
|
337
|
+
if (!db) return;
|
|
338
|
+
const initialStatus = db.currentStatus;
|
|
339
|
+
if (initialStatus) {
|
|
340
|
+
statusTrackerRef.current?.handleStatusChange(initialStatus);
|
|
341
|
+
}
|
|
342
|
+
const unsubscribe = db.registerListener({
|
|
343
|
+
statusChanged: (status_0) => {
|
|
344
|
+
statusTrackerRef.current?.handleStatusChange(status_0);
|
|
345
|
+
const dataFlow = status_0.dataFlowStatus;
|
|
346
|
+
const isDownloading = dataFlow?.downloading ?? false;
|
|
347
|
+
const progress = status_0.downloadProgress;
|
|
348
|
+
if (isDownloading && !wasSyncingRef.current) {
|
|
349
|
+
metricsCollectorRef.current?.markSyncStart();
|
|
350
|
+
}
|
|
351
|
+
if (!isDownloading && wasSyncingRef.current) {
|
|
352
|
+
const duration = metricsCollectorRef.current?.markSyncEnd();
|
|
353
|
+
if (duration !== null && duration !== void 0) {
|
|
354
|
+
metricsCollectorRef.current?.recordSync({
|
|
355
|
+
durationMs: duration,
|
|
356
|
+
success: true,
|
|
357
|
+
operationsDownloaded: progress?.totalOperations ?? 0
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
wasSyncingRef.current = isDownloading;
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
listenerUnsubscribeRef.current = unsubscribe;
|
|
365
|
+
return () => {
|
|
366
|
+
unsubscribe();
|
|
367
|
+
listenerUnsubscribeRef.current = null;
|
|
368
|
+
};
|
|
369
|
+
}, [db]);
|
|
370
|
+
const prevPendingCountRef = useRef(0);
|
|
371
|
+
const parseCrudRow = useCallback((row) => {
|
|
372
|
+
try {
|
|
373
|
+
const parsed = JSON.parse(row.data);
|
|
374
|
+
const opMap = {
|
|
375
|
+
"PUT": "PUT",
|
|
376
|
+
"PATCH": "PATCH",
|
|
377
|
+
"DELETE": "DELETE"
|
|
378
|
+
};
|
|
379
|
+
const op = opMap[parsed.op] ?? "PUT";
|
|
380
|
+
const entityId = parsed.data?.id ?? parsed.id ?? row.id;
|
|
381
|
+
return {
|
|
382
|
+
id: entityId,
|
|
383
|
+
clientId: parseInt(row.id, 10) || 0,
|
|
384
|
+
op,
|
|
385
|
+
table: parsed.type ?? "unknown",
|
|
386
|
+
opData: parsed.data ?? void 0,
|
|
387
|
+
// Ensure null becomes undefined
|
|
388
|
+
transactionId: row.tx_id ?? void 0
|
|
389
|
+
};
|
|
390
|
+
} catch (e) {
|
|
391
|
+
logger.warn("[PowerSyncProvider] Failed to parse CRUD entry:", e, row);
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
}, [logger]);
|
|
395
|
+
useEffect(() => {
|
|
396
|
+
if (!db) return;
|
|
397
|
+
const abortController = new AbortController();
|
|
398
|
+
db.watch("SELECT COUNT(*) as count FROM ps_crud", [], {
|
|
399
|
+
onResult: async (results) => {
|
|
400
|
+
if (dbClosedRef.current) {
|
|
401
|
+
logger.debug("[PowerSyncProvider] Skipping watch callback - database is closed");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const count = results.rows?._array?.[0]?.count ?? 0;
|
|
405
|
+
const prevCount = prevPendingCountRef.current;
|
|
406
|
+
prevPendingCountRef.current = count;
|
|
407
|
+
let mutations = [];
|
|
408
|
+
if (count > 0) {
|
|
409
|
+
try {
|
|
410
|
+
if (dbClosedRef.current) {
|
|
411
|
+
logger.debug("[PowerSyncProvider] Skipping getAll - database is closed");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const rows = await db.getAll("SELECT id, tx_id, data FROM ps_crud ORDER BY id ASC");
|
|
415
|
+
mutations = rows.map(parseCrudRow).filter((entry) => entry !== null);
|
|
416
|
+
} catch (e_0) {
|
|
417
|
+
const errorMessage = e_0 instanceof Error ? e_0.message : String(e_0);
|
|
418
|
+
if (errorMessage.includes("not open") || errorMessage.includes("closed")) {
|
|
419
|
+
logger.debug("[PowerSyncProvider] Database closed during CRUD fetch, ignoring");
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
logger.warn("[PowerSyncProvider] Failed to fetch CRUD entries:", e_0);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (dbClosedRef.current) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
statusTrackerRef.current?.updatePendingMutations(mutations);
|
|
429
|
+
setPendingMutations(mutations);
|
|
430
|
+
if (prevCount > 0 && count === 0) {
|
|
431
|
+
logger.debug("[PowerSyncProvider] All pending uploads complete, clearing force flag");
|
|
432
|
+
statusTrackerRef.current?.clearForceNextUpload();
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
onError: (error_1) => {
|
|
436
|
+
const errorMessage_0 = error_1.message ?? "";
|
|
437
|
+
if (errorMessage_0.includes("not open") || errorMessage_0.includes("closed") || dbClosedRef.current) {
|
|
438
|
+
logger.debug("[PowerSyncProvider] Watch error during database close, ignoring");
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
logger.warn("[PowerSyncProvider] Error watching ps_crud:", error_1);
|
|
442
|
+
}
|
|
443
|
+
}, {
|
|
444
|
+
signal: abortController.signal,
|
|
445
|
+
throttleMs: 100
|
|
446
|
+
// Debounce to avoid excessive updates during bulk operations
|
|
447
|
+
});
|
|
448
|
+
return () => {
|
|
449
|
+
abortController.abort();
|
|
450
|
+
};
|
|
451
|
+
}, [db, logger, parseCrudRow]);
|
|
452
|
+
useEffect(() => {
|
|
453
|
+
if (!db || !attachmentConfig || attachmentQueueRef.current) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const initAttachmentQueue = async () => {
|
|
457
|
+
try {
|
|
458
|
+
logger.info("[PowerSyncProvider] Initializing attachment queue...");
|
|
459
|
+
const queue = new AttachmentQueue({
|
|
460
|
+
powersync: db,
|
|
461
|
+
platform,
|
|
462
|
+
config: attachmentConfig
|
|
463
|
+
});
|
|
464
|
+
await queue.init();
|
|
465
|
+
attachmentQueueRef.current = queue;
|
|
466
|
+
setAttachmentQueue(queue);
|
|
467
|
+
logger.info("[PowerSyncProvider] Attachment queue initialized");
|
|
468
|
+
} catch (err_1) {
|
|
469
|
+
logger.error("[PowerSyncProvider] Attachment queue initialization failed:", err_1);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
initAttachmentQueue();
|
|
473
|
+
return () => {
|
|
474
|
+
attachmentQueueRef.current?.dispose();
|
|
475
|
+
attachmentQueueRef.current = null;
|
|
476
|
+
};
|
|
477
|
+
}, [db, attachmentConfig, platform, logger]);
|
|
478
|
+
useEffect(() => {
|
|
479
|
+
if (db) {
|
|
480
|
+
dbClosedRef.current = false;
|
|
481
|
+
}
|
|
482
|
+
return () => {
|
|
483
|
+
logger.info("[PowerSyncProvider] Cleaning up...");
|
|
484
|
+
dbClosedRef.current = true;
|
|
485
|
+
connectorRef.current?.destroy();
|
|
486
|
+
connectorRef.current = null;
|
|
487
|
+
listenerUnsubscribeRef.current?.();
|
|
488
|
+
attachmentQueueRef.current?.dispose();
|
|
489
|
+
healthMonitorRef.current?.stop();
|
|
490
|
+
conflictBusRef.current?.destroy();
|
|
491
|
+
if (db) {
|
|
492
|
+
(async () => {
|
|
493
|
+
try {
|
|
494
|
+
await db.disconnect();
|
|
495
|
+
await db.close();
|
|
496
|
+
} catch (err_2) {
|
|
497
|
+
const errorMessage_1 = err_2 instanceof Error ? err_2.message : String(err_2);
|
|
498
|
+
if (!errorMessage_1.includes("not open") && !errorMessage_1.includes("closed")) {
|
|
499
|
+
logger.warn("[PowerSyncProvider] Error during cleanup:", err_2);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
})();
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}, [db, logger]);
|
|
506
|
+
const clearFailure = useCallback((failureId) => {
|
|
507
|
+
const tracker = statusTrackerRef.current;
|
|
508
|
+
if (!tracker) {
|
|
509
|
+
logger.warn("[PowerSyncProvider] Cannot clear failure - tracker not initialized");
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
tracker.clearFailure(failureId);
|
|
513
|
+
setFailedTransactions(tracker.getFailedTransactions());
|
|
514
|
+
}, [logger]);
|
|
515
|
+
const clearAllFailures = useCallback(() => {
|
|
516
|
+
const tracker_0 = statusTrackerRef.current;
|
|
517
|
+
if (!tracker_0) {
|
|
518
|
+
logger.warn("[PowerSyncProvider] Cannot clear failures - tracker not initialized");
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
tracker_0.clearAllFailures();
|
|
522
|
+
setFailedTransactions(tracker_0.getFailedTransactions());
|
|
523
|
+
}, [logger]);
|
|
524
|
+
const clearCompletedHistory = useCallback(() => {
|
|
525
|
+
const tracker_1 = statusTrackerRef.current;
|
|
526
|
+
if (!tracker_1) {
|
|
527
|
+
logger.warn("[PowerSyncProvider] Cannot clear completed history - tracker not initialized");
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
tracker_1.clearCompletedHistory();
|
|
531
|
+
setCompletedTransactions(tracker_1.getCompletedTransactions());
|
|
532
|
+
}, [logger]);
|
|
533
|
+
const setSyncMode = useCallback(async (mode) => {
|
|
534
|
+
const tracker_2 = statusTrackerRef.current;
|
|
535
|
+
if (!tracker_2) {
|
|
536
|
+
logger.warn("[PowerSyncProvider] Cannot set sync mode - tracker not initialized");
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
await tracker_2.setSyncMode(mode);
|
|
540
|
+
setSyncModeState({
|
|
541
|
+
loaded: true,
|
|
542
|
+
mode
|
|
543
|
+
});
|
|
544
|
+
}, [logger]);
|
|
545
|
+
const setForceNextUpload = useCallback((force) => {
|
|
546
|
+
const tracker_3 = statusTrackerRef.current;
|
|
547
|
+
if (!tracker_3) {
|
|
548
|
+
logger.warn("[PowerSyncProvider] Cannot set force upload - tracker not initialized");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
tracker_3.setForceNextUpload(force);
|
|
552
|
+
}, [logger]);
|
|
553
|
+
const discardPendingMutation = useCallback(async (clientId) => {
|
|
554
|
+
if (!db || !connector) {
|
|
555
|
+
logger.warn("[PowerSync] Cannot discard - not initialized");
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (syncStatus.uploading) {
|
|
559
|
+
throw new Error("Cannot discard while upload is in progress");
|
|
560
|
+
}
|
|
561
|
+
logger.info("[PowerSync] Discarding pending mutation:", clientId);
|
|
562
|
+
await db.disconnect();
|
|
563
|
+
try {
|
|
564
|
+
await db.execute("DELETE FROM ps_crud WHERE id = ?", [clientId]);
|
|
565
|
+
logger.info("[PowerSync] Mutation discarded successfully");
|
|
566
|
+
} finally {
|
|
567
|
+
await db.connect(connector);
|
|
568
|
+
}
|
|
569
|
+
}, [db, connector, syncStatus.uploading, logger]);
|
|
570
|
+
const discardAllPendingMutations = useCallback(async () => {
|
|
571
|
+
if (!db || !connector) {
|
|
572
|
+
logger.warn("[PowerSync] Cannot discard all - not initialized");
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (syncStatus.uploading) {
|
|
576
|
+
throw new Error("Cannot discard while upload is in progress");
|
|
577
|
+
}
|
|
578
|
+
logger.info("[PowerSync] Discarding all pending mutations");
|
|
579
|
+
await db.disconnect();
|
|
580
|
+
try {
|
|
581
|
+
await db.execute("DELETE FROM ps_crud");
|
|
582
|
+
logger.info("[PowerSync] All mutations discarded successfully");
|
|
583
|
+
} finally {
|
|
584
|
+
await db.connect(connector);
|
|
585
|
+
}
|
|
586
|
+
}, [db, connector, syncStatus.uploading, logger]);
|
|
587
|
+
const powerSyncContextValue = useMemo(() => ({
|
|
588
|
+
db,
|
|
589
|
+
connector,
|
|
590
|
+
attachmentQueue,
|
|
591
|
+
isReady,
|
|
592
|
+
isInitializing,
|
|
593
|
+
error,
|
|
594
|
+
schema,
|
|
595
|
+
platform,
|
|
596
|
+
conflictBus
|
|
597
|
+
}), [db, connector, attachmentQueue, isReady, isInitializing, error, schema, platform, conflictBus]);
|
|
598
|
+
const syncStatusContextValue = useMemo(() => ({
|
|
599
|
+
status: syncStatus,
|
|
600
|
+
pendingMutations,
|
|
601
|
+
pendingCount: pendingMutations.length,
|
|
602
|
+
// Expose uploading/downloading directly from syncStatus for reliable activity detection
|
|
603
|
+
isUploading: syncStatus.uploading,
|
|
604
|
+
isDownloading: syncStatus.downloading,
|
|
605
|
+
isPaused: syncModeState.mode === "offline",
|
|
606
|
+
syncMode: syncModeState.mode,
|
|
607
|
+
lastSyncedAt,
|
|
608
|
+
// Connection error for consumers to display
|
|
609
|
+
connectionError,
|
|
610
|
+
// Failed transaction fields
|
|
611
|
+
failedTransactions,
|
|
612
|
+
hasUploadErrors: failedTransactions.length > 0,
|
|
613
|
+
permanentErrorCount: failedTransactions.filter((f_0) => f_0.isPermanent).length,
|
|
614
|
+
// Clear failure functions
|
|
615
|
+
clearFailure,
|
|
616
|
+
clearAllFailures,
|
|
617
|
+
// Completed transaction fields
|
|
618
|
+
completedTransactions,
|
|
619
|
+
clearCompletedHistory,
|
|
620
|
+
// Sync mode control functions
|
|
621
|
+
setSyncMode,
|
|
622
|
+
setForceNextUpload,
|
|
623
|
+
// Discard mutation functions
|
|
624
|
+
discardPendingMutation,
|
|
625
|
+
discardAllPendingMutations
|
|
626
|
+
}), [syncStatus, pendingMutations, syncModeState.mode, lastSyncedAt, connectionError, failedTransactions, clearFailure, clearAllFailures, completedTransactions, clearCompletedHistory, setSyncMode, setForceNextUpload, discardPendingMutation, discardAllPendingMutations]);
|
|
627
|
+
const connectionHealthContextValue = useMemo(() => ({
|
|
628
|
+
health: connectionHealth
|
|
629
|
+
}), [connectionHealth]);
|
|
630
|
+
const syncMetricsContextValue = useMemo(() => ({
|
|
631
|
+
metrics: syncMetrics
|
|
632
|
+
}), [syncMetrics]);
|
|
633
|
+
return /* @__PURE__ */ jsx(PowerSyncContext.Provider, { value: powerSyncContextValue, children: /* @__PURE__ */ jsx(SyncStatusContext.Provider, { value: syncStatusContextValue, children: /* @__PURE__ */ jsx(ConnectionHealthContext.Provider, { value: connectionHealthContextValue, children: /* @__PURE__ */ jsx(SyncMetricsContext.Provider, { value: syncMetricsContextValue, children: /* @__PURE__ */ jsx(AttachmentQueueContext.Provider, { value: attachmentQueue, children }) }) }) }) });
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/provider/hooks.ts
|
|
637
|
+
import { c as _c } from "react/compiler-runtime";
|
|
638
|
+
import { useContext, useRef as useRef2, useState as useState2, useEffect as useEffect2 } from "react";
|
|
639
|
+
function usePowerSync() {
|
|
640
|
+
const context = useContext(PowerSyncContext);
|
|
641
|
+
if (!context) {
|
|
642
|
+
throw new Error("usePowerSync must be used within a PowerSyncProvider");
|
|
643
|
+
}
|
|
644
|
+
return context;
|
|
645
|
+
}
|
|
646
|
+
function useSyncStatus() {
|
|
647
|
+
const context = useContext(SyncStatusContext);
|
|
648
|
+
if (!context) {
|
|
649
|
+
throw new Error("useSyncStatus must be used within a PowerSyncProvider");
|
|
650
|
+
}
|
|
651
|
+
return context;
|
|
652
|
+
}
|
|
653
|
+
function useSyncControl() {
|
|
654
|
+
const $ = _c(35);
|
|
655
|
+
const {
|
|
656
|
+
db,
|
|
657
|
+
connector,
|
|
658
|
+
platform
|
|
659
|
+
} = usePowerSync();
|
|
660
|
+
const {
|
|
661
|
+
setSyncMode: setContextSyncMode,
|
|
662
|
+
setForceNextUpload
|
|
663
|
+
} = useSyncStatus();
|
|
664
|
+
const scopeRef = useRef2(null);
|
|
665
|
+
let t0;
|
|
666
|
+
if ($[0] !== connector || $[1] !== db || $[2] !== platform || $[3] !== setContextSyncMode) {
|
|
667
|
+
t0 = async (mode) => {
|
|
668
|
+
await setContextSyncMode(mode);
|
|
669
|
+
if (mode === "offline") {
|
|
670
|
+
if (db?.connected) {
|
|
671
|
+
platform.logger.info("[useSyncControl] Mode changed to offline - disconnecting");
|
|
672
|
+
await db.disconnect();
|
|
673
|
+
}
|
|
674
|
+
} else {
|
|
675
|
+
if (db && connector && !db.connected) {
|
|
676
|
+
platform.logger.info("[useSyncControl] Mode changed to", mode, "- reconnecting");
|
|
677
|
+
await db.connect(connector);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
$[0] = connector;
|
|
682
|
+
$[1] = db;
|
|
683
|
+
$[2] = platform;
|
|
684
|
+
$[3] = setContextSyncMode;
|
|
685
|
+
$[4] = t0;
|
|
686
|
+
} else {
|
|
687
|
+
t0 = $[4];
|
|
688
|
+
}
|
|
689
|
+
const setSyncMode = t0;
|
|
690
|
+
let t1;
|
|
691
|
+
if ($[5] !== connector || $[6] !== db || $[7] !== platform || $[8] !== setForceNextUpload) {
|
|
692
|
+
t1 = async () => {
|
|
693
|
+
if (!db || !connector) {
|
|
694
|
+
platform.logger.warn("[useSyncControl] Cannot sync - database not initialized");
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
setForceNextUpload(true);
|
|
698
|
+
platform.logger.info("[useSyncControl] Sync Now triggered - forcing full sync");
|
|
699
|
+
if (db.connected) {
|
|
700
|
+
await db.disconnect();
|
|
701
|
+
}
|
|
702
|
+
await db.connect(connector);
|
|
703
|
+
platform.logger.info("[useSyncControl] Connected, sync should start automatically");
|
|
704
|
+
};
|
|
705
|
+
$[5] = connector;
|
|
706
|
+
$[6] = db;
|
|
707
|
+
$[7] = platform;
|
|
708
|
+
$[8] = setForceNextUpload;
|
|
709
|
+
$[9] = t1;
|
|
710
|
+
} else {
|
|
711
|
+
t1 = $[9];
|
|
712
|
+
}
|
|
713
|
+
const syncNow = t1;
|
|
714
|
+
let t2;
|
|
715
|
+
if ($[10] !== connector || $[11] !== db || $[12] !== platform || $[13] !== setContextSyncMode) {
|
|
716
|
+
t2 = async () => {
|
|
717
|
+
if (!db || !connector) {
|
|
718
|
+
platform.logger.warn("[useSyncControl] Cannot trigger sync - not initialized");
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
await setContextSyncMode("push-pull");
|
|
722
|
+
if (db.connected) {
|
|
723
|
+
platform.logger.info("[useSyncControl] Disconnecting to force fresh sync...");
|
|
724
|
+
await db.disconnect();
|
|
725
|
+
}
|
|
726
|
+
platform.logger.info("[useSyncControl] Connecting...");
|
|
727
|
+
await db.connect(connector);
|
|
728
|
+
platform.logger.info("[useSyncControl] Connected, sync should start automatically");
|
|
729
|
+
};
|
|
730
|
+
$[10] = connector;
|
|
731
|
+
$[11] = db;
|
|
732
|
+
$[12] = platform;
|
|
733
|
+
$[13] = setContextSyncMode;
|
|
734
|
+
$[14] = t2;
|
|
735
|
+
} else {
|
|
736
|
+
t2 = $[14];
|
|
737
|
+
}
|
|
738
|
+
const triggerSync = t2;
|
|
739
|
+
let t3;
|
|
740
|
+
if ($[15] !== platform || $[16] !== setSyncMode) {
|
|
741
|
+
t3 = async () => {
|
|
742
|
+
await setSyncMode("offline");
|
|
743
|
+
platform.logger.info("[useSyncControl] Sync paused");
|
|
744
|
+
};
|
|
745
|
+
$[15] = platform;
|
|
746
|
+
$[16] = setSyncMode;
|
|
747
|
+
$[17] = t3;
|
|
748
|
+
} else {
|
|
749
|
+
t3 = $[17];
|
|
750
|
+
}
|
|
751
|
+
const pause = t3;
|
|
752
|
+
let t4;
|
|
753
|
+
if ($[18] !== platform || $[19] !== setSyncMode) {
|
|
754
|
+
t4 = async () => {
|
|
755
|
+
await setSyncMode("push-pull");
|
|
756
|
+
platform.logger.info("[useSyncControl] Sync resumed");
|
|
757
|
+
};
|
|
758
|
+
$[18] = platform;
|
|
759
|
+
$[19] = setSyncMode;
|
|
760
|
+
$[20] = t4;
|
|
761
|
+
} else {
|
|
762
|
+
t4 = $[20];
|
|
763
|
+
}
|
|
764
|
+
const resume = t4;
|
|
765
|
+
let t5;
|
|
766
|
+
if ($[21] !== db || $[22] !== platform) {
|
|
767
|
+
t5 = async () => {
|
|
768
|
+
if (!db) {
|
|
769
|
+
platform.logger.warn("[useSyncControl] Cannot disconnect - not initialized");
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
platform.logger.info("[useSyncControl] Disconnecting...");
|
|
773
|
+
await db.disconnect();
|
|
774
|
+
platform.logger.info("[useSyncControl] Disconnected");
|
|
775
|
+
};
|
|
776
|
+
$[21] = db;
|
|
777
|
+
$[22] = platform;
|
|
778
|
+
$[23] = t5;
|
|
779
|
+
} else {
|
|
780
|
+
t5 = $[23];
|
|
781
|
+
}
|
|
782
|
+
const disconnect = t5;
|
|
783
|
+
let t6;
|
|
784
|
+
if ($[24] !== connector || $[25] !== platform) {
|
|
785
|
+
t6 = (scope) => {
|
|
786
|
+
scopeRef.current = scope;
|
|
787
|
+
if (connector && scope) {
|
|
788
|
+
connector.setActiveProjectIds(scope.ids);
|
|
789
|
+
platform.logger.info("[useSyncControl] Scope set:", scope);
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
$[24] = connector;
|
|
793
|
+
$[25] = platform;
|
|
794
|
+
$[26] = t6;
|
|
795
|
+
} else {
|
|
796
|
+
t6 = $[26];
|
|
797
|
+
}
|
|
798
|
+
const setScope = t6;
|
|
799
|
+
let t7;
|
|
800
|
+
if ($[27] !== disconnect || $[28] !== pause || $[29] !== resume || $[30] !== setScope || $[31] !== setSyncMode || $[32] !== syncNow || $[33] !== triggerSync) {
|
|
801
|
+
t7 = {
|
|
802
|
+
triggerSync,
|
|
803
|
+
syncNow,
|
|
804
|
+
pause,
|
|
805
|
+
resume,
|
|
806
|
+
disconnect,
|
|
807
|
+
setScope,
|
|
808
|
+
setSyncMode
|
|
809
|
+
};
|
|
810
|
+
$[27] = disconnect;
|
|
811
|
+
$[28] = pause;
|
|
812
|
+
$[29] = resume;
|
|
813
|
+
$[30] = setScope;
|
|
814
|
+
$[31] = setSyncMode;
|
|
815
|
+
$[32] = syncNow;
|
|
816
|
+
$[33] = triggerSync;
|
|
817
|
+
$[34] = t7;
|
|
818
|
+
} else {
|
|
819
|
+
t7 = $[34];
|
|
820
|
+
}
|
|
821
|
+
return t7;
|
|
822
|
+
}
|
|
823
|
+
function useSyncMode() {
|
|
824
|
+
const $ = _c(5);
|
|
825
|
+
const {
|
|
826
|
+
syncMode
|
|
827
|
+
} = useSyncStatus();
|
|
828
|
+
const {
|
|
829
|
+
setSyncMode
|
|
830
|
+
} = useSyncControl();
|
|
831
|
+
const t0 = syncMode === "push-pull";
|
|
832
|
+
const t1 = syncMode !== "offline";
|
|
833
|
+
let t2;
|
|
834
|
+
if ($[0] !== setSyncMode || $[1] !== syncMode || $[2] !== t0 || $[3] !== t1) {
|
|
835
|
+
t2 = {
|
|
836
|
+
mode: syncMode,
|
|
837
|
+
setMode: setSyncMode,
|
|
838
|
+
canUpload: t0,
|
|
839
|
+
canDownload: t1
|
|
840
|
+
};
|
|
841
|
+
$[0] = setSyncMode;
|
|
842
|
+
$[1] = syncMode;
|
|
843
|
+
$[2] = t0;
|
|
844
|
+
$[3] = t1;
|
|
845
|
+
$[4] = t2;
|
|
846
|
+
} else {
|
|
847
|
+
t2 = $[4];
|
|
848
|
+
}
|
|
849
|
+
return t2;
|
|
850
|
+
}
|
|
851
|
+
function useConnectionHealth() {
|
|
852
|
+
const context = useContext(ConnectionHealthContext);
|
|
853
|
+
if (!context) {
|
|
854
|
+
throw new Error("useConnectionHealth must be used within a PowerSyncProvider");
|
|
855
|
+
}
|
|
856
|
+
return context.health;
|
|
857
|
+
}
|
|
858
|
+
function useSyncMetrics() {
|
|
859
|
+
const context = useContext(SyncMetricsContext);
|
|
860
|
+
if (!context) {
|
|
861
|
+
throw new Error("useSyncMetrics must be used within a PowerSyncProvider");
|
|
862
|
+
}
|
|
863
|
+
return context.metrics;
|
|
864
|
+
}
|
|
865
|
+
function useAttachmentQueue() {
|
|
866
|
+
return useContext(AttachmentQueueContext);
|
|
867
|
+
}
|
|
868
|
+
function useDatabase() {
|
|
869
|
+
const {
|
|
870
|
+
db,
|
|
871
|
+
isReady,
|
|
872
|
+
error
|
|
873
|
+
} = usePowerSync();
|
|
874
|
+
if (error) {
|
|
875
|
+
throw error;
|
|
876
|
+
}
|
|
877
|
+
if (!isReady || !db) {
|
|
878
|
+
throw new Error("PowerSync database is not ready");
|
|
879
|
+
}
|
|
880
|
+
return db;
|
|
881
|
+
}
|
|
882
|
+
function usePlatform() {
|
|
883
|
+
const {
|
|
884
|
+
platform
|
|
885
|
+
} = usePowerSync();
|
|
886
|
+
return platform;
|
|
887
|
+
}
|
|
888
|
+
function useOnlineStatus() {
|
|
889
|
+
const $ = _c(4);
|
|
890
|
+
const {
|
|
891
|
+
platform
|
|
892
|
+
} = usePowerSync();
|
|
893
|
+
const [isOnline, setIsOnline] = useState2(true);
|
|
894
|
+
let t0;
|
|
895
|
+
if ($[0] !== platform.network) {
|
|
896
|
+
t0 = () => {
|
|
897
|
+
platform.network.isConnected().then(setIsOnline);
|
|
898
|
+
const unsubscribe = platform.network.addConnectionListener(setIsOnline);
|
|
899
|
+
return unsubscribe;
|
|
900
|
+
};
|
|
901
|
+
$[0] = platform.network;
|
|
902
|
+
$[1] = t0;
|
|
903
|
+
} else {
|
|
904
|
+
t0 = $[1];
|
|
905
|
+
}
|
|
906
|
+
let t1;
|
|
907
|
+
if ($[2] !== platform) {
|
|
908
|
+
t1 = [platform];
|
|
909
|
+
$[2] = platform;
|
|
910
|
+
$[3] = t1;
|
|
911
|
+
} else {
|
|
912
|
+
t1 = $[3];
|
|
913
|
+
}
|
|
914
|
+
useEffect2(t0, t1);
|
|
915
|
+
return isOnline;
|
|
916
|
+
}
|
|
917
|
+
function usePendingMutations() {
|
|
918
|
+
const $ = _c(3);
|
|
919
|
+
const {
|
|
920
|
+
pendingMutations,
|
|
921
|
+
pendingCount
|
|
922
|
+
} = useSyncStatus();
|
|
923
|
+
let t0;
|
|
924
|
+
if ($[0] !== pendingCount || $[1] !== pendingMutations) {
|
|
925
|
+
t0 = {
|
|
926
|
+
mutations: pendingMutations,
|
|
927
|
+
count: pendingCount
|
|
928
|
+
};
|
|
929
|
+
$[0] = pendingCount;
|
|
930
|
+
$[1] = pendingMutations;
|
|
931
|
+
$[2] = t0;
|
|
932
|
+
} else {
|
|
933
|
+
t0 = $[2];
|
|
934
|
+
}
|
|
935
|
+
return t0;
|
|
936
|
+
}
|
|
937
|
+
function useIsSyncing() {
|
|
938
|
+
const {
|
|
939
|
+
status
|
|
940
|
+
} = useSyncStatus();
|
|
941
|
+
return status.uploading || status.downloading;
|
|
942
|
+
}
|
|
943
|
+
function useDownloadProgress() {
|
|
944
|
+
const {
|
|
945
|
+
status
|
|
946
|
+
} = useSyncStatus();
|
|
947
|
+
return status.downloadProgress;
|
|
948
|
+
}
|
|
949
|
+
var recentlySyncedEntities = /* @__PURE__ */ new Map();
|
|
950
|
+
var SYNCED_DISPLAY_DURATION_MS = 3e3;
|
|
951
|
+
function useEntitySyncStatus(entityId) {
|
|
952
|
+
const $ = _c(27);
|
|
953
|
+
const {
|
|
954
|
+
pendingMutations,
|
|
955
|
+
clearFailure,
|
|
956
|
+
failedTransactions
|
|
957
|
+
} = useSyncStatus();
|
|
958
|
+
const [, forceUpdate] = useState2(0);
|
|
959
|
+
let t0;
|
|
960
|
+
bb0: {
|
|
961
|
+
if (!entityId) {
|
|
962
|
+
let t13;
|
|
963
|
+
if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
964
|
+
t13 = [];
|
|
965
|
+
$[0] = t13;
|
|
966
|
+
} else {
|
|
967
|
+
t13 = $[0];
|
|
968
|
+
}
|
|
969
|
+
t0 = t13;
|
|
970
|
+
break bb0;
|
|
971
|
+
}
|
|
972
|
+
let t12;
|
|
973
|
+
if ($[1] !== entityId || $[2] !== pendingMutations) {
|
|
974
|
+
let t22;
|
|
975
|
+
if ($[4] !== entityId) {
|
|
976
|
+
t22 = (entry) => entry.id === entityId || String(entry.opData?.id) === entityId;
|
|
977
|
+
$[4] = entityId;
|
|
978
|
+
$[5] = t22;
|
|
979
|
+
} else {
|
|
980
|
+
t22 = $[5];
|
|
981
|
+
}
|
|
982
|
+
t12 = pendingMutations.filter(t22);
|
|
983
|
+
$[1] = entityId;
|
|
984
|
+
$[2] = pendingMutations;
|
|
985
|
+
$[3] = t12;
|
|
986
|
+
} else {
|
|
987
|
+
t12 = $[3];
|
|
988
|
+
}
|
|
989
|
+
t0 = t12;
|
|
990
|
+
}
|
|
991
|
+
const entityPendingMutations = t0;
|
|
992
|
+
let t1;
|
|
993
|
+
bb1: {
|
|
994
|
+
if (!entityId) {
|
|
995
|
+
t1 = null;
|
|
996
|
+
break bb1;
|
|
997
|
+
}
|
|
998
|
+
let t22;
|
|
999
|
+
if ($[6] !== entityId || $[7] !== failedTransactions) {
|
|
1000
|
+
t22 = failedTransactions.find((ft) => ft.affectedEntityIds.includes(entityId)) ?? null;
|
|
1001
|
+
$[6] = entityId;
|
|
1002
|
+
$[7] = failedTransactions;
|
|
1003
|
+
$[8] = t22;
|
|
1004
|
+
} else {
|
|
1005
|
+
t22 = $[8];
|
|
1006
|
+
}
|
|
1007
|
+
t1 = t22;
|
|
1008
|
+
}
|
|
1009
|
+
const failedTransaction = t1;
|
|
1010
|
+
const wasSyncingRef = useRef2(false);
|
|
1011
|
+
const isCurrentlySyncing = entityPendingMutations.length > 0;
|
|
1012
|
+
let t2;
|
|
1013
|
+
if ($[9] !== entityId || $[10] !== failedTransaction || $[11] !== forceUpdate || $[12] !== isCurrentlySyncing) {
|
|
1014
|
+
t2 = () => {
|
|
1015
|
+
if (!entityId) {
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
if (wasSyncingRef.current && !isCurrentlySyncing && !failedTransaction) {
|
|
1019
|
+
recentlySyncedEntities.set(entityId, Date.now());
|
|
1020
|
+
const timer = setTimeout(() => {
|
|
1021
|
+
const syncedAt = recentlySyncedEntities.get(entityId);
|
|
1022
|
+
if (syncedAt && Date.now() - syncedAt >= SYNCED_DISPLAY_DURATION_MS) {
|
|
1023
|
+
recentlySyncedEntities.delete(entityId);
|
|
1024
|
+
forceUpdate(_temp);
|
|
1025
|
+
}
|
|
1026
|
+
}, SYNCED_DISPLAY_DURATION_MS);
|
|
1027
|
+
return () => clearTimeout(timer);
|
|
1028
|
+
}
|
|
1029
|
+
wasSyncingRef.current = isCurrentlySyncing;
|
|
1030
|
+
};
|
|
1031
|
+
$[9] = entityId;
|
|
1032
|
+
$[10] = failedTransaction;
|
|
1033
|
+
$[11] = forceUpdate;
|
|
1034
|
+
$[12] = isCurrentlySyncing;
|
|
1035
|
+
$[13] = t2;
|
|
1036
|
+
} else {
|
|
1037
|
+
t2 = $[13];
|
|
1038
|
+
}
|
|
1039
|
+
let t3;
|
|
1040
|
+
if ($[14] !== entityId || $[15] !== failedTransaction || $[16] !== isCurrentlySyncing) {
|
|
1041
|
+
t3 = [entityId, isCurrentlySyncing, failedTransaction];
|
|
1042
|
+
$[14] = entityId;
|
|
1043
|
+
$[15] = failedTransaction;
|
|
1044
|
+
$[16] = isCurrentlySyncing;
|
|
1045
|
+
$[17] = t3;
|
|
1046
|
+
} else {
|
|
1047
|
+
t3 = $[17];
|
|
1048
|
+
}
|
|
1049
|
+
useEffect2(t2, t3);
|
|
1050
|
+
let t4;
|
|
1051
|
+
bb2: {
|
|
1052
|
+
if (!entityId) {
|
|
1053
|
+
t4 = "idle";
|
|
1054
|
+
break bb2;
|
|
1055
|
+
}
|
|
1056
|
+
if (failedTransaction) {
|
|
1057
|
+
t4 = "error";
|
|
1058
|
+
break bb2;
|
|
1059
|
+
}
|
|
1060
|
+
if (entityPendingMutations.length > 0) {
|
|
1061
|
+
t4 = "syncing";
|
|
1062
|
+
break bb2;
|
|
1063
|
+
}
|
|
1064
|
+
const syncedAt_0 = recentlySyncedEntities.get(entityId);
|
|
1065
|
+
if (syncedAt_0 && Date.now() - syncedAt_0 < SYNCED_DISPLAY_DURATION_MS) {
|
|
1066
|
+
t4 = "synced";
|
|
1067
|
+
break bb2;
|
|
1068
|
+
}
|
|
1069
|
+
t4 = "idle";
|
|
1070
|
+
}
|
|
1071
|
+
const state = t4;
|
|
1072
|
+
const error = failedTransaction?.error ?? null;
|
|
1073
|
+
let t5;
|
|
1074
|
+
if ($[18] !== clearFailure || $[19] !== failedTransaction) {
|
|
1075
|
+
t5 = () => {
|
|
1076
|
+
if (failedTransaction) {
|
|
1077
|
+
clearFailure(failedTransaction.id);
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
$[18] = clearFailure;
|
|
1081
|
+
$[19] = failedTransaction;
|
|
1082
|
+
$[20] = t5;
|
|
1083
|
+
} else {
|
|
1084
|
+
t5 = $[20];
|
|
1085
|
+
}
|
|
1086
|
+
const dismiss = t5;
|
|
1087
|
+
let t6;
|
|
1088
|
+
if ($[21] !== dismiss || $[22] !== entityPendingMutations.length || $[23] !== error || $[24] !== failedTransaction || $[25] !== state) {
|
|
1089
|
+
t6 = {
|
|
1090
|
+
state,
|
|
1091
|
+
error,
|
|
1092
|
+
pendingOperations: entityPendingMutations.length,
|
|
1093
|
+
failedTransaction,
|
|
1094
|
+
dismiss
|
|
1095
|
+
};
|
|
1096
|
+
$[21] = dismiss;
|
|
1097
|
+
$[22] = entityPendingMutations.length;
|
|
1098
|
+
$[23] = error;
|
|
1099
|
+
$[24] = failedTransaction;
|
|
1100
|
+
$[25] = state;
|
|
1101
|
+
$[26] = t6;
|
|
1102
|
+
} else {
|
|
1103
|
+
t6 = $[26];
|
|
1104
|
+
}
|
|
1105
|
+
return t6;
|
|
1106
|
+
}
|
|
1107
|
+
function _temp(n) {
|
|
1108
|
+
return n + 1;
|
|
1109
|
+
}
|
|
1110
|
+
function useUploadStatus() {
|
|
1111
|
+
const $ = _c(12);
|
|
1112
|
+
const {
|
|
1113
|
+
pendingCount,
|
|
1114
|
+
clearAllFailures,
|
|
1115
|
+
failedTransactions,
|
|
1116
|
+
hasUploadErrors,
|
|
1117
|
+
permanentErrorCount
|
|
1118
|
+
} = useSyncStatus();
|
|
1119
|
+
const {
|
|
1120
|
+
triggerSync
|
|
1121
|
+
} = useSyncControl();
|
|
1122
|
+
let t0;
|
|
1123
|
+
if ($[0] !== triggerSync) {
|
|
1124
|
+
t0 = async () => {
|
|
1125
|
+
await triggerSync();
|
|
1126
|
+
};
|
|
1127
|
+
$[0] = triggerSync;
|
|
1128
|
+
$[1] = t0;
|
|
1129
|
+
} else {
|
|
1130
|
+
t0 = $[1];
|
|
1131
|
+
}
|
|
1132
|
+
const retryAll = t0;
|
|
1133
|
+
let t1;
|
|
1134
|
+
if ($[2] !== clearAllFailures) {
|
|
1135
|
+
t1 = () => {
|
|
1136
|
+
clearAllFailures();
|
|
1137
|
+
};
|
|
1138
|
+
$[2] = clearAllFailures;
|
|
1139
|
+
$[3] = t1;
|
|
1140
|
+
} else {
|
|
1141
|
+
t1 = $[3];
|
|
1142
|
+
}
|
|
1143
|
+
const dismissAll = t1;
|
|
1144
|
+
const t2 = permanentErrorCount > 0;
|
|
1145
|
+
let t3;
|
|
1146
|
+
if ($[4] !== dismissAll || $[5] !== failedTransactions || $[6] !== hasUploadErrors || $[7] !== pendingCount || $[8] !== permanentErrorCount || $[9] !== retryAll || $[10] !== t2) {
|
|
1147
|
+
t3 = {
|
|
1148
|
+
pendingCount,
|
|
1149
|
+
failedCount: failedTransactions.length,
|
|
1150
|
+
permanentFailureCount: permanentErrorCount,
|
|
1151
|
+
hasErrors: hasUploadErrors,
|
|
1152
|
+
hasPermanentErrors: t2,
|
|
1153
|
+
failedTransactions,
|
|
1154
|
+
retryAll,
|
|
1155
|
+
dismissAll
|
|
1156
|
+
};
|
|
1157
|
+
$[4] = dismissAll;
|
|
1158
|
+
$[5] = failedTransactions;
|
|
1159
|
+
$[6] = hasUploadErrors;
|
|
1160
|
+
$[7] = pendingCount;
|
|
1161
|
+
$[8] = permanentErrorCount;
|
|
1162
|
+
$[9] = retryAll;
|
|
1163
|
+
$[10] = t2;
|
|
1164
|
+
$[11] = t3;
|
|
1165
|
+
} else {
|
|
1166
|
+
t3 = $[11];
|
|
1167
|
+
}
|
|
1168
|
+
return t3;
|
|
1169
|
+
}
|
|
1170
|
+
function useSyncActivity() {
|
|
1171
|
+
const $ = _c(19);
|
|
1172
|
+
const {
|
|
1173
|
+
pendingMutations,
|
|
1174
|
+
clearFailure,
|
|
1175
|
+
failedTransactions,
|
|
1176
|
+
completedTransactions,
|
|
1177
|
+
clearCompletedHistory,
|
|
1178
|
+
isUploading,
|
|
1179
|
+
isDownloading
|
|
1180
|
+
} = useSyncStatus();
|
|
1181
|
+
const {
|
|
1182
|
+
triggerSync
|
|
1183
|
+
} = useSyncControl();
|
|
1184
|
+
let t0;
|
|
1185
|
+
if ($[0] !== triggerSync) {
|
|
1186
|
+
t0 = async () => {
|
|
1187
|
+
await triggerSync();
|
|
1188
|
+
};
|
|
1189
|
+
$[0] = triggerSync;
|
|
1190
|
+
$[1] = t0;
|
|
1191
|
+
} else {
|
|
1192
|
+
t0 = $[1];
|
|
1193
|
+
}
|
|
1194
|
+
const retryAll = t0;
|
|
1195
|
+
let t1;
|
|
1196
|
+
if ($[2] !== clearFailure) {
|
|
1197
|
+
t1 = (failureId) => {
|
|
1198
|
+
clearFailure(failureId);
|
|
1199
|
+
};
|
|
1200
|
+
$[2] = clearFailure;
|
|
1201
|
+
$[3] = t1;
|
|
1202
|
+
} else {
|
|
1203
|
+
t1 = $[3];
|
|
1204
|
+
}
|
|
1205
|
+
const dismissFailure = t1;
|
|
1206
|
+
let t2;
|
|
1207
|
+
if ($[4] !== clearCompletedHistory) {
|
|
1208
|
+
t2 = () => {
|
|
1209
|
+
clearCompletedHistory();
|
|
1210
|
+
};
|
|
1211
|
+
$[4] = clearCompletedHistory;
|
|
1212
|
+
$[5] = t2;
|
|
1213
|
+
} else {
|
|
1214
|
+
t2 = $[5];
|
|
1215
|
+
}
|
|
1216
|
+
const clearCompleted = t2;
|
|
1217
|
+
let t3;
|
|
1218
|
+
if ($[6] !== completedTransactions.length || $[7] !== failedTransactions.length || $[8] !== pendingMutations.length) {
|
|
1219
|
+
t3 = {
|
|
1220
|
+
pending: pendingMutations.length,
|
|
1221
|
+
failed: failedTransactions.length,
|
|
1222
|
+
completed: completedTransactions.length
|
|
1223
|
+
};
|
|
1224
|
+
$[6] = completedTransactions.length;
|
|
1225
|
+
$[7] = failedTransactions.length;
|
|
1226
|
+
$[8] = pendingMutations.length;
|
|
1227
|
+
$[9] = t3;
|
|
1228
|
+
} else {
|
|
1229
|
+
t3 = $[9];
|
|
1230
|
+
}
|
|
1231
|
+
const counts = t3;
|
|
1232
|
+
const hasActivity = isUploading || isDownloading || failedTransactions.length > 0;
|
|
1233
|
+
let t4;
|
|
1234
|
+
if ($[10] !== clearCompleted || $[11] !== completedTransactions || $[12] !== counts || $[13] !== dismissFailure || $[14] !== failedTransactions || $[15] !== hasActivity || $[16] !== pendingMutations || $[17] !== retryAll) {
|
|
1235
|
+
t4 = {
|
|
1236
|
+
pending: pendingMutations,
|
|
1237
|
+
failed: failedTransactions,
|
|
1238
|
+
completed: completedTransactions,
|
|
1239
|
+
counts,
|
|
1240
|
+
hasActivity,
|
|
1241
|
+
retryAll,
|
|
1242
|
+
dismissFailure,
|
|
1243
|
+
clearCompleted
|
|
1244
|
+
};
|
|
1245
|
+
$[10] = clearCompleted;
|
|
1246
|
+
$[11] = completedTransactions;
|
|
1247
|
+
$[12] = counts;
|
|
1248
|
+
$[13] = dismissFailure;
|
|
1249
|
+
$[14] = failedTransactions;
|
|
1250
|
+
$[15] = hasActivity;
|
|
1251
|
+
$[16] = pendingMutations;
|
|
1252
|
+
$[17] = retryAll;
|
|
1253
|
+
$[18] = t4;
|
|
1254
|
+
} else {
|
|
1255
|
+
t4 = $[18];
|
|
1256
|
+
}
|
|
1257
|
+
return t4;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
export {
|
|
1261
|
+
ConflictBus,
|
|
1262
|
+
PowerSyncContext,
|
|
1263
|
+
SyncStatusContext,
|
|
1264
|
+
ConnectionHealthContext,
|
|
1265
|
+
SyncMetricsContext,
|
|
1266
|
+
AttachmentQueueContext,
|
|
1267
|
+
PowerSyncProvider,
|
|
1268
|
+
usePowerSync,
|
|
1269
|
+
useSyncStatus,
|
|
1270
|
+
useSyncControl,
|
|
1271
|
+
useSyncMode,
|
|
1272
|
+
useConnectionHealth,
|
|
1273
|
+
useSyncMetrics,
|
|
1274
|
+
useAttachmentQueue,
|
|
1275
|
+
useDatabase,
|
|
1276
|
+
usePlatform,
|
|
1277
|
+
useOnlineStatus,
|
|
1278
|
+
usePendingMutations,
|
|
1279
|
+
useIsSyncing,
|
|
1280
|
+
useDownloadProgress,
|
|
1281
|
+
useEntitySyncStatus,
|
|
1282
|
+
useUploadStatus,
|
|
1283
|
+
useSyncActivity
|
|
1284
|
+
};
|
|
1285
|
+
//# sourceMappingURL=chunk-GMFDCVMZ.js.map
|